@tiptap/react 3.0.0-next.1 → 3.0.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -40,6 +40,7 @@ __export(src_exports, {
40
40
  NodeViewContent: () => NodeViewContent,
41
41
  NodeViewWrapper: () => NodeViewWrapper,
42
42
  PureEditorContent: () => PureEditorContent,
43
+ ReactNodeView: () => ReactNodeView,
43
44
  ReactNodeViewContext: () => ReactNodeViewContext,
44
45
  ReactNodeViewRenderer: () => ReactNodeViewRenderer,
45
46
  ReactRenderer: () => ReactRenderer,
@@ -52,11 +53,11 @@ module.exports = __toCommonJS(src_exports);
52
53
 
53
54
  // src/BubbleMenu.tsx
54
55
  var import_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
55
- var import_react5 = __toESM(require("react"), 1);
56
+ var import_react6 = __toESM(require("react"), 1);
56
57
  var import_react_dom2 = require("react-dom");
57
58
 
58
59
  // src/Context.tsx
59
- var import_react4 = __toESM(require("react"), 1);
60
+ var import_react5 = __toESM(require("react"), 1);
60
61
 
61
62
  // src/EditorContent.tsx
62
63
  var import_react = __toESM(require("react"), 1);
@@ -215,12 +216,14 @@ var EditorContent = import_react.default.memo(EditorContentWithKey);
215
216
 
216
217
  // src/useEditor.ts
217
218
  var import_core = require("@tiptap/core");
218
- var import_react3 = require("react");
219
+ var import_react4 = require("react");
219
220
  var import_shim2 = require("use-sync-external-store/shim");
220
221
 
221
222
  // src/useEditorState.ts
222
- var import_react2 = require("react");
223
+ var import_react2 = __toESM(require("fast-deep-equal/es6/react"), 1);
224
+ var import_react3 = require("react");
223
225
  var import_with_selector = require("use-sync-external-store/shim/with-selector");
226
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react3.useLayoutEffect : import_react3.useEffect;
224
227
  var EditorStateManager = class {
225
228
  constructor(initialEditor) {
226
229
  this.transactionNumber = 0;
@@ -279,18 +282,19 @@ var EditorStateManager = class {
279
282
  }
280
283
  };
281
284
  function useEditorState(options) {
282
- const [editorInstance] = (0, import_react2.useState)(() => new EditorStateManager(options.editor));
285
+ var _a;
286
+ const [editorStateManager] = (0, import_react3.useState)(() => new EditorStateManager(options.editor));
283
287
  const selectedState = (0, import_with_selector.useSyncExternalStoreWithSelector)(
284
- editorInstance.subscribe,
285
- editorInstance.getSnapshot,
286
- editorInstance.getServerSnapshot,
288
+ editorStateManager.subscribe,
289
+ editorStateManager.getSnapshot,
290
+ editorStateManager.getServerSnapshot,
287
291
  options.selector,
288
- options.equalityFn
292
+ (_a = options.equalityFn) != null ? _a : import_react2.default
289
293
  );
290
- (0, import_react2.useEffect)(() => {
291
- return editorInstance.watch(options.editor);
292
- }, [options.editor, editorInstance]);
293
- (0, import_react2.useDebugValue)(selectedState);
294
+ useIsomorphicLayoutEffect(() => {
295
+ return editorStateManager.watch(options.editor);
296
+ }, [options.editor, editorStateManager]);
297
+ (0, import_react3.useDebugValue)(selectedState);
294
298
  return selectedState;
295
299
  }
296
300
 
@@ -324,6 +328,7 @@ var EditorInstanceManager = class {
324
328
  this.options = options;
325
329
  this.subscriptions = /* @__PURE__ */ new Set();
326
330
  this.setEditor(this.getInitialEditor());
331
+ this.scheduleDestroy();
327
332
  this.getEditor = this.getEditor.bind(this);
328
333
  this.getServerSnapshot = this.getServerSnapshot.bind(this);
329
334
  this.subscribe = this.subscribe.bind(this);
@@ -401,6 +406,14 @@ var EditorInstanceManager = class {
401
406
  onContentError: (...args) => {
402
407
  var _a, _b;
403
408
  return (_b = (_a = this.options.current).onContentError) == null ? void 0 : _b.call(_a, ...args);
409
+ },
410
+ onDrop: (...args) => {
411
+ var _a, _b;
412
+ return (_b = (_a = this.options.current).onDrop) == null ? void 0 : _b.call(_a, ...args);
413
+ },
414
+ onPaste: (...args) => {
415
+ var _a, _b;
416
+ return (_b = (_a = this.options.current).onPaste) == null ? void 0 : _b.call(_a, ...args);
404
417
  }
405
418
  };
406
419
  const editor = new import_core.Editor(optionsToApply);
@@ -437,7 +450,10 @@ var EditorInstanceManager = class {
437
450
  this.isComponentMounted = true;
438
451
  clearTimeout(this.scheduledDestructionTimeout);
439
452
  if (this.editor && !this.editor.isDestroyed && deps.length === 0) {
440
- this.editor.setOptions(this.options.current);
453
+ this.editor.setOptions({
454
+ ...this.options.current,
455
+ editable: this.editor.isEditable
456
+ });
441
457
  } else {
442
458
  this.refreshEditorInstance(deps);
443
459
  }
@@ -488,24 +504,24 @@ var EditorInstanceManager = class {
488
504
  this.setEditor(null);
489
505
  }
490
506
  }
491
- }, 0);
507
+ }, 1);
492
508
  }
