@tiptap/react 3.0.0-next.1 → 3.0.0-next.2
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 +255 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +155 -42
- package/dist/index.d.ts +155 -42
- package/dist/index.js +244 -113
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
- package/src/BubbleMenu.tsx +70 -50
- package/src/Context.tsx +14 -6
- package/src/FloatingMenu.tsx +51 -45
- package/src/ReactNodeViewRenderer.tsx +152 -41
- package/src/ReactRenderer.tsx +26 -19
- package/src/useEditor.ts +17 -9
- package/src/useEditorState.ts +49 -10
package/dist/index.js
CHANGED
|
@@ -4,7 +4,11 @@ import React3, { useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
|
|
6
6
|
// src/Context.tsx
|
|
7
|
-
import React2, {
|
|
7
|
+
import React2, {
|
|
8
|
+
createContext,
|
|
9
|
+
useContext,
|
|
10
|
+
useMemo
|
|
11
|
+
} from "react";
|
|
8
12
|
|
|
9
13
|
// src/EditorContent.tsx
|
|
10
14
|
import React, {
|
|
@@ -174,8 +178,15 @@ import {
|
|
|
174
178
|
import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim";
|
|
175
179
|
|
|
176
180
|
// src/useEditorState.ts
|
|
177
|
-
import
|
|
181
|
+
import deepEqual from "fast-deep-equal/es6/react";
|
|
182
|
+
import {
|
|
183
|
+
useDebugValue,
|
|
184
|
+
useEffect,
|
|
185
|
+
useLayoutEffect,
|
|
186
|
+
useState
|
|
187
|
+
} from "react";
|
|
178
188
|
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector";
|
|
189
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
179
190
|
var EditorStateManager = class {
|
|
180
191
|
constructor(initialEditor) {
|
|
181
192
|
this.transactionNumber = 0;
|
|
@@ -234,17 +245,18 @@ var EditorStateManager = class {
|
|
|
234
245
|
}
|
|
235
246
|
};
|
|
236
247
|
function useEditorState(options) {
|
|
237
|
-
|
|
248
|
+
var _a;
|
|
249
|
+
const [editorStateManager] = useState(() => new EditorStateManager(options.editor));
|
|
238
250
|
const selectedState = useSyncExternalStoreWithSelector(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
251
|
+
editorStateManager.subscribe,
|
|
252
|
+
editorStateManager.getSnapshot,
|
|
253
|
+
editorStateManager.getServerSnapshot,
|
|
242
254
|
options.selector,
|
|
243
|
-
options.equalityFn
|
|
255
|
+
(_a = options.equalityFn) != null ? _a : deepEqual
|
|
244
256
|
);
|
|
245
|
-
|
|
246
|
-
return
|
|
247
|
-
}, [options.editor,
|
|
257
|
+
useIsomorphicLayoutEffect(() => {
|
|
258
|
+
return editorStateManager.watch(options.editor);
|
|
259
|
+
}, [options.editor, editorStateManager]);
|
|
248
260
|
useDebugValue(selectedState);
|
|
249
261
|
return selectedState;
|
|
250
262
|
}
|
|
@@ -279,6 +291,7 @@ var EditorInstanceManager = class {
|
|
|
279
291
|
this.options = options;
|
|
280
292
|
this.subscriptions = /* @__PURE__ */ new Set();
|
|
281
293
|
this.setEditor(this.getInitialEditor());
|
|
294
|
+
this.scheduleDestroy();
|
|
282
295
|
this.getEditor = this.getEditor.bind(this);
|
|
283
296
|
this.getServerSnapshot = this.getServerSnapshot.bind(this);
|
|
284
297
|
this.subscribe = this.subscribe.bind(this);
|
|
@@ -356,6 +369,14 @@ var EditorInstanceManager = class {
|
|
|
356
369
|
onContentError: (...args) => {
|
|
357
370
|
var _a, _b;
|
|
358
371
|
return (_b = (_a = this.options.current).onContentError) == null ? void 0 : _b.call(_a, ...args);
|
|
372
|
+
},
|
|
373
|
+
onDrop: (...args) => {
|
|
374
|
+
var _a, _b;
|
|
375
|
+
return (_b = (_a = this.options.current).onDrop) == null ? void 0 : _b.call(_a, ...args);
|
|
376
|
+
},
|
|
377
|
+
onPaste: (...args) => {
|
|
378
|
+
var _a, _b;
|
|
379
|
+
return (_b = (_a = this.options.current).onPaste) == null ? void 0 : _b.call(_a, ...args);
|
|
359
380
|
}
|
|
360
381
|
};
|
|
361
382
|
const editor = new Editor(optionsToApply);
|
|
@@ -392,7 +413,10 @@ var EditorInstanceManager = class {
|
|
|
392
413
|
this.isComponentMounted = true;
|
|
393
414
|
clearTimeout(this.scheduledDestructionTimeout);
|
|
394
415
|
if (this.editor && !this.editor.isDestroyed && deps.length === 0) {
|
|
395
|
-
this.editor.setOptions(
|
|
416
|
+
this.editor.setOptions({
|
|
417
|
+
...this.options.current,
|
|
418
|
+
editable: this.editor.isEditable
|
|
419
|
+
});
|
|
396
420
|
} else {
|
|
397
421
|
this.refreshEditorInstance(deps);
|
|
398
422
|
}
|
|
@@ -443,7 +467,7 @@ var EditorInstanceManager = class {
|
|
|
443
467
|
this.setEditor(null);
|
|
444
468
|
}
|
|
445
469
|
}
|
|
446
|
-
},
|
|
470
|
+
}, 1);
|
|
447
471
|
}
|
|
448
472
|
};
|
|
449
473
|
function useEditor(options = {}, deps = []) {
|
|
@@ -460,7 +484,7 @@ function useEditor(options = {}, deps = []) {
|
|
|
460
484
|
useEditorState({
|
|
461
485
|
editor,
|
|
462
486
|
selector: ({ transactionNumber }) => {
|
|
463
|
-
if (options.shouldRerenderOnTransaction === false) {
|
|
487
|
+
if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === void 0) {
|
|
464
488
|
return null;
|
|
465
489
|
}
|
|
466
490
|
if (options.immediatelyRender && transactionNumber === 0) {
|
|
@@ -482,118 +506,143 @@ function EditorProvider({
|
|
|
482
506
|
children,
|
|
483
507
|
slotAfter,
|
|
484
508
|
slotBefore,
|
|
509
|
+
editorContainerProps = {},
|
|
485
510
|
...editorOptions
|
|
486
511
|
}) {
|
|
487
512
|
const editor = useEditor(editorOptions);
|
|
513
|
+
const contextValue = useMemo(() => ({ editor }), [editor]);
|
|
488
514
|
if (!editor) {
|
|
489
515
|
return null;
|
|
490
516
|
}
|
|
491
|
-
return /* @__PURE__ */ React2.createElement(EditorContext.Provider, { value:
|
|
517
|
+
return /* @__PURE__ */ React2.createElement(EditorContext.Provider, { value: contextValue }, slotBefore, /* @__PURE__ */ React2.createElement(EditorConsumer, null, ({ editor: currentEditor }) => /* @__PURE__ */ React2.createElement(EditorContent, { editor: currentEditor, ...editorContainerProps })), children, slotAfter);
|
|
492
518
|
}
|
|
493
519
|
|
|
494
520
|
// src/BubbleMenu.tsx
|
|
495
|
-
var BubbleMenu = (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
editor
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
521
|
+
var BubbleMenu = React3.forwardRef(
|
|
522
|
+
({
|
|
523
|
+
pluginKey = "bubbleMenu",
|
|
524
|
+
editor,
|
|
525
|
+
updateDelay,
|
|
526
|
+
resizeDelay,
|
|
527
|
+
shouldShow = null,
|
|
528
|
+
options,
|
|
529
|
+
children,
|
|
530
|
+
...restProps
|
|
531
|
+
}, ref) => {
|
|
532
|
+
const menuEl = useRef2(document.createElement("div"));
|
|
533
|
+
if (typeof ref === "function") {
|
|
534
|
+
ref(menuEl.current);
|
|
535
|
+
} else if (ref) {
|
|
536
|
+
ref.current = menuEl.current;
|
|
537
|
+
}
|
|
538
|
+
const { editor: currentEditor } = useCurrentEditor();
|
|
539
|
+
useEffect3(() => {
|
|
540
|
+
const bubbleMenuElement = menuEl.current;
|
|
541
|
+
bubbleMenuElement.style.visibility = "hidden";
|
|
542
|
+
bubbleMenuElement.style.position = "absolute";
|
|
543
|
+
if ((editor == null ? void 0 : editor.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const attachToEditor = editor || currentEditor;
|
|
547
|
+
if (!attachToEditor) {
|
|
548
|
+
console.warn(
|
|
549
|
+
"BubbleMenu component is not rendered inside of an editor component or does not have editor prop."
|
|
550
|
+
);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const plugin = BubbleMenuPlugin({
|
|
554
|
+
updateDelay,
|
|
555
|
+
resizeDelay,
|
|
556
|
+
editor: attachToEditor,
|
|
557
|
+
element: bubbleMenuElement,
|
|
558
|
+
pluginKey,
|
|
559
|
+
shouldShow,
|
|
560
|
+
options
|
|
533
561
|
});
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
};
|
|
562
|
+
attachToEditor.registerPlugin(plugin);
|
|
563
|
+
return () => {
|
|
564
|
+
attachToEditor.unregisterPlugin(pluginKey);
|
|
565
|
+
window.requestAnimationFrame(() => {
|
|
566
|
+
if (bubbleMenuElement.parentNode) {
|
|
567
|
+
bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
};
|
|
571
|
+
}, [editor, currentEditor]);
|
|
572
|
+
return createPortal(
|
|
573
|
+
/* @__PURE__ */ React3.createElement(
|
|
574
|
+
"div",
|
|
575
|
+
{
|
|
576
|
+
...restProps
|
|
577
|
+
},
|
|
578
|
+
children
|
|
579
|
+
),
|
|
580
|
+
menuEl.current
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
);
|
|
542
584
|
|
|
543
585
|
// src/FloatingMenu.tsx
|
|
544
586
|
import { FloatingMenuPlugin } from "@tiptap/extension-floating-menu";
|
|
545
|
-
import React4, {
|
|
546
|
-
useEffect as useEffect4,
|
|
547
|
-
useRef as useRef3
|
|
548
|
-
} from "react";
|
|
587
|
+
import React4, { useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
549
588
|
import { createPortal as createPortal2 } from "react-dom";
|
|
550
|
-
var FloatingMenu = (
|
|
589
|
+
var FloatingMenu = React4.forwardRef(({
|
|
590
|
+
pluginKey = "floatingMenu",
|
|
591
|
+
editor,
|
|
592
|
+
shouldShow = null,
|
|
593
|
+
options,
|
|
594
|
+
children,
|
|
595
|
+
...restProps
|
|
596
|
+
}, ref) => {
|
|
551
597
|
const menuEl = useRef3(document.createElement("div"));
|
|
598
|
+
if (typeof ref === "function") {
|
|
599
|
+
ref(menuEl.current);
|
|
600
|
+
} else if (ref) {
|
|
601
|
+
ref.current = menuEl.current;
|
|
602
|
+
}
|
|
552
603
|
const { editor: currentEditor } = useCurrentEditor();
|
|
553
604
|
useEffect4(() => {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if ((
|
|
605
|
+
const floatingMenuElement = menuEl.current;
|
|
606
|
+
floatingMenuElement.style.visibility = "hidden";
|
|
607
|
+
floatingMenuElement.style.position = "absolute";
|
|
608
|
+
if ((editor == null ? void 0 : editor.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
|
|
558
609
|
return;
|
|
559
610
|
}
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
} = props;
|
|
566
|
-
const menuEditor = editor || currentEditor;
|
|
567
|
-
if (!menuEditor) {
|
|
568
|
-
console.warn("FloatingMenu component is not rendered inside of an editor component or does not have editor prop.");
|
|
611
|
+
const attachToEditor = editor || currentEditor;
|
|
612
|
+
if (!attachToEditor) {
|
|
613
|
+
console.warn(
|
|
614
|
+
"FloatingMenu component is not rendered inside of an editor component or does not have editor prop."
|
|
615
|
+
);
|
|
569
616
|
return;
|
|
570
617
|
}
|
|
571
618
|
const plugin = FloatingMenuPlugin({
|
|
619
|
+
editor: attachToEditor,
|
|
620
|
+
element: floatingMenuElement,
|
|
572
621
|
pluginKey,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
options,
|
|
576
|
-
shouldShow
|
|
622
|
+
shouldShow,
|
|
623
|
+
options
|
|
577
624
|
});
|
|
578
|
-
|
|
625
|
+
attachToEditor.registerPlugin(plugin);
|
|
579
626
|
return () => {
|
|
580
|
-
|
|
627
|
+
attachToEditor.unregisterPlugin(pluginKey);
|
|
581
628
|
window.requestAnimationFrame(() => {
|
|
582
|
-
if (
|
|
583
|
-
|
|
629
|
+
if (floatingMenuElement.parentNode) {
|
|
630
|
+
floatingMenuElement.parentNode.removeChild(floatingMenuElement);
|
|
584
631
|
}
|
|
585
632
|
});
|
|
586
633
|
};
|
|
587
|
-
}, [
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
634
|
+
}, [editor, currentEditor]);
|
|
635
|
+
return createPortal2(
|
|
636
|
+
/* @__PURE__ */ React4.createElement(
|
|
637
|
+
"div",
|
|
638
|
+
{
|
|
639
|
+
...restProps
|
|
640
|
+
},
|
|
641
|
+
children
|
|
642
|
+
),
|
|
593
643
|
menuEl.current
|
|
594
644
|
);
|
|
595
|
-
|
|
596
|
-
};
|
|
645
|
+
});
|
|
597
646
|
|
|
598
647
|
// src/NodeViewContent.tsx
|
|
599
648
|
import React5 from "react";
|
|
@@ -651,6 +700,7 @@ var NodeViewWrapper = React6.forwardRef((props, ref) => {
|
|
|
651
700
|
|
|
652
701
|
// src/ReactNodeViewRenderer.tsx
|
|
653
702
|
import {
|
|
703
|
+
getRenderedAttributes,
|
|
654
704
|
NodeView
|
|
655
705
|
} from "@tiptap/core";
|
|
656
706
|
import React8 from "react";
|
|
@@ -666,12 +716,14 @@ function isForwardRefComponent(Component) {
|
|
|
666
716
|
return !!(typeof Component === "object" && ((_a = Component.$$typeof) == null ? void 0 : _a.toString()) === "Symbol(react.forward_ref)");
|
|
667
717
|
}
|
|
668
718
|
var ReactRenderer = class {
|
|
719
|
+
/**
|
|
720
|
+
* Immediately creates element and renders the provided React component.
|
|
721
|
+
*/
|
|
669
722
|
constructor(component, {
|
|
670
723
|
editor,
|
|
671
724
|
props = {},
|
|
672
725
|
as = "div",
|
|
673
|
-
className = ""
|
|
674
|
-
attrs
|
|
726
|
+
className = ""
|
|
675
727
|
}) {
|
|
676
728
|
this.ref = null;
|
|
677
729
|
this.id = Math.floor(Math.random() * 4294967295).toString();
|
|
@@ -683,11 +735,6 @@ var ReactRenderer = class {
|
|
|
683
735
|
if (className) {
|
|
684
736
|
this.element.classList.add(...className.split(" "));
|
|
685
737
|
}
|
|
686
|
-
if (attrs) {
|
|
687
|
-
Object.keys(attrs).forEach((key) => {
|
|
688
|
-
this.element.setAttribute(key, attrs[key]);
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
738
|
if (this.editor.isInitialized) {
|
|
692
739
|
flushSync(() => {
|
|
693
740
|
this.render();
|
|
@@ -696,6 +743,9 @@ var ReactRenderer = class {
|
|
|
696
743
|
this.render();
|
|
697
744
|
}
|
|
698
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* Render the React component.
|
|
748
|
+
*/
|
|
699
749
|
render() {
|
|
700
750
|
var _a;
|
|
701
751
|
const Component = this.component;
|
|
@@ -706,9 +756,12 @@ var ReactRenderer = class {
|
|
|
706
756
|
this.ref = ref;
|
|
707
757
|
};
|
|
708
758
|
}
|
|
709
|
-
this.reactElement = React7.createElement(Component, props);
|
|
759
|
+
this.reactElement = /* @__PURE__ */ React7.createElement(Component, { ...props });
|
|
710
760
|
(_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.setRenderer(this.id, this);
|
|
711
761
|
}
|
|
762
|
+
/**
|
|
763
|
+
* Re-renders the React component with new props.
|
|
764
|
+
*/
|
|
712
765
|
updateProps(props = {}) {
|
|
713
766
|
this.props = {
|
|
714
767
|
...this.props,
|
|
@@ -716,22 +769,40 @@ var ReactRenderer = class {
|
|
|
716
769
|
};
|
|
717
770
|
this.render();
|
|
718
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Destroy the React component.
|
|
774
|
+
*/
|
|
719
775
|
destroy() {
|
|
720
776
|
var _a;
|
|
721
777
|
const editor = this.editor;
|
|
722
778
|
(_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.removeRenderer(this.id);
|
|
723
779
|
}
|
|
780
|
+
/**
|
|
781
|
+
* Update the attributes of the element that holds the React component.
|
|
782
|
+
*/
|
|
783
|
+
updateAttributes(attributes) {
|
|
784
|
+
Object.keys(attributes).forEach((key) => {
|
|
785
|
+
this.element.setAttribute(key, attributes[key]);
|
|
786
|
+
});
|
|
787
|
+
}
|
|
724
788
|
};
|
|
725
789
|
|
|
726
790
|
// src/ReactNodeViewRenderer.tsx
|
|
727
791
|
var ReactNodeView = class extends NodeView {
|
|
792
|
+
/**
|
|
793
|
+
* Setup the React component.
|
|
794
|
+
* Called on initialization.
|
|
795
|
+
*/
|
|
728
796
|
mount() {
|
|
729
797
|
const props = {
|
|
730
798
|
editor: this.editor,
|
|
731
799
|
node: this.node,
|
|
732
800
|
decorations: this.decorations,
|
|
801
|
+
innerDecorations: this.innerDecorations,
|
|
802
|
+
view: this.view,
|
|
733
803
|
selected: false,
|
|
734
804
|
extension: this.extension,
|
|
805
|
+
HTMLAttributes: this.HTMLAttributes,
|
|
735
806
|
getPos: () => this.getPos(),
|
|
736
807
|
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
|
|
737
808
|
deleteNode: () => this.deleteNode()
|
|
@@ -750,9 +821,11 @@ var ReactNodeView = class extends NodeView {
|
|
|
750
821
|
};
|
|
751
822
|
const context = { onDragStart, nodeViewContentRef };
|
|
752
823
|
const Component = this.component;
|
|
753
|
-
const ReactNodeViewProvider = React8.memo(
|
|
754
|
-
|
|
755
|
-
|
|
824
|
+
const ReactNodeViewProvider = React8.memo(
|
|
825
|
+
(componentProps) => {
|
|
826
|
+
return /* @__PURE__ */ React8.createElement(ReactNodeViewContext.Provider, { value: context }, React8.createElement(Component, componentProps));
|
|
827
|
+
}
|
|
828
|
+
);
|
|
756
829
|
ReactNodeViewProvider.displayName = "ReactNodeView";
|
|
757
830
|
if (this.node.isLeaf) {
|
|
758
831
|
this.contentDOMElement = null;
|
|
@@ -762,6 +835,7 @@ var ReactNodeView = class extends NodeView {
|
|
|
762
835
|
this.contentDOMElement = document.createElement(this.node.isInline ? "span" : "div");
|
|
763
836
|
}
|
|
764
837
|
if (this.contentDOMElement) {
|
|
838
|
+
this.contentDOMElement.dataset.nodeViewContentReact = "";
|
|
765
839
|
this.contentDOMElement.style.whiteSpace = "inherit";
|
|
766
840
|
}
|
|
767
841
|
let as = this.node.isInline ? "span" : "div";
|
|
@@ -775,10 +849,14 @@ var ReactNodeView = class extends NodeView {
|
|
|
775
849
|
editor: this.editor,
|
|
776
850
|
props,
|
|
777
851
|
as,
|
|
778
|
-
className: `node-${this.node.type.name} ${className}`.trim()
|
|
779
|
-
attrs: this.options.attrs
|
|
852
|
+
className: `node-${this.node.type.name} ${className}`.trim()
|
|
780
853
|
});
|
|
854
|
+
this.updateElementAttributes();
|
|
781
855
|
}
|
|
856
|
+
/**
|
|
857
|
+
* Return the DOM element.
|
|
858
|
+
* This is the element that will be used to display the node view.
|
|
859
|
+
*/
|
|
782
860
|
get dom() {
|
|
783
861
|
var _a;
|
|
784
862
|
if (this.renderer.element.firstElementChild && !((_a = this.renderer.element.firstElementChild) == null ? void 0 : _a.hasAttribute("data-node-view-wrapper"))) {
|
|
@@ -786,15 +864,27 @@ var ReactNodeView = class extends NodeView {
|
|
|
786
864
|
}
|
|
787
865
|
return this.renderer.element;
|
|
788
866
|
}
|
|
867
|
+
/**
|
|
868
|
+
* Return the content DOM element.
|
|
869
|
+
* This is the element that will be used to display the rich-text content of the node.
|
|
870
|
+
*/
|
|
789
871
|
get contentDOM() {
|
|
790
872
|
if (this.node.isLeaf) {
|
|
791
873
|
return null;
|
|
792
874
|
}
|
|
793
875
|
return this.contentDOMElement;
|
|
794
876
|
}
|
|
877
|
+
/**
|
|
878
|
+
* On editor selection update, check if the node is selected.
|
|
879
|
+
* If it is, call `selectNode`, otherwise call `deselectNode`.
|
|
880
|
+
*/
|
|
795
881
|
handleSelectionUpdate() {
|
|
796
882
|
const { from, to } = this.editor.state.selection;
|
|
797
|
-
|
|
883
|
+
const pos = this.getPos();
|
|
884
|
+
if (typeof pos !== "number") {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (from <= pos && to >= pos + this.node.nodeSize) {
|
|
798
888
|
if (this.renderer.props.selected) {
|
|
799
889
|
return;
|
|
800
890
|
}
|
|
@@ -806,9 +896,16 @@ var ReactNodeView = class extends NodeView {
|
|
|
806
896
|
this.deselectNode();
|
|
807
897
|
}
|
|
808
898
|
}
|
|
809
|
-
|
|
810
|
-
|
|
899
|
+
/**
|
|
900
|
+
* On update, update the React component.
|
|
901
|
+
* To prevent unnecessary updates, the `update` option can be used.
|
|
902
|
+
*/
|
|
903
|
+
update(node, decorations, innerDecorations) {
|
|
904
|
+
const rerenderComponent = (props) => {
|
|
811
905
|
this.renderer.updateProps(props);
|
|
906
|
+
if (typeof this.options.attrs === "function") {
|
|
907
|
+
this.updateElementAttributes();
|
|
908
|
+
}
|
|
812
909
|
};
|
|
813
910
|
if (node.type !== this.node.type) {
|
|
814
911
|
return false;
|
|
@@ -816,41 +913,74 @@ var ReactNodeView = class extends NodeView {
|
|
|
816
913
|
if (typeof this.options.update === "function") {
|
|
817
914
|
const oldNode = this.node;
|
|
818
915
|
const oldDecorations = this.decorations;
|
|
916
|
+
const oldInnerDecorations = this.innerDecorations;
|
|
819
917
|
this.node = node;
|
|
820
918
|
this.decorations = decorations;
|
|
919
|
+
this.innerDecorations = innerDecorations;
|
|
821
920
|
return this.options.update({
|
|
822
921
|
oldNode,
|
|
823
922
|
oldDecorations,
|
|
824
923
|
newNode: node,
|
|
825
924
|
newDecorations: decorations,
|
|
826
|
-
|
|
925
|
+
oldInnerDecorations,
|
|
926
|
+
innerDecorations,
|
|
927
|
+
updateProps: () => rerenderComponent({ node, decorations, innerDecorations })
|
|
827
928
|
});
|
|
828
929
|
}
|
|
829
|
-
if (node === this.node && this.decorations === decorations) {
|
|
930
|
+
if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
|
|
830
931
|
return true;
|
|
831
932
|
}
|
|
832
933
|
this.node = node;
|
|
833
934
|
this.decorations = decorations;
|
|
834
|
-
|
|
935
|
+
this.innerDecorations = innerDecorations;
|
|
936
|
+
rerenderComponent({ node, decorations, innerDecorations });
|
|
835
937
|
return true;
|
|
836
938
|
}
|
|
939
|
+
/**
|
|
940
|
+
* Select the node.
|
|
941
|
+
* Add the `selected` prop and the `ProseMirror-selectednode` class.
|
|
942
|
+
*/
|
|
837
943
|
selectNode() {
|
|
838
944
|
this.renderer.updateProps({
|
|
839
945
|
selected: true
|
|
840
946
|
});
|
|
841
947
|
this.renderer.element.classList.add("ProseMirror-selectednode");
|
|
842
948
|
}
|
|
949
|
+
/**
|
|
950
|
+
* Deselect the node.
|
|
951
|
+
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
|
|
952
|
+
*/
|
|
843
953
|
deselectNode() {
|
|
844
954
|
this.renderer.updateProps({
|
|
845
955
|
selected: false
|
|
846
956
|
});
|
|
847
957
|
this.renderer.element.classList.remove("ProseMirror-selectednode");
|
|
848
958
|
}
|
|
959
|
+
/**
|
|
960
|
+
* Destroy the React component instance.
|
|
961
|
+
*/
|
|
849
962
|
destroy() {
|
|
850
963
|
this.renderer.destroy();
|
|
851
964
|
this.editor.off("selectionUpdate", this.handleSelectionUpdate);
|
|
852
965
|
this.contentDOMElement = null;
|
|
853
966
|
}
|
|
967
|
+
/**
|
|
968
|
+
* Update the attributes of the top-level element that holds the React component.
|
|
969
|
+
* Applying the attributes defined in the `attrs` option.
|
|
970
|
+
*/
|
|
971
|
+
updateElementAttributes() {
|
|
972
|
+
if (this.options.attrs) {
|
|
973
|
+
let attrsObj = {};
|
|
974
|
+
if (typeof this.options.attrs === "function") {
|
|
975
|
+
const extensionAttributes = this.editor.extensionManager.attributes;
|
|
976
|
+
const HTMLAttributes2 = getRenderedAttributes(this.node, extensionAttributes);
|
|
977
|
+
attrsObj = this.options.attrs({ node: this.node, HTMLAttributes: HTMLAttributes2 });
|
|
978
|
+
} else {
|
|
979
|
+
attrsObj = this.options.attrs;
|
|
980
|
+
}
|
|
981
|
+
this.renderer.updateAttributes(attrsObj);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
854
984
|
};
|
|
855
985
|
function ReactNodeViewRenderer(component, options) {
|
|
856
986
|
return (props) => {
|
|
@@ -873,6 +1003,7 @@ export {
|
|
|
873
1003
|
NodeViewContent,
|
|
874
1004
|
NodeViewWrapper,
|
|
875
1005
|
PureEditorContent,
|
|
1006
|
+
ReactNodeView,
|
|
876
1007
|
ReactNodeViewContext,
|
|
877
1008
|
ReactNodeViewRenderer,
|
|
878
1009
|
ReactRenderer,
|