I want to make details
with detailsContent
and detailsSummary
sub-nodes, where open
state of details
is triggered by a button
inside detailsSummary
node and which is controlled by open
attribute on details
node. I did that through custom node views and here is my final version - I would appreaciate if someone could tell whether it’s written properly or maybe I should do this in other way?
In short: I have a custom node view for detailsSummary
with preventDefault()
on click to do not trigger details open state through clickng on summary
. Instead of this, I added button
with click
event which updates open
attribute of its parent node. To pick this change, details
has a custom node view as well and in update
method I update the value of open
attribute to close and open details
. Is all below fine or there is a space for improvement?
// detailsSummary node view
return ({ editor, getPos, node, HTMLAttributes }) => {
const dom = document.createElement('summary'),
attrs = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
'data-type': this.name,
});
Object.entries(attrs).forEach(([key, value]) =>
dom.setAttribute(key, value)
);
const button = document.createElement('button');
button.innerText = 'toggle';
button.onclick = () => {
getPos = getPos as () => number;
const pos = editor.state.doc.resolve(getPos());
const detailsNode = findParentNodeClosestToPos(
pos,
(node) => node.type.name === 'details'
);
editor
.chain()
.focus()
.updateAttributes('details', {
open: !detailsNode?.node.attrs.open,
})
.run();
};
dom.appendChild(button);
const contentDOM = document.createElement('div');
dom.appendChild(contentDOM);
return {
dom,
contentDOM,
update: (node) => node.type === this.type,
};
};
// details node view
return ({ editor, getPos, node, HTMLAttributes }) => {
const dom = document.createElement('details'),
attrs = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
'data-type': this.name,
});
if (node.attrs.open) attrs['open'] = true;
Object.entries(attrs).forEach(([key, value]) =>
dom.setAttribute(key, value)
);
return {
dom: dom,
contentDOM: dom,
update: (node) => {
if (node.type !== this.type) return false;
dom.open = node.attrs.open;
return true;
},
};
};