493
509
  };
494
510
  function useEditor(options = {}, deps = []) {
495
- const mostRecentOptions = (0, import_react3.useRef)(options);
511
+ const mostRecentOptions = (0, import_react4.useRef)(options);
496
512
  mostRecentOptions.current = options;
497
- const [instanceManager] = (0, import_react3.useState)(() => new EditorInstanceManager(mostRecentOptions));
513
+ const [instanceManager] = (0, import_react4.useState)(() => new EditorInstanceManager(mostRecentOptions));
498
514
  const editor = (0, import_shim2.useSyncExternalStore)(
499
515
  instanceManager.subscribe,
500
516
  instanceManager.getEditor,
501
517
  instanceManager.getServerSnapshot
502
518
  );
503
- (0, import_react3.useDebugValue)(editor);
504
- (0, import_react3.useEffect)(instanceManager.onRender(deps));
519
+ (0, import_react4.useDebugValue)(editor);
520
+ (0, import_react4.useEffect)(instanceManager.onRender(deps));
505
521
  useEditorState({
506
522
  editor,
507
523
  selector: ({ transactionNumber }) => {
508
- if (options.shouldRerenderOnTransaction === false) {
524
+ if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === void 0) {
509
525
  return null;
510
526
  }
511
527
  if (options.immediatelyRender && transactionNumber === 0) {
@@ -518,134 +534,162 @@ function useEditor(options = {}, deps = []) {
518
534
  }
519
535
 
520
536
  // src/Context.tsx
521
- var EditorContext = (0, import_react4.createContext)({
537
+ var EditorContext = (0, import_react5.createContext)({
522
538
  editor: null
523
539
  });
524
540
  var EditorConsumer = EditorContext.Consumer;
525
- var useCurrentEditor = () => (0, import_react4.useContext)(EditorContext);
541
+ var useCurrentEditor = () => (0, import_react5.useContext)(EditorContext);
526
542
  function EditorProvider({
527
543
  children,
528
544
  slotAfter,
529
545
  slotBefore,
546
+ editorContainerProps = {},
530
547
  ...editorOptions
531
548
  }) {
532
549
  const editor = useEditor(editorOptions);
550
+ const contextValue = (0, import_react5.useMemo)(() => ({ editor }), [editor]);
533
551
  if (!editor) {
534
552
  return null;
535
553
  }
536
- return /* @__PURE__ */ import_react4.default.createElement(EditorContext.Provider, { value: { editor } }, slotBefore, /* @__PURE__ */ import_react4.default.createElement(EditorConsumer, null, ({ editor: currentEditor }) => /* @__PURE__ */ import_react4.default.createElement(EditorContent, { editor: currentEditor })), children, slotAfter);
554
+ return /* @__PURE__ */ import_react5.default.createElement(EditorContext.Provider, { value: contextValue }, slotBefore, /* @__PURE__ */ import_react5.default.createElement(EditorConsumer, null, ({ editor: currentEditor }) => /* @__PURE__ */ import_react5.default.createElement(EditorContent, { editor: currentEditor, ...editorContainerProps })), children, slotAfter);
537
555
  }
538
556
 
539
557
  // src/BubbleMenu.tsx
540
- var BubbleMenu = (props) => {
541
- const menuEl = (0, import_react5.useRef)(document.createElement("div"));
542
- const { editor: currentEditor } = useCurrentEditor();
543
- (0, import_react5.useEffect)(() => {
544
- var _a;
545
- menuEl.current.style.visibility = "hidden";
546
- menuEl.current.style.position = "absolute";
547
- if (((_a = props.editor) == null ? void 0 : _a.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
548
- return;
549
- }
550
- const {
551
- pluginKey = "bubbleMenu",
552
- editor,
553
- updateDelay,
554
- resizeDelay,
555
- shouldShow = null
556
- } = props;
557
- const menuEditor = editor || currentEditor;
558
- if (!menuEditor) {
559
- console.warn("BubbleMenu component is not rendered inside of an editor component or does not have editor prop.");
560
- return;
558
+ var BubbleMenu = import_react6.default.forwardRef(
559
+ ({
560
+ pluginKey = "bubbleMenu",
561
+ editor,
562
+ updateDelay,
563
+ resizeDelay,
564
+ shouldShow = null,
565
+ options,
566
+ children,
567
+ ...restProps
568
+ }, ref) => {
569
+ const menuEl = (0, import_react6.useRef)(document.createElement("div"));
570
+ if (typeof ref === "function") {
571
+ ref(menuEl.current);
572
+ } else if (ref) {
573
+ ref.current = menuEl.current;
561
574
  }
562
- const plugin = (0, import_extension_bubble_menu.BubbleMenuPlugin)({
563
- updateDelay,
564
- resizeDelay,
565
- editor: menuEditor,
566
- element: menuEl.current,
567
- pluginKey,
568
- shouldShow,
569
- options: props.options
570
- });
571
- menuEditor.registerPlugin(plugin);
572
- return () => {
573
- menuEditor.unregisterPlugin(pluginKey);
574
- window.requestAnimationFrame(() => {
575
- if (menuEl.current.parentNode) {
576
- menuEl.current.parentNode.removeChild(menuEl.current);
577
- }
575
+ const { editor: currentEditor } = useCurrentEditor();
576
+ (0, import_react6.useEffect)(() => {
577
+ const bubbleMenuElement = menuEl.current;
578
+ bubbleMenuElement.style.visibility = "hidden";
579
+ bubbleMenuElement.style.position = "absolute";
580
+ if ((editor == null ? void 0 : editor.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
581
+ return;
582
+ }
583
+ const attachToEditor = editor || currentEditor;
584
+ if (!attachToEditor) {
585
+ console.warn(
586
+ "BubbleMenu component is not rendered inside of an editor component or does not have editor prop."
587
+ );
588
+ return;
589
+ }
590
+ const plugin = (0, import_extension_bubble_menu.BubbleMenuPlugin)({
591
+ updateDelay,
592
+ resizeDelay,
593
+ editor: attachToEditor,
594
+ element: bubbleMenuElement,
595
+ pluginKey,
596
+ shouldShow,
597
+ options
578
598
  });
579
- };
580
- }, [props.editor, currentEditor]);
581
- const portal = (0, import_react_dom2.createPortal)(
582
- /* @__PURE__ */ import_react5.default.createElement("div", { className: props.className }, props.children),
583
- menuEl.current
584
- );
585
- return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, portal);
586
- };
599
+ attachToEditor.registerPlugin(plugin);
600
+ return () => {
601
+ attachToEditor.unregisterPlugin(pluginKey);
602
+ window.requestAnimationFrame(() => {
603
+ if (bubbleMenuElement.parentNode) {
604
+ bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
605
+ }
606
+ });
607
+ };
608
+ }, [editor, currentEditor]);
609
+ return (0, import_react_dom2.createPortal)(
610
+ /* @__PURE__ */ import_react6.default.createElement(
611
+ "div",
612
+ {
613
+ ...restProps
614
+ },
615
+ children
616
+ ),
617
+ menuEl.current
618
+ );
619
+ }
620
+ );
587
621
 
588
622
  // src/FloatingMenu.tsx
589
623
  var import_extension_floating_menu = require("@tiptap/extension-floating-menu");
590
- var import_react6 = __toESM(require("react"), 1);
624
+ var import_react7 = __toESM(require("react"), 1);
591
625
  var import_react_dom3 = require("react-dom");
592
- var FloatingMenu = (props) => {
593
- const menuEl = (0, import_react6.useRef)(document.createElement("div"));
626
+ var FloatingMenu = import_react7.default.forwardRef(({
627
+ pluginKey = "floatingMenu",
628
+ editor,
629
+ shouldShow = null,
630
+ options,
631
+ children,
632
+ ...restProps
633
+ }, ref) => {
634
+ const menuEl = (0, import_react7.useRef)(document.createElement("div"));
635
+ if (typeof ref === "function") {
636
+ ref(menuEl.current);
637
+ } else if (ref) {
638
+ ref.current = menuEl.current;
639
+ }
594
640
  const { editor: currentEditor } = useCurrentEditor();
595
- (0, import_react6.useEffect)(() => {
596
- var _a;
597
- menuEl.current.style.visibility = "hidden";
598
- menuEl.current.style.position = "absolute";
599
- if (((_a = props.editor) == null ? void 0 : _a.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
641
+ (0, import_react7.useEffect)(() => {
642
+ const floatingMenuElement = menuEl.current;
643
+ floatingMenuElement.style.visibility = "hidden";
644
+ floatingMenuElement.style.position = "absolute";
645
+ if ((editor == null ? void 0 : editor.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
600
646
  return;
601
647
  }
602
- const {
603
- pluginKey = "floatingMenu",
604
- editor,
605
- options,
606
- shouldShow = null
607
- } = props;
608
- const menuEditor = editor || currentEditor;
609
- if (!menuEditor) {
610
- console.warn("FloatingMenu component is not rendered inside of an editor component or does not have editor prop.");
648
+ const attachToEditor = editor || currentEditor;
649
+ if (!attachToEditor) {
650
+ console.warn(
651
+ "FloatingMenu component is not rendered inside of an editor component or does not have editor prop."
652
+ );
611
653
  return;
612
654
  }
613
655
  const plugin = (0, import_extension_floating_menu.FloatingMenuPlugin)({
656
+ editor: attachToEditor,
657
+ element: floatingMenuElement,
614
658
  pluginKey,
615
- editor: menuEditor,
616
- element: menuEl.current,
617
- options,
618
- shouldShow
659
+ shouldShow,
660
+ options
619
661
  });
620
- menuEditor.registerPlugin(plugin);
662
+ attachToEditor.registerPlugin(plugin);
621
663
  return () => {
622
- menuEditor.unregisterPlugin(pluginKey);
664
+ attachToEditor.unregisterPlugin(pluginKey);
623
665
  window.requestAnimationFrame(() => {
624
- if (menuEl.current.parentNode) {
625
- menuEl.current.parentNode.removeChild(menuEl.current);
666
+ if (floatingMenuElement.parentNode) {
667
+ floatingMenuElement.parentNode.removeChild(floatingMenuElement);
626
668
  }
627
669
  });
628
670
  };
629
- }, [
630
- props.editor,
631
- currentEditor
632
- ]);
633
- const portal = (0, import_react_dom3.createPortal)(
634
- /* @__PURE__ */ import_react6.default.createElement("div", { className: props.className }, props.children),
671
+ }, [editor, currentEditor]);
672
+ return (0, import_react_dom3.createPortal)(
673
+ /* @__PURE__ */ import_react7.default.createElement(
674
+ "div",
675
+ {
676
+ ...restProps
677
+ },
678
+ children
679
+ ),
635
680
  menuEl.current
636
681
  );
637
- return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, portal);
638
- };
682
+ });
639
683
 
640
684
  // src/NodeViewContent.tsx
641
- var import_react8 = __toESM(require("react"), 1);
685
+ var import_react9 = __toESM(require("react"), 1);
642
686
 
643
687
  // src/useReactNodeView.ts
644
- var import_react7 = require("react");
645
- var ReactNodeViewContext = (0, import_react7.createContext)({
688
+ var import_react8 = require("react");
689
+ var ReactNodeViewContext = (0, import_react8.createContext)({
646
690
  onDragStart: void 0
647
691
  });
648
- var useReactNodeView = () => (0, import_react7.useContext)(ReactNodeViewContext);
692
+ var useReactNodeView = () => (0, import_react8.useContext)(ReactNodeViewContext);
649
693
 
650
694
  // src/NodeViewContent.tsx
651
695
  var NodeViewContent = (props) => {
@@ -653,7 +697,7 @@ var NodeViewContent = (props) => {
653
697
  const { nodeViewContentRef } = useReactNodeView();
654
698
  return (
655
699
  // @ts-ignore
656
- /* @__PURE__ */ import_react8.default.createElement(
700
+ /* @__PURE__ */ import_react9.default.createElement(
657
701
  Tag,
658
702
  {
659
703
  ...props,
@@ -669,13 +713,13 @@ var NodeViewContent = (props) => {
669
713
  };
670
714
 
671
715
  // src/NodeViewWrapper.tsx
672
- var import_react9 = __toESM(require("react"), 1);
673
- var NodeViewWrapper = import_react9.default.forwardRef((props, ref) => {
716
+ var import_react10 = __toESM(require("react"), 1);
717
+ var NodeViewWrapper = import_react10.default.forwardRef((props, ref) => {
674
718
  const { onDragStart } = useReactNodeView();
675
719
  const Tag = props.as || "div";
676
720
  return (
677
721
  // @ts-ignore
678
- /* @__PURE__ */ import_react9.default.createElement(
722
+ /* @__PURE__ */ import_react10.default.createElement(
679
723
  Tag,
680
724
  {
681
725
  ...props,
@@ -693,10 +737,10 @@ var NodeViewWrapper = import_react9.default.forwardRef((props, ref) => {
693
737
 
694
738
  // src/ReactNodeViewRenderer.tsx
695
739
  var import_core2 = require("@tiptap/core");
696
- var import_react11 = __toESM(require("react"), 1);
740
+ var import_react12 = __toESM(require("react"), 1);
697
741
 
698
742
  // src/ReactRenderer.tsx
699
- var import_react10 = __toESM(require("react"), 1);
743
+ var import_react11 = __toESM(require("react"), 1);
700
744
  var import_react_dom4 = require("react-dom");
701
745
  function isClassComponent(Component) {
702
746
  return !!(typeof Component === "function" && Component.prototype && Component.prototype.isReactComponent);
@@ -706,12 +750,14 @@ function isForwardRefComponent(Component) {
706
750
  return !!(typeof Component === "object" && ((_a = Component.$$typeof) == null ? void 0 : _a.toString()) === "Symbol(react.forward_ref)");
707
751
  }
708
752
  var ReactRenderer = class {
753
+ /**
754
+ * Immediately creates element and renders the provided React component.
755
+ */
709
756
  constructor(component, {
710
757
  editor,
711
758
  props = {},
712
759
  as = "div",
713
- className = "",
714
- attrs
760
+ className = ""
715
761
  }) {
716
762
  this.ref = null;
717
763
  this.id = Math.floor(Math.random() * 4294967295).toString();
@@ -723,11 +769,6 @@ var ReactRenderer = class {
723
769
  if (className) {
724
770
  this.element.classList.add(...className.split(" "));
725
771
  }
726
- if (attrs) {
727
- Object.keys(attrs).forEach((key) => {
728
- this.element.setAttribute(key, attrs[key]);
729
- });
730
- }
731
772
  if (this.editor.isInitialized) {
732
773
  (0, import_react_dom4.flushSync)(() => {
733
774
  this.render();
@@ -736,6 +777,9 @@ var ReactRenderer = class {
736
777
  this.render();
737
778
  }
738
779
  }
780
+ /**
781
+ * Render the React component.
782
+ */
739
783
  render() {
740
784
  var _a;
741
785
  const Component = this.component;
@@ -746,9 +790,12 @@ var ReactRenderer = class {
746
790
  this.ref = ref;
747
791
  };
748
792
  }
749
- this.reactElement = import_react10.default.createElement(Component, props);
793
+ this.reactElement = /* @__PURE__ */ import_react11.default.createElement(Component, { ...props });
750
794
  (_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.setRenderer(this.id, this);
751
795
  }
796
+ /**
797
+ * Re-renders the React component with new props.
798
+ */
752
799
  updateProps(props = {}) {
753
800
  this.props = {
754
801
  ...this.props,
@@ -756,22 +803,40 @@ var ReactRenderer = class {
756
803
  };
757
804
  this.render();
758
805
  }
806
+ /**
807
+ * Destroy the React component.
808
+ */
759
809
  destroy() {
760
810
  var _a;
761
811
  const editor = this.editor;
762
812
  (_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.removeRenderer(this.id);
763
813
  }
814
+ /**
815
+ * Update the attributes of the element that holds the React component.
816
+ */
817
+ updateAttributes(attributes) {
818
+ Object.keys(attributes).forEach((key) => {
819
+ this.element.setAttribute(key, attributes[key]);
820
+ });
821
+ }
764
822
  };
765
823
 
766
824
  // src/ReactNodeViewRenderer.tsx
767
825
  var ReactNodeView = class extends import_core2.NodeView {
826
+ /**
827
+ * Setup the React component.
828
+ * Called on initialization.
829
+ */
768
830
  mount() {
769
831
  const props = {
770
832
  editor: this.editor,
771
833
  node: this.node,
772
834
  decorations: this.decorations,
835
+ innerDecorations: this.innerDecorations,
836
+ view: this.view,
773
837
  selected: false,
774
838
  extension: this.extension,
839
+ HTMLAttributes: this.HTMLAttributes,
775
840
  getPos: () => this.getPos(),
776
841
  updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
777
842
  deleteNode: () => this.deleteNode()
@@ -790,9 +855,11 @@ var ReactNodeView = class extends import_core2.NodeView {
790
855
  };
791
856
  const context = { onDragStart, nodeViewContentRef };
792
857
  const Component = this.component;
793
- const ReactNodeViewProvider = import_react11.default.memo((componentProps) => {
794
- return /* @__PURE__ */ import_react11.default.createElement(ReactNodeViewContext.Provider, { value: context }, import_react11.default.createElement(Component, componentProps));
795
- });
858
+ const ReactNodeViewProvider = import_react12.default.memo(
859
+ (componentProps) => {
860
+ return /* @__PURE__ */ import_react12.default.createElement(ReactNodeViewContext.Provider, { value: context }, import_react12.default.createElement(Component, componentProps));
861
+ }
862
+ );
796
863
  ReactNodeViewProvider.displayName = "ReactNodeView";
797
864
  if (this.node.isLeaf) {
798
865
  this.contentDOMElement = null;
@@ -802,6 +869,7 @@ var ReactNodeView = class extends import_core2.NodeView {
802
869
  this.contentDOMElement = document.createElement(this.node.isInline ? "span" : "div");
803
870
  }
804
871
  if (this.contentDOMElement) {
872
+ this.contentDOMElement.dataset.nodeViewContentReact = "";
805
873
  this.contentDOMElement.style.whiteSpace = "inherit";
806
874
  }
807
875
  let as = this.node.isInline ? "span" : "div";
@@ -815,10 +883,14 @@ var ReactNodeView = class extends import_core2.NodeView {
815
883
  editor: this.editor,
816
884
  props,
817
885
  as,
818
- className: `node-${this.node.type.name} ${className}`.trim(),
819
- attrs: this.options.attrs
886
+ className: `node-${this.node.type.name} ${className}`.trim()
820
887
  });
888
+ this.updateElementAttributes();
821
889
  }
890
+ /**
891
+ * Return the DOM element.
892
+ * This is the element that will be used to display the node view.
893
+ */
822
894
  get dom() {
823
895
  var _a;
824
896
  if (this.renderer.element.firstElementChild && !((_a = this.renderer.element.firstElementChild) == null ? void 0 : _a.hasAttribute("data-node-view-wrapper"))) {
@@ -826,15 +898,27 @@ var ReactNodeView = class extends import_core2.NodeView {
826
898
  }
827
899
  return this.renderer.element;
828
900
  }
901
+ /**
902
+ * Return the content DOM element.
903
+ * This is the element that will be used to display the rich-text content of the node.
904
+ */
829
905
  get contentDOM() {
830
906
  if (this.node.isLeaf) {
831
907
  return null;
832
908
  }
833
909
  return this.contentDOMElement;
834
910
  }
911
+ /**
912
+ * On editor selection update, check if the node is selected.
913
+ * If it is, call `selectNode`, otherwise call `deselectNode`.
914
+ */
835
915
  handleSelectionUpdate() {
836
916
  const { from, to } = this.editor.state.selection;
837
- if (from <= this.getPos() && to >= this.getPos() + this.node.nodeSize) {
917
+ const pos = this.getPos();
918
+ if (typeof pos !== "number") {
919
+ return;
920
+ }
921
+ if (from <= pos && to >= pos + this.node.nodeSize) {
838
922
  if (this.renderer.props.selected) {
839
923
  return;
840
924
  }
@@ -846,9 +930,16 @@ var ReactNodeView = class extends import_core2.NodeView {
846
930
  this.deselectNode();
847
931
  }
848
932
  }
849
- update(node, decorations) {
850
- const updateProps = (props) => {
933
+ /**
934
+ * On update, update the React component.
935
+ * To prevent unnecessary updates, the `update` option can be used.
936
+ */
937
+ update(node, decorations, innerDecorations) {
938
+ const rerenderComponent = (props) => {
851
939
  this.renderer.updateProps(props);
940
+ if (typeof this.options.attrs === "function") {
941
+ this.updateElementAttributes();
942
+ }
852
943
  };
853
944
  if (node.type !== this.node.type) {
854
945
  return false;
@@ -856,41 +947,74 @@ var ReactNodeView = class extends import_core2.NodeView {
856
947
  if (typeof this.options.update === "function") {
857
948
  const oldNode = this.node;
858
949
  const oldDecorations = this.decorations;
950
+ const oldInnerDecorations = this.innerDecorations;
859
951
  this.node = node;
860
952
  this.decorations = decorations;
953
+ this.innerDecorations = innerDecorations;
861
954
  return this.options.update({
862
955
  oldNode,
863
956
  oldDecorations,
864
957
  newNode: node,
865
958
  newDecorations: decorations,
866
- updateProps: () => updateProps({ node, decorations })
959
+ oldInnerDecorations,
960
+ innerDecorations,
961
+ updateProps: () => rerenderComponent({ node, decorations, innerDecorations })
867
962
  });
868
963
  }
869
- if (node === this.node && this.decorations === decorations) {
964
+ if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
870
965
  return true;
871
966
  }
872
967
  this.node = node;
873
968
  this.decorations = decorations;
874
- updateProps({ node, decorations });
969
+ this.innerDecorations = innerDecorations;
970
+ rerenderComponent({ node, decorations, innerDecorations });
875
971
  return true;
876
972
  }
973
+ /**
974
+ * Select the node.
975
+ * Add the `selected` prop and the `ProseMirror-selectednode` class.
976
+ */
877
977
  selectNode() {
878
978
  this.renderer.updateProps({
879
979
  selected: true
880
980
  });
881
981
  this.renderer.element.classList.add("ProseMirror-selectednode");
882
982
  }
983
+ /**
984
+ * Deselect the node.
985
+ * Remove the `selected` prop and the `ProseMirror-selectednode` class.
986
+ */
883
987
  deselectNode() {
884
988
  this.renderer.updateProps({
885
989
  selected: false
886
990
  });
887
991
  this.renderer.element.classList.remove("ProseMirror-selectednode");
888
992
  }
993
+ /**
994
+ * Destroy the React component instance.
995
+ */
889
996
  destroy() {
890
997
  this.renderer.destroy();
891
998
  this.editor.off("selectionUpdate", this.handleSelectionUpdate);
892
999
  this.contentDOMElement = null;
893
1000
  }
1001
+ /**
1002
+ * Update the attributes of the top-level element that holds the React component.
1003
+ * Applying the attributes defined in the `attrs` option.
1004
+ */
1005
+ updateElementAttributes() {
1006
+ if (this.options.attrs) {
1007
+ let attrsObj = {};
1008
+ if (typeof this.options.attrs === "function") {
1009
+ const extensionAttributes = this.editor.extensionManager.attributes;
1010
+ const HTMLAttributes2 = (0, import_core2.getRenderedAttributes)(this.node, extensionAttributes);
1011
+ attrsObj = this.options.attrs({ node: this.node, HTMLAttributes: HTMLAttributes2 });
1012
+ } else {
1013
+ attrsObj = this.options.attrs;
1014
+ }
1015
+ this.renderer.updateAttributes(attrsObj);
1016
+ }
1017
+ }
894
1018
  };
895
1019
  function ReactNodeViewRenderer(component, options) {
896
1020
  return (props) => {
@@ -914,6 +1038,7 @@ __reExport(src_exports, require("@tiptap/core"), module.exports);
914
1038
  NodeViewContent,
915
1039
  NodeViewWrapper,
916
1040
  PureEditorContent,
1041
+ ReactNodeView,
917
1042
  ReactNodeViewContext,
918
1043
  ReactNodeViewRenderer,
919
1044
  ReactRenderer,