mirror of
https://github.com/discourse/discourse.git
synced 2025-05-05 03:34:37 +08:00

`pos` is the exact position of the click in the entire document `nodePos` is the position of the clicked node itself The idea is that we want the click to be the first position **within the node**. The previous code was checking for the first 2 positions of the entire document. I'd love to add a test for this, but it's very tricky.
90 lines
2.3 KiB
JavaScript
90 lines
2.3 KiB
JavaScript
/** @type {RichEditorExtension} */
|
|
const extension = {
|
|
nodeSpec: {
|
|
details: {
|
|
allowGapCursor: true,
|
|
attrs: { open: { default: true } },
|
|
content: "summary block+",
|
|
group: "block",
|
|
draggable: true,
|
|
selectable: true,
|
|
defining: true,
|
|
isolating: true,
|
|
parseDOM: [{ tag: "details" }],
|
|
toDOM: (node) => ["details", { open: node.attrs.open || undefined }, 0],
|
|
},
|
|
summary: {
|
|
content: "inline*",
|
|
parseDOM: [{ tag: "summary" }],
|
|
toDOM: () => ["summary", 0],
|
|
},
|
|
},
|
|
parse: {
|
|
bbcode_open(state, token) {
|
|
if (token.tag === "details") {
|
|
state.openNode(state.schema.nodes.details, {
|
|
open: token.attrGet("open") !== null,
|
|
});
|
|
return true;
|
|
}
|
|
|
|
if (token.tag === "summary") {
|
|
state.openNode(state.schema.nodes.summary);
|
|
return true;
|
|
}
|
|
},
|
|
bbcode_close(state, token) {
|
|
if (token.tag === "details" || token.tag === "summary") {
|
|
state.closeNode();
|
|
return true;
|
|
}
|
|
},
|
|
},
|
|
serializeNode: {
|
|
details(state, node) {
|
|
state.renderContent(node);
|
|
state.write("[/details]\n\n");
|
|
},
|
|
summary(state, node, parent) {
|
|
let hasSummary = false;
|
|
// If the [details] tag has no summary.
|
|
if (node.content.childCount === 0) {
|
|
state.write("[details");
|
|
} else {
|
|
hasSummary = true;
|
|
state.write('[details="');
|
|
node.content.forEach(
|
|
(child) =>
|
|
child.text &&
|
|
state.text(child.text.replace(/"/g, "“"), state.inAutolink)
|
|
);
|
|
}
|
|
let finalState = `${parent.attrs.open ? " open" : ""}]\n`;
|
|
if (hasSummary) {
|
|
finalState = `"${finalState}`;
|
|
}
|
|
state.write(finalState);
|
|
},
|
|
},
|
|
plugins: {
|
|
props: {
|
|
handleClickOn(view, pos, node, nodePos) {
|
|
// if the click position in the document is not the first within the summary node
|
|
if (pos > nodePos + 1 || node.type.name !== "summary") {
|
|
return false;
|
|
}
|
|
|
|
const details = view.state.doc.nodeAt(nodePos - 1);
|
|
view.dispatch(
|
|
view.state.tr.setNodeMarkup(nodePos - 1, null, {
|
|
open: !details.attrs.open,
|
|
})
|
|
);
|
|
return true;
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export default extension;
|