Closed aulneau closed 3 years ago
lgtm, please run yarn lint --fix
to pass CI
lgtm, please run
yarn lint --fix
to pass CI
Done :)
Sorry if I'm drive-by commenting, but the duplicated logic between handleCopyToClipboard
and handleLanguageChange
to find the codeblock node could be removed by allowing for custom nodeviews written with vanilla javascript (in addition to React ).
That's because with custom node views, you can easily get access to the current node and its position in the constructor and in the update method.
To do that, this library would just need to relax constraints in createNodeViews
and move ComponentView
into the extension.component
method, so individual nodes could choose to use React or plain JS.
createNodeViews() {
return this.extensions.extensions
.filter((extension: Node) => extension.component)
.reduce((nodeViews, extension: Node) => {
const nodeView = (node, view, getPos, decorations) => {
return extension.component({
editor: this,
extension,
node,
view,
getPos,
decorations,
});
};
return {
...nodeViews,
[extension.name]: nodeView,
};
}, {});
}
Then for the codefence, you could have something like this:
export default class CodeFence extends Node {
get schema() {
return {
attrs: {
language: {
default: "javascript",
},
},
content: "text*",
marks: "",
group: "block",
code: true,
defining: true,
draggable: false,
parseDOM: [
{ tag: "pre", preserveWhitespace: "full" },
{
tag: ".code-block",
preserveWhitespace: "full",
contentElement: "code",
getAttrs: (dom: HTMLDivElement) => {
return {
language: dom.dataset.language,
};
},
},
],
toDOM: node => {
return [
"div",
{ class: "code-block", "data-language": node.attrs.language },
["pre", ["code", { spellCheck: false }, 0]],
];
},
};
}
component(props) {
return new CodeFenceNodeView(props);
}
}
class CodeFenceNodeView {
constructor({ editor, extension, node, view, getPos, decorations}) {
this.extension = extension;
this.node = node;
this.view = view;
this.getPos = getPos;
this.dom = document.createElement("div");
const div = this.dom.appendChild(document.createElement("div"));
const button = div.appendChild(document.createElement("button"));
button.innerText = "Copy";
button.type = "button";
button.addEventListener("click", this.handleCopyToClipboard);
const select = div.appendChild(document.createElement("select"));
select.addEventListener("change", this.handleLanguageChange);
let pre = this.dom.appendChild(document.createElement("pre"));
this.contentDOM = pre.appendChild(document.createElement("code"));
}
handleCopyToClipboard = event => {
copy(this.node.textContent);
if (this.extension.options.onShowToast) {
this.extension.options.onShowToast(
this.extension.options.dictionary.codeCopied,
ToastType.Info
);
}
};
handleLanguageChange = event => {
const element = event.target;
this.view.view.dispatch(this.view.state.tr.setNodeMarkup(this.getPos(), undefined, {
language: element.value,
}););
}
};
update (node, decorations) {
if (node.type !== this.node.type) { return false; }
this.node = node;
return true;
}
}
When playing around with the CodeFence node, I noticed there was a copy action, but it was always copying the initial value of the code block node from when the editor first loaded. This change copies the logic of the language toggle such that it gets the position of the node, and then finds the node in the editor view and gets the latest text content.