import { Node, mergeAttributes } from '@tiptap/core';
export const DynamicDataPlaceholder = Node.create({
name: 'dynamicDataPlaceholder',
group: 'block',
content: 'block*', // Allow block content to include images and tables
addAttributes() {
return {
id: {
default: null,
},
innerHtml: {
default: '', // Renamed for clarity
},
};
},
parseHTML() {
return [
{
tag: 'div[data-dynamic-data-placeholder-div]',
getAttrs: (dom) => {
// Capture the complete inner HTML for more complex content
return {
id: dom.getAttribute('id'),
innerHtml: dom.innerHTML, // Capture the complete inner HTML
};
},
},
];
},
renderHTML({ HTMLAttributes }) {
// Instead of relying on the static HTMLAttributes.innerHtml, we'll manage it in the node view
return [
'div',
mergeAttributes(
{ 'data-dynamic-data-placeholder-div': '', id: HTMLAttributes.id },
HTMLAttributes
),
0, // Use contentDOM to manage inner content dynamically
];
},
addNodeView() {
return ({ node, getPos, editor }) => {
const wrapperDiv = document.createElement('div');
wrapperDiv.setAttribute('contenteditable', 'true');
wrapperDiv.dataset.customDiv = '';
wrapperDiv.id = node.attrs.id;
wrapperDiv.style.backgroundColor = '#F3F5FF';
wrapperDiv.style.padding = '5px';
wrapperDiv.style.width = '100%';
wrapperDiv.style.height = '100%';
wrapperDiv.innerHTML = node.attrs.innerHtml;
const update = (updatedNode) => {
const isNodeDeleted = updatedNode.type.name !== this.name;
const isContentEmpty =
updatedNode.content.size === 0 ||
wrapperDiv.textContent.trim().length === 0;
const { state, dispatch } = editor.view;
const pos = getPos();
if (isNodeDeleted || isContentEmpty) {
console.log('nodedeleted');
const tr = state.tr.delete(pos, pos + 1);
dispatch(tr);
return false;
}
return true;
};
return {
dom: wrapperDiv,
contentDOM: wrapperDiv,
update,
};
};
},
});
Inside here I want to delete the whole placeholder when the content is empty, so I want to call an API also, but this is going into an infinite loop when transaction is done. Please help.
addNodeView() {
return ({ node, getPos, editor }) => {
const wrapperDiv = document.createElement('div');
wrapperDiv.setAttribute('contenteditable', 'true');
wrapperDiv.dataset.customDiv = '';
wrapperDiv.id = node.attrs.id;
wrapperDiv.style.backgroundColor = '#F3F5FF';
wrapperDiv.style.padding = '5px';
wrapperDiv.style.width = '100%';
wrapperDiv.style.height = '100%';
wrapperDiv.innerHTML = node.attrs.innerHtml;
const update = (updatedNode) => {
const isNodeDeleted = updatedNode.type.name !== this.name;
const isContentEmpty =
updatedNode.content.size === 0 ||
wrapperDiv.textContent.trim().length === 0;
const { state, dispatch } = editor.view;
const { selection } = state;
const { $from } = selection;
if (isNodeDeleted || isContentEmpty) {
console.log('nodedeleted');
const tr = state.tr.delete($from.before() + 2, $from.after());
dispatch(tr);
return false;
}
return true;
};
return {
dom: wrapperDiv,
contentDOM: wrapperDiv,
update,
};
};
},
});
I also tried adding addEventListener but it was not getting triggered on wrapperDiv.