How I can attach attribute with dynamic value when new paragraph is inserted?

Hi there,

Just faced with the same task and want to share my solution to save time of the next generation of ProseMirror lovers :slight_smile:

Plugin file:

import {Plugin} from "prosemirror-state";
import {uuidv4} from "uuidv4";

const isTargetNodeOfType = (node, type) => (node.type === type);

const isNodeHasAttribute = (node, attrName) => Boolean(node.attrs && node.attrs[attrName]);

const attrName = "guid";

export const createPlugin = (guidGenerator = uuidv4) => {
  return new Plugin({
    appendTransaction: (transactions, prevState, nextState) => {
      const tr = nextState.tr;
      let modified = false;
      if (transactions.some((transaction) => transaction.docChanged)) {
        // Adds a unique id to a node
        nextState.doc.descendants((node, pos) => {
          const {paragraph} = nextState.schema.nodes;
          if (isTargetNodeOfType(node, paragraph) && !isNodeHasAttribute(node, attrName)) {
            const attrs = node.attrs;
            tr.setNodeMarkup(pos, undefined, {...attrs, [attrName]: guidGenerator()});
            modified = true;
          }
        });
      }

      return modified ? tr : null;
    },
  });
};

Schema file:

import {Schema} from "prosemirror-model";
import {marks, nodes} from "prosemirror-schema-basic";

const baseNodes = {
  ...nodes,
  // customize basic paragraph with guid
  paragraph: {
    content: "inline*",
    group: "block",
    attrs: {
      guid: {default: ""},
    },
    parseDOM: [
      {
        tag: "p",
        getAttrs: (dom) => ({guid: dom.getAttribute("data-guid")}),
      },
    ],
    toDOM(node) {
      const {guid} = node.attrs;
      return ["p", {"data-guid": guid}, 0];
    },
  },
};

const base = new Schema({nodes: baseNodes, marks: marks});
...

Thank you @marijn for such a wonderful library!

Good luck!

13 Likes