@tiptap/react 2.5.9 → 3.0.0-next.0
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 +171 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +172 -274
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +171 -273
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +4 -10
- package/dist/packages/core/src/style.d.ts +1 -1
- package/dist/packages/extension-bubble-menu/src/bubble-menu-plugin.d.ts +35 -12
- package/dist/packages/extension-floating-menu/src/floating-menu-plugin.d.ts +29 -14
- package/dist/packages/react/src/BubbleMenu.d.ts +2 -0
- package/dist/packages/react/src/FloatingMenu.d.ts +1 -0
- package/package.json +7 -7
- package/src/BubbleMenu.tsx +29 -12
- package/src/FloatingMenu.tsx +25 -12
- package/src/useEditor.ts +87 -241
- package/src/useEditorState.ts +60 -75
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
|
|
2
2
|
import React, { forwardRef, useState, useEffect, useDebugValue, useRef, createContext, useContext } from 'react';
|
|
3
|
-
import ReactDOM, { flushSync } from 'react-dom';
|
|
3
|
+
import ReactDOM, { flushSync, createPortal } from 'react-dom';
|
|
4
4
|
import { Editor as Editor$1, NodeView } from '@tiptap/core';
|
|
5
5
|
export * from '@tiptap/core';
|
|
6
6
|
import { FloatingMenuPlugin } from '@tiptap/extension-floating-menu';
|
|
@@ -126,6 +126,17 @@ const EditorContentWithKey = forwardRef((props, ref) => {
|
|
|
126
126
|
});
|
|
127
127
|
const EditorContent = React.memo(EditorContentWithKey);
|
|
128
128
|
|
|
129
|
+
class Editor extends Editor$1 {
|
|
130
|
+
constructor() {
|
|
131
|
+
super(...arguments);
|
|
132
|
+
this.contentComponent = null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var withSelector = {exports: {}};
|
|
137
|
+
|
|
138
|
+
var withSelector_production_min = {};
|
|
139
|
+
|
|
129
140
|
var shim = {exports: {}};
|
|
130
141
|
|
|
131
142
|
var useSyncExternalStoreShim_production_min = {};
|
|
@@ -396,25 +407,20 @@ function requireUseSyncExternalStoreShim_development () {
|
|
|
396
407
|
return useSyncExternalStoreShim_development;
|
|
397
408
|
}
|
|
398
409
|
|
|
399
|
-
|
|
400
|
-
shim.exports = requireUseSyncExternalStoreShim_production_min();
|
|
401
|
-
} else {
|
|
402
|
-
shim.exports = requireUseSyncExternalStoreShim_development();
|
|
403
|
-
}
|
|
410
|
+
var hasRequiredShim;
|
|
404
411
|
|
|
405
|
-
|
|
412
|
+
function requireShim () {
|
|
413
|
+
if (hasRequiredShim) return shim.exports;
|
|
414
|
+
hasRequiredShim = 1;
|
|
406
415
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
416
|
+
if (process.env.NODE_ENV === 'production') {
|
|
417
|
+
shim.exports = requireUseSyncExternalStoreShim_production_min();
|
|
418
|
+
} else {
|
|
419
|
+
shim.exports = requireUseSyncExternalStoreShim_development();
|
|
420
|
+
}
|
|
421
|
+
return shim.exports;
|
|
412
422
|
}
|
|
413
423
|
|
|
414
|
-
var withSelector = {exports: {}};
|
|
415
|
-
|
|
416
|
-
var withSelector_production_min = {};
|
|
417
|
-
|
|
418
424
|
/**
|
|
419
425
|
* @license React
|
|
420
426
|
* use-sync-external-store-shim/with-selector.production.min.js
|
|
@@ -430,7 +436,7 @@ var hasRequiredWithSelector_production_min;
|
|
|
430
436
|
function requireWithSelector_production_min () {
|
|
431
437
|
if (hasRequiredWithSelector_production_min) return withSelector_production_min;
|
|
432
438
|
hasRequiredWithSelector_production_min = 1;
|
|
433
|
-
var h=React,n=
|
|
439
|
+
var h=React,n=requireShim();function p(a,b){return a===b&&(0!==a||1/a===1/b)||a!==a&&b!==b}var q="function"===typeof Object.is?Object.is:p,r=n.useSyncExternalStore,t=h.useRef,u=h.useEffect,v=h.useMemo,w=h.useDebugValue;
|
|
434
440
|
withSelector_production_min.useSyncExternalStoreWithSelector=function(a,b,e,l,g){var c=t(null);if(null===c.current){var f={hasValue:!1,value:null};c.current=f;}else f=c.current;c=v(function(){function a(a){if(!c){c=!0;d=a;a=l(a);if(void 0!==g&&f.hasValue){var b=f.value;if(g(b,a))return k=b}return k=a}b=k;if(q(d,a))return b;var e=l(a);if(void 0!==g&&g(b,e))return b;d=a;return k=e}var c=!1,d,k,m=void 0===e?null:e;return [function(){return a(b())},null===m?void 0:function(){return a(m())}]},[b,e,l,g]);var d=r(a,c[0],c[1]);
|
|
435
441
|
u(function(){f.hasValue=!0;f.value=d;},[d]);w(d);return d};
|
|
436
442
|
return withSelector_production_min;
|
|
@@ -466,7 +472,7 @@ function requireWithSelector_development () {
|
|
|
466
472
|
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
|
|
467
473
|
}
|
|
468
474
|
var React$1 = React;
|
|
469
|
-
var shim =
|
|
475
|
+
var shim = requireShim();
|
|
470
476
|
|
|
471
477
|
/**
|
|
472
478
|
* inlined Object.is polyfill to avoid requiring consumers ship their own
|
|
@@ -620,75 +626,71 @@ var withSelectorExports = withSelector.exports;
|
|
|
620
626
|
* To synchronize the editor instance with the component state,
|
|
621
627
|
* we need to create a separate instance that is not affected by the component re-renders.
|
|
622
628
|
*/
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* Subscribe to the editor instance's changes.
|
|
654
|
-
*/
|
|
655
|
-
subscribe(callback) {
|
|
656
|
-
this.subscribers.add(callback);
|
|
657
|
-
return () => {
|
|
658
|
-
this.subscribers.delete(callback);
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Watch the editor instance for changes.
|
|
663
|
-
*/
|
|
664
|
-
watch(nextEditor) {
|
|
665
|
-
this.editor = nextEditor;
|
|
666
|
-
if (this.editor) {
|
|
667
|
-
/**
|
|
668
|
-
* This will force a re-render when the editor state changes.
|
|
669
|
-
* This is to support things like `editor.can().toggleBold()` in components that `useEditor`.
|
|
670
|
-
* This could be more efficient, but it's a good trade-off for now.
|
|
671
|
-
*/
|
|
672
|
-
const fn = () => {
|
|
673
|
-
this.transactionNumber += 1;
|
|
674
|
-
this.subscribers.forEach(callback => callback());
|
|
675
|
-
};
|
|
676
|
-
const currentEditor = this.editor;
|
|
677
|
-
currentEditor.on('transaction', fn);
|
|
629
|
+
function makeEditorStateInstance(initialEditor) {
|
|
630
|
+
let transactionNumber = 0;
|
|
631
|
+
let lastTransactionNumber = 0;
|
|
632
|
+
let lastSnapshot = { editor: initialEditor, transactionNumber: 0 };
|
|
633
|
+
let editor = initialEditor;
|
|
634
|
+
const subscribers = new Set();
|
|
635
|
+
const editorInstance = {
|
|
636
|
+
/**
|
|
637
|
+
* Get the current editor instance.
|
|
638
|
+
*/
|
|
639
|
+
getSnapshot() {
|
|
640
|
+
if (transactionNumber === lastTransactionNumber) {
|
|
641
|
+
return lastSnapshot;
|
|
642
|
+
}
|
|
643
|
+
lastTransactionNumber = transactionNumber;
|
|
644
|
+
lastSnapshot = { editor, transactionNumber };
|
|
645
|
+
return lastSnapshot;
|
|
646
|
+
},
|
|
647
|
+
/**
|
|
648
|
+
* Always disable the editor on the server-side.
|
|
649
|
+
*/
|
|
650
|
+
getServerSnapshot() {
|
|
651
|
+
return { editor: null, transactionNumber: 0 };
|
|
652
|
+
},
|
|
653
|
+
/**
|
|
654
|
+
* Subscribe to the editor instance's changes.
|
|
655
|
+
*/
|
|
656
|
+
subscribe(callback) {
|
|
657
|
+
subscribers.add(callback);
|
|
678
658
|
return () => {
|
|
679
|
-
|
|
659
|
+
subscribers.delete(callback);
|
|
680
660
|
};
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
|
|
661
|
+
},
|
|
662
|
+
/**
|
|
663
|
+
* Watch the editor instance for changes.
|
|
664
|
+
*/
|
|
665
|
+
watch(nextEditor) {
|
|
666
|
+
editor = nextEditor;
|
|
667
|
+
if (editor) {
|
|
668
|
+
/**
|
|
669
|
+
* This will force a re-render when the editor state changes.
|
|
670
|
+
* This is to support things like `editor.can().toggleBold()` in components that `useEditor`.
|
|
671
|
+
* This could be more efficient, but it's a good trade-off for now.
|
|
672
|
+
*/
|
|
673
|
+
const fn = () => {
|
|
674
|
+
transactionNumber += 1;
|
|
675
|
+
subscribers.forEach(callback => callback());
|
|
676
|
+
};
|
|
677
|
+
const currentEditor = editor;
|
|
678
|
+
currentEditor.on('transaction', fn);
|
|
679
|
+
return () => {
|
|
680
|
+
currentEditor.off('transaction', fn);
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
};
|
|
685
|
+
return editorInstance;
|
|
684
686
|
}
|
|
685
687
|
function useEditorState(options) {
|
|
686
|
-
const [editorInstance] = useState(() =>
|
|
688
|
+
const [editorInstance] = useState(() => makeEditorStateInstance(options.editor));
|
|
687
689
|
// Using the `useSyncExternalStore` hook to sync the editor instance with the component state
|
|
688
690
|
const selectedState = withSelectorExports.useSyncExternalStoreWithSelector(editorInstance.subscribe, editorInstance.getSnapshot, editorInstance.getServerSnapshot, options.selector, options.equalityFn);
|
|
689
691
|
useEffect(() => {
|
|
690
692
|
return editorInstance.watch(options.editor);
|
|
691
|
-
}, [options.editor
|
|
693
|
+
}, [options.editor]);
|
|
692
694
|
useDebugValue(selectedState);
|
|
693
695
|
return selectedState;
|
|
694
696
|
}
|
|
@@ -697,50 +699,25 @@ const isDev = process.env.NODE_ENV !== 'production';
|
|
|
697
699
|
const isSSR = typeof window === 'undefined';
|
|
698
700
|
const isNext = isSSR || Boolean(typeof window !== 'undefined' && window.next);
|
|
699
701
|
/**
|
|
700
|
-
*
|
|
702
|
+
* Create a new editor instance. And attach event listeners.
|
|
701
703
|
*/
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
*/
|
|
720
|
-
this.previousDeps = null;
|
|
721
|
-
/**
|
|
722
|
-
* The unique instance ID. This is used to identify the editor instance. And will be re-generated for each new instance.
|
|
723
|
-
*/
|
|
724
|
-
this.instanceId = '';
|
|
725
|
-
this.options = options;
|
|
726
|
-
this.subscriptions = new Set();
|
|
727
|
-
this.setEditor(this.getInitialEditor());
|
|
728
|
-
this.getEditor = this.getEditor.bind(this);
|
|
729
|
-
this.getServerSnapshot = this.getServerSnapshot.bind(this);
|
|
730
|
-
this.subscribe = this.subscribe.bind(this);
|
|
731
|
-
this.refreshEditorInstance = this.refreshEditorInstance.bind(this);
|
|
732
|
-
this.scheduleDestroy = this.scheduleDestroy.bind(this);
|
|
733
|
-
this.onRender = this.onRender.bind(this);
|
|
734
|
-
this.createEditor = this.createEditor.bind(this);
|
|
735
|
-
}
|
|
736
|
-
setEditor(editor) {
|
|
737
|
-
this.editor = editor;
|
|
738
|
-
this.instanceId = Math.random().toString(36).slice(2, 9);
|
|
739
|
-
// Notify all subscribers that the editor instance has been created
|
|
740
|
-
this.subscriptions.forEach(cb => cb());
|
|
741
|
-
}
|
|
742
|
-
getInitialEditor() {
|
|
743
|
-
if (this.options.current.immediatelyRender === undefined) {
|
|
704
|
+
function createEditor(options) {
|
|
705
|
+
const editor = new Editor(options.current);
|
|
706
|
+
editor.on('beforeCreate', (...args) => { var _a, _b; return (_b = (_a = options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
707
|
+
editor.on('blur', (...args) => { var _a, _b; return (_b = (_a = options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
708
|
+
editor.on('create', (...args) => { var _a, _b; return (_b = (_a = options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
709
|
+
editor.on('destroy', (...args) => { var _a, _b; return (_b = (_a = options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
710
|
+
editor.on('focus', (...args) => { var _a, _b; return (_b = (_a = options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
711
|
+
editor.on('selectionUpdate', (...args) => { var _a, _b; return (_b = (_a = options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
712
|
+
editor.on('transaction', (...args) => { var _a, _b; return (_b = (_a = options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
713
|
+
editor.on('update', (...args) => { var _a, _b; return (_b = (_a = options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
714
|
+
editor.on('contentError', (...args) => { var _a, _b; return (_b = (_a = options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
715
|
+
return editor;
|
|
716
|
+
}
|
|
717
|
+
function useEditor(options = {}, deps = []) {
|
|
718
|
+
const mostRecentOptions = useRef(options);
|
|
719
|
+
const [editor, setEditor] = useState(() => {
|
|
720
|
+
if (options.immediatelyRender === undefined) {
|
|
744
721
|
if (isSSR || isNext) {
|
|
745
722
|
// TODO in the next major release, we should throw an error here
|
|
746
723
|
if (isDev) {
|
|
@@ -754,148 +731,55 @@ class EditorInstanceManager {
|
|
|
754
731
|
return null;
|
|
755
732
|
}
|
|
756
733
|
// Default to immediately rendering when client-side rendering
|
|
757
|
-
return
|
|
734
|
+
return createEditor(mostRecentOptions);
|
|
758
735
|
}
|
|
759
|
-
if (
|
|
736
|
+
if (options.immediatelyRender && isSSR && isDev) {
|
|
760
737
|
// Warn in development, to make sure the developer is aware that tiptap cannot be SSR'd, set `immediatelyRender` to `false` to avoid hydration mismatches.
|
|
761
738
|
throw new Error('Tiptap Error: SSR has been detected, and `immediatelyRender` has been set to `true` this is an unsupported configuration that may result in errors, explicitly set `immediatelyRender` to `false` to avoid hydration mismatches.');
|
|
762
739
|
}
|
|
763
|
-
if (
|
|
764
|
-
return
|
|
740
|
+
if (options.immediatelyRender) {
|
|
741
|
+
return createEditor(mostRecentOptions);
|
|
765
742
|
}
|
|
766
743
|
return null;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
return editor;
|
|
785
|
-
}
|
|
786
|
-
/**
|
|
787
|
-
* Get the current editor instance.
|
|
788
|
-
*/
|
|
789
|
-
getEditor() {
|
|
790
|
-
return this.editor;
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* Always disable the editor on the server-side.
|
|
794
|
-
*/
|
|
795
|
-
getServerSnapshot() {
|
|
796
|
-
return null;
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Subscribe to the editor instance's changes.
|
|
800
|
-
*/
|
|
801
|
-
subscribe(onStoreChange) {
|
|
802
|
-
this.subscriptions.add(onStoreChange);
|
|
803
|
-
return () => {
|
|
804
|
-
this.subscriptions.delete(onStoreChange);
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* On each render, we will create, update, or destroy the editor instance.
|
|
809
|
-
* @param deps The dependencies to watch for changes
|
|
810
|
-
* @returns A cleanup function
|
|
811
|
-
*/
|
|
812
|
-
onRender(deps) {
|
|
813
|
-
// The returned callback will run on each render
|
|
814
|
-
return () => {
|
|
815
|
-
this.isComponentMounted = true;
|
|
816
|
-
// Cleanup any scheduled destructions, since we are currently rendering
|
|
817
|
-
clearTimeout(this.scheduledDestructionTimeout);
|
|
818
|
-
if (this.editor && !this.editor.isDestroyed && deps.length === 0) {
|
|
819
|
-
// if the editor does exist & deps are empty, we don't need to re-initialize the editor
|
|
820
|
-
// we can fast-path to update the editor options on the existing instance
|
|
821
|
-
this.editor.setOptions(this.options.current);
|
|
822
|
-
}
|
|
823
|
-
else {
|
|
824
|
-
// When the editor:
|
|
825
|
-
// - does not yet exist
|
|
826
|
-
// - is destroyed
|
|
827
|
-
// - the deps array changes
|
|
828
|
-
// We need to destroy the editor instance and re-initialize it
|
|
829
|
-
this.refreshEditorInstance(deps);
|
|
744
|
+
});
|
|
745
|
+
const mostRecentEditor = useRef(editor);
|
|
746
|
+
mostRecentEditor.current = editor;
|
|
747
|
+
useDebugValue(editor);
|
|
748
|
+
// This effect will handle creating/updating the editor instance
|
|
749
|
+
useEffect(() => {
|
|
750
|
+
const destroyUnusedEditor = (editorInstance) => {
|
|
751
|
+
if (editorInstance) {
|
|
752
|
+
// We need to destroy the editor asynchronously to avoid memory leaks
|
|
753
|
+
// because the editor instance is still being used in the component.
|
|
754
|
+
setTimeout(() => {
|
|
755
|
+
// re-use the editor instance if it hasn't been replaced yet
|
|
756
|
+
// otherwise, asynchronously destroy the old editor instance
|
|
757
|
+
if (editorInstance !== mostRecentEditor.current && !editorInstance.isDestroyed) {
|
|
758
|
+
editorInstance.destroy();
|
|
759
|
+
}
|
|
760
|
+
});
|
|
830
761
|
}
|
|
831
|
-
return () => {
|
|
832
|
-
this.isComponentMounted = false;
|
|
833
|
-
this.scheduleDestroy();
|
|
834
|
-
};
|
|
835
762
|
};
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if (this.editor && !this.editor.isDestroyed) {
|
|
842
|
-
// Editor instance already exists
|
|
843
|
-
if (this.previousDeps === null) {
|
|
844
|
-
// If lastDeps has not yet been initialized, reuse the current editor instance
|
|
845
|
-
this.previousDeps = deps;
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
const depsAreEqual = this.previousDeps.length === deps.length
|
|
849
|
-
&& this.previousDeps.every((dep, index) => dep === deps[index]);
|
|
850
|
-
if (depsAreEqual) {
|
|
851
|
-
// deps exist and are equal, no need to recreate
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
763
|
+
let editorInstance = mostRecentEditor.current;
|
|
764
|
+
if (!editorInstance) {
|
|
765
|
+
editorInstance = createEditor(mostRecentOptions);
|
|
766
|
+
setEditor(editorInstance);
|
|
767
|
+
return () => destroyUnusedEditor(editorInstance);
|
|
854
768
|
}
|
|
855
|
-
if (
|
|
856
|
-
//
|
|
857
|
-
|
|
769
|
+
if (!Array.isArray(deps) || deps.length === 0) {
|
|
770
|
+
// if the editor does exist & deps are empty, we don't need to re-initialize the editor
|
|
771
|
+
// we can fast-path to update the editor options on the existing instance
|
|
772
|
+
editorInstance.setOptions(options);
|
|
773
|
+
return () => destroyUnusedEditor(editorInstance);
|
|
858
774
|
}
|
|
859
|
-
|
|
860
|
-
//
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
*/
|
|
868
|
-
scheduleDestroy() {
|
|
869
|
-
const currentInstanceId = this.instanceId;
|
|
870
|
-
const currentEditor = this.editor;
|
|
871
|
-
// Wait a tick to see if the component is still mounted
|
|
872
|
-
this.scheduledDestructionTimeout = setTimeout(() => {
|
|
873
|
-
if (this.isComponentMounted && this.instanceId === currentInstanceId) {
|
|
874
|
-
// If still mounted on the next tick, with the same instanceId, do not destroy the editor
|
|
875
|
-
if (currentEditor) {
|
|
876
|
-
// just re-apply options as they might have changed
|
|
877
|
-
currentEditor.setOptions(this.options.current);
|
|
878
|
-
}
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
if (currentEditor && !currentEditor.isDestroyed) {
|
|
882
|
-
currentEditor.destroy();
|
|
883
|
-
if (this.instanceId === currentInstanceId) {
|
|
884
|
-
this.setEditor(null);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
}, 0);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
function useEditor(options = {}, deps = []) {
|
|
891
|
-
const mostRecentOptions = useRef(options);
|
|
892
|
-
mostRecentOptions.current = options;
|
|
893
|
-
const [instanceManager] = useState(() => new EditorInstanceManager(mostRecentOptions));
|
|
894
|
-
const editor = shimExports.useSyncExternalStore(instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot);
|
|
895
|
-
useDebugValue(editor);
|
|
896
|
-
// This effect will handle creating/updating the editor instance
|
|
897
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
898
|
-
useEffect(instanceManager.onRender(deps));
|
|
775
|
+
// We need to destroy the editor instance and re-initialize it
|
|
776
|
+
// when the deps array changes
|
|
777
|
+
editorInstance.destroy();
|
|
778
|
+
// the deps array is used to re-initialize the editor instance
|
|
779
|
+
editorInstance = createEditor(mostRecentOptions);
|
|
780
|
+
setEditor(editorInstance);
|
|
781
|
+
return () => destroyUnusedEditor(editorInstance);
|
|
782
|
+
}, deps);
|
|
899
783
|
// The default behavior is to re-render on each transaction
|
|
900
784
|
// This is legacy behavior that will be removed in future versions
|
|
901
785
|
useEditorState({
|
|
@@ -941,17 +825,16 @@ function EditorProvider({ children, slotAfter, slotBefore, ...editorOptions }) {
|
|
|
941
825
|
}
|
|
942
826
|
|
|
943
827
|
const BubbleMenu = (props) => {
|
|
944
|
-
const
|
|
828
|
+
const menuEl = useRef(document.createElement('div'));
|
|
945
829
|
const { editor: currentEditor } = useCurrentEditor();
|
|
946
830
|
useEffect(() => {
|
|
947
831
|
var _a;
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
}
|
|
832
|
+
menuEl.current.style.visibility = 'hidden';
|
|
833
|
+
menuEl.current.style.position = 'absolute';
|
|
951
834
|
if (((_a = props.editor) === null || _a === void 0 ? void 0 : _a.isDestroyed) || (currentEditor === null || currentEditor === void 0 ? void 0 : currentEditor.isDestroyed)) {
|
|
952
835
|
return;
|
|
953
836
|
}
|
|
954
|
-
const { pluginKey = 'bubbleMenu', editor,
|
|
837
|
+
const { pluginKey = 'bubbleMenu', editor, updateDelay, resizeDelay, shouldShow = null, } = props;
|
|
955
838
|
const menuEditor = editor || currentEditor;
|
|
956
839
|
if (!menuEditor) {
|
|
957
840
|
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.');
|
|
@@ -959,30 +842,38 @@ const BubbleMenu = (props) => {
|
|
|
959
842
|
}
|
|
960
843
|
const plugin = BubbleMenuPlugin({
|
|
961
844
|
updateDelay,
|
|
845
|
+
resizeDelay,
|
|
962
846
|
editor: menuEditor,
|
|
963
|
-
element,
|
|
847
|
+
element: menuEl.current,
|
|
964
848
|
pluginKey,
|
|
965
849
|
shouldShow,
|
|
966
|
-
|
|
850
|
+
options: props.options,
|
|
967
851
|
});
|
|
968
852
|
menuEditor.registerPlugin(plugin);
|
|
969
|
-
return () =>
|
|
970
|
-
|
|
971
|
-
|
|
853
|
+
return () => {
|
|
854
|
+
menuEditor.unregisterPlugin(pluginKey);
|
|
855
|
+
window.requestAnimationFrame(() => {
|
|
856
|
+
if (menuEl.current.parentNode) {
|
|
857
|
+
menuEl.current.parentNode.removeChild(menuEl.current);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
};
|
|
861
|
+
}, [props.editor, currentEditor]);
|
|
862
|
+
const portal = createPortal((React.createElement("div", { className: props.className }, props.children)), menuEl.current);
|
|
863
|
+
return (React.createElement(React.Fragment, null, portal));
|
|
972
864
|
};
|
|
973
865
|
|
|
974
866
|
const FloatingMenu = (props) => {
|
|
975
|
-
const
|
|
867
|
+
const menuEl = useRef(document.createElement('div'));
|
|
976
868
|
const { editor: currentEditor } = useCurrentEditor();
|
|
977
869
|
useEffect(() => {
|
|
978
870
|
var _a;
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
}
|
|
871
|
+
menuEl.current.style.visibility = 'hidden';
|
|
872
|
+
menuEl.current.style.position = 'absolute';
|
|
982
873
|
if (((_a = props.editor) === null || _a === void 0 ? void 0 : _a.isDestroyed) || (currentEditor === null || currentEditor === void 0 ? void 0 : currentEditor.isDestroyed)) {
|
|
983
874
|
return;
|
|
984
875
|
}
|
|
985
|
-
const { pluginKey = 'floatingMenu', editor,
|
|
876
|
+
const { pluginKey = 'floatingMenu', editor, options, shouldShow = null, } = props;
|
|
986
877
|
const menuEditor = editor || currentEditor;
|
|
987
878
|
if (!menuEditor) {
|
|
988
879
|
console.warn('FloatingMenu component is not rendered inside of an editor component or does not have editor prop.');
|
|
@@ -991,18 +882,25 @@ const FloatingMenu = (props) => {
|
|
|
991
882
|
const plugin = FloatingMenuPlugin({
|
|
992
883
|
pluginKey,
|
|
993
884
|
editor: menuEditor,
|
|
994
|
-
element,
|
|
995
|
-
|
|
885
|
+
element: menuEl.current,
|
|
886
|
+
options,
|
|
996
887
|
shouldShow,
|
|
997
888
|
});
|
|
998
889
|
menuEditor.registerPlugin(plugin);
|
|
999
|
-
return () =>
|
|
890
|
+
return () => {
|
|
891
|
+
menuEditor.unregisterPlugin(pluginKey);
|
|
892
|
+
window.requestAnimationFrame(() => {
|
|
893
|
+
if (menuEl.current.parentNode) {
|
|
894
|
+
menuEl.current.parentNode.removeChild(menuEl.current);
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
};
|
|
1000
898
|
}, [
|
|
1001
899
|
props.editor,
|
|
1002
900
|
currentEditor,
|
|
1003
|
-
element,
|
|
1004
901
|
]);
|
|
1005
|
-
|
|
902
|
+
const portal = createPortal((React.createElement("div", { className: props.className }, props.children)), menuEl.current);
|
|
903
|
+
return (React.createElement(React.Fragment, null, portal));
|
|
1006
904
|
};
|
|
1007
905
|
|
|
1008
906
|
const ReactNodeViewContext = createContext({
|