Closed X0rD3v1L closed 2 years ago
Ref: INT-2916
Well I went down a rabbit hole trying to get this to work, however I ran into a Firefox bug - so to summarize you're going to have to use fake checkboxes inside the editor and transform them on output.
The editor will remove any event handler attributes when it parses content. This is to prevent cross site scripting. You could override this however generally that is a bad idea. The normal way to handle clicks on things like this is to put a handler on a surrounding element and then find out what the target is. You can do this inside TinyMCE and in normal HTML so it will work for the output content too.
If you only cared about Chrome then this might work for you: https://codesandbox.io/s/happy-cookies-eckfcu?file=/src/index.js
<Editor
apiKey="qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc"
init={{
menu: {
custom: {
title: "Custom",
items: "myCustomMenuItem"
}
},
menubar: "custom",
setup: (editor) => {
editor.on("click", (evt) => {
if (evt.target.matches('input[type="checkbox"]')) {
alert("clicked!");
}
});
editor.ui.registry.addMenuItem("myCustomMenuItem", {
text: "My Custom Menu Item",
onAction: function () {
editor.insertContent('<input type="checkbox">Hello</input>');
}
});
}
}}
/>
The problem is that in Firefox the click event never propagates inside a contenteditable=true
region so you can't catch it. You can stick the checkbox inside a span with <span contenteditable="false">
and you'll be able to detect the click but then you'll not be able to display the checkbox as checked on either Firefox or Chrome so that seems to be a net loss...
The only real solution is to fake the checkbox while it is inside the contenteditable region used by TinyMCE. You have two options for that
Then you can use a nodeFilter to rewrite the HTML as you prefer it.
For example: https://codesandbox.io/s/awesome-germain-56x49o?file=/src/index.js
<Editor
apiKey="qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc"
init={{
menu: {
custom: {
title: "Custom",
items: "myCustomMenuItem"
}
},
menubar: "custom",
setup: (editor) => {
console.log("setup runs");
editor.on("click", (evt) => {
if (evt.target.matches("span.fakebox")) {
const container = evt.target.closest("div.fakecheckbox");
const checked = container.classList.toggle("checked");
evt.target.innerHTML = checked ? "☑" : "☐";
}
});
editor.ui.registry.addMenuItem("myCustomMenuItem", {
text: "My Custom Menu Item",
onAction: function () {
editor.insertContent(
'<div class="fakecheckbox"><span contenteditable="false" style="display: cursor;" class="fakebox">☐</span> Hello</div>'
);
}
});
editor.on("PreInit", () => {
editor.serializer.addNodeFilter("div", (nodes, name, args) => {
const chks = nodes.filter((node) =>
/\bfakecheckbox\b/.test(node.attr("class"))
);
chks.forEach((chk) => {
const checked = /\bchecked\b/.test(chk.attr("class"));
const children = chk
.children()
.filter((child) => !/\bfakebox\b/.test(child.attr("class")));
const input = new tinymce.html.Node("input", 1);
input.attr("type", "checkbox");
if (checked) input.attr("checked", "checked");
const label = new tinymce.html.Node("label", 1);
label.append(input);
children.forEach((child) => label.append(child));
chk.replace(label);
});
});
});
}
}}
/>
This kind of question isn't well suited to issues. This actually had nothing to do with the react integration. In future please post these questions to stackoverflow or subscribe to our paid support and ask there.
I am not able to add a custom plugin with checkbox having it's own actions.