Don't know why this is going in infinite loop. Please help urgent!

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.

I’m not sure what you are trying to use NodeView.update for, but that’s a method called to update the node’s DOM representation. Dispatching transactions from there is never a reasonable thing to do.

@marijn I did it as inside addNodeView addEventListener was not working when setup with he wrapperDiv, I am new to this, please give me a idea what should be a better way to do it… Thanks. I just want to delete the NodeView when content inside it become empty… And there can be multiple instances of the same nodeview.

I’m not exactly sure what you are trying to do here. But I would add a key-down listener for backspace and check if the node is empty.