@tiptap/react 2.5.7 → 2.5.9
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 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +255 -205
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +255 -205
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/core/src/Extension.d.ts +1 -1
- package/dist/packages/core/src/Mark.d.ts +1 -1
- package/dist/packages/core/src/Node.d.ts +1 -1
- package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +10 -4
- package/package.json +7 -7
- package/src/useEditor.ts +231 -165
- package/src/useEditorState.ts +75 -60
package/dist/index.js
CHANGED
|
@@ -126,17 +126,6 @@ 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
|
-
|
|
140
129
|
var shim = {exports: {}};
|
|
141
130
|
|
|
142
131
|
var useSyncExternalStoreShim_production_min = {};
|
|
@@ -407,20 +396,25 @@ function requireUseSyncExternalStoreShim_development () {
|
|
|
407
396
|
return useSyncExternalStoreShim_development;
|
|
408
397
|
}
|
|
409
398
|
|
|
410
|
-
|
|
399
|
+
if (process.env.NODE_ENV === 'production') {
|
|
400
|
+
shim.exports = requireUseSyncExternalStoreShim_production_min();
|
|
401
|
+
} else {
|
|
402
|
+
shim.exports = requireUseSyncExternalStoreShim_development();
|
|
403
|
+
}
|
|
411
404
|
|
|
412
|
-
|
|
413
|
-
if (hasRequiredShim) return shim.exports;
|
|
414
|
-
hasRequiredShim = 1;
|
|
405
|
+
var shimExports = shim.exports;
|
|
415
406
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return shim.exports;
|
|
407
|
+
class Editor extends Editor$1 {
|
|
408
|
+
constructor() {
|
|
409
|
+
super(...arguments);
|
|
410
|
+
this.contentComponent = null;
|
|
411
|
+
}
|
|
422
412
|
}
|
|
423
413
|
|
|
414
|
+
var withSelector = {exports: {}};
|
|
415
|
+
|
|
416
|
+
var withSelector_production_min = {};
|
|
417
|
+
|
|
424
418
|
/**
|
|
425
419
|
* @license React
|
|
426
420
|
* use-sync-external-store-shim/with-selector.production.min.js
|
|
@@ -436,7 +430,7 @@ var hasRequiredWithSelector_production_min;
|
|
|
436
430
|
function requireWithSelector_production_min () {
|
|
437
431
|
if (hasRequiredWithSelector_production_min) return withSelector_production_min;
|
|
438
432
|
hasRequiredWithSelector_production_min = 1;
|
|
439
|
-
var h=React,n=
|
|
433
|
+
var h=React,n=shimExports;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;
|
|
440
434
|
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]);
|
|
441
435
|
u(function(){f.hasValue=!0;f.value=d;},[d]);w(d);return d};
|
|
442
436
|
return withSelector_production_min;
|
|
@@ -472,7 +466,7 @@ function requireWithSelector_development () {
|
|
|
472
466
|
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
|
|
473
467
|
}
|
|
474
468
|
var React$1 = React;
|
|
475
|
-
var shim =
|
|
469
|
+
var shim = shimExports;
|
|
476
470
|
|
|
477
471
|
/**
|
|
478
472
|
* inlined Object.is polyfill to avoid requiring consumers ship their own
|
|
@@ -626,71 +620,75 @@ var withSelectorExports = withSelector.exports;
|
|
|
626
620
|
* To synchronize the editor instance with the component state,
|
|
627
621
|
* we need to create a separate instance that is not affected by the component re-renders.
|
|
628
622
|
*/
|
|
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
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
623
|
+
class EditorStateManager {
|
|
624
|
+
constructor(initialEditor) {
|
|
625
|
+
this.transactionNumber = 0;
|
|
626
|
+
this.lastTransactionNumber = 0;
|
|
627
|
+
this.subscribers = new Set();
|
|
628
|
+
this.editor = initialEditor;
|
|
629
|
+
this.lastSnapshot = { editor: initialEditor, transactionNumber: 0 };
|
|
630
|
+
this.getSnapshot = this.getSnapshot.bind(this);
|
|
631
|
+
this.getServerSnapshot = this.getServerSnapshot.bind(this);
|
|
632
|
+
this.watch = this.watch.bind(this);
|
|
633
|
+
this.subscribe = this.subscribe.bind(this);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Get the current editor instance.
|
|
637
|
+
*/
|
|
638
|
+
getSnapshot() {
|
|
639
|
+
if (this.transactionNumber === this.lastTransactionNumber) {
|
|
640
|
+
return this.lastSnapshot;
|
|
641
|
+
}
|
|
642
|
+
this.lastTransactionNumber = this.transactionNumber;
|
|
643
|
+
this.lastSnapshot = { editor: this.editor, transactionNumber: this.transactionNumber };
|
|
644
|
+
return this.lastSnapshot;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Always disable the editor on the server-side.
|
|
648
|
+
*/
|
|
649
|
+
getServerSnapshot() {
|
|
650
|
+
return { editor: null, transactionNumber: 0 };
|
|
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);
|
|
658
678
|
return () => {
|
|
659
|
-
|
|
679
|
+
currentEditor.off('transaction', fn);
|
|
660
680
|
};
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
|
|
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;
|
|
681
|
+
}
|
|
682
|
+
return undefined;
|
|
683
|
+
}
|
|
686
684
|
}
|
|
687
685
|
function useEditorState(options) {
|
|
688
|
-
const [editorInstance] = useState(() =>
|
|
686
|
+
const [editorInstance] = useState(() => new EditorStateManager(options.editor));
|
|
689
687
|
// Using the `useSyncExternalStore` hook to sync the editor instance with the component state
|
|
690
688
|
const selectedState = withSelectorExports.useSyncExternalStoreWithSelector(editorInstance.subscribe, editorInstance.getSnapshot, editorInstance.getServerSnapshot, options.selector, options.equalityFn);
|
|
691
689
|
useEffect(() => {
|
|
692
690
|
return editorInstance.watch(options.editor);
|
|
693
|
-
}, [options.editor]);
|
|
691
|
+
}, [options.editor, editorInstance]);
|
|
694
692
|
useDebugValue(selectedState);
|
|
695
693
|
return selectedState;
|
|
696
694
|
}
|
|
@@ -698,10 +696,51 @@ function useEditorState(options) {
|
|
|
698
696
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
699
697
|
const isSSR = typeof window === 'undefined';
|
|
700
698
|
const isNext = isSSR || Boolean(typeof window !== 'undefined' && window.next);
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
699
|
+
/**
|
|
700
|
+
* This class handles the creation, destruction, and re-creation of the editor instance.
|
|
701
|
+
*/
|
|
702
|
+
class EditorInstanceManager {
|
|
703
|
+
constructor(options) {
|
|
704
|
+
/**
|
|
705
|
+
* The current editor instance.
|
|
706
|
+
*/
|
|
707
|
+
this.editor = null;
|
|
708
|
+
/**
|
|
709
|
+
* The subscriptions to notify when the editor instance
|
|
710
|
+
* has been created or destroyed.
|
|
711
|
+
*/
|
|
712
|
+
this.subscriptions = new Set();
|
|
713
|
+
/**
|
|
714
|
+
* Whether the editor has been mounted.
|
|
715
|
+
*/
|
|
716
|
+
this.isComponentMounted = false;
|
|
717
|
+
/**
|
|
718
|
+
* The most recent dependencies array.
|
|
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) {
|
|
705
744
|
if (isSSR || isNext) {
|
|
706
745
|
// TODO in the next major release, we should throw an error here
|
|
707
746
|
if (isDev) {
|
|
@@ -715,137 +754,148 @@ function useEditor(options = {}, deps = []) {
|
|
|
715
754
|
return null;
|
|
716
755
|
}
|
|
717
756
|
// Default to immediately rendering when client-side rendering
|
|
718
|
-
return
|
|
757
|
+
return this.createEditor();
|
|
719
758
|
}
|
|
720
|
-
if (options.immediatelyRender && isSSR && isDev) {
|
|
759
|
+
if (this.options.current.immediatelyRender && isSSR && isDev) {
|
|
721
760
|
// Warn in development, to make sure the developer is aware that tiptap cannot be SSR'd, set `immediatelyRender` to `false` to avoid hydration mismatches.
|
|
722
761
|
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.');
|
|
723
762
|
}
|
|
724
|
-
if (options.immediatelyRender) {
|
|
725
|
-
return
|
|
763
|
+
if (this.options.current.immediatelyRender) {
|
|
764
|
+
return this.createEditor();
|
|
726
765
|
}
|
|
727
766
|
return null;
|
|
728
|
-
}
|
|
729
|
-
useDebugValue(editor);
|
|
730
|
-
// This effect will handle creating/updating the editor instance
|
|
731
|
-
useEffect(() => {
|
|
732
|
-
let editorInstance = editor;
|
|
733
|
-
if (!editorInstance) {
|
|
734
|
-
editorInstance = new Editor(options);
|
|
735
|
-
// instantiate the editor if it doesn't exist
|
|
736
|
-
// for ssr, this is the first time the editor is created
|
|
737
|
-
setEditor(editorInstance);
|
|
738
|
-
}
|
|
739
|
-
else if (Array.isArray(deps) && deps.length) {
|
|
740
|
-
// We need to destroy the editor instance and re-initialize it
|
|
741
|
-
// when the deps array changes
|
|
742
|
-
editorInstance.destroy();
|
|
743
|
-
// the deps array is used to re-initialize the editor instance
|
|
744
|
-
editorInstance = new Editor(options);
|
|
745
|
-
setEditor(editorInstance);
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
748
|
-
// if the editor does exist & deps are empty, we don't need to re-initialize the editor
|
|
749
|
-
// we can fast-path to update the editor options on the existing instance
|
|
750
|
-
editorInstance.setOptions(options);
|
|
751
|
-
}
|
|
752
|
-
}, deps);
|
|
753
|
-
const { onBeforeCreate, onBlur, onCreate, onDestroy, onFocus, onSelectionUpdate, onTransaction, onUpdate, onContentError, } = options;
|
|
754
|
-
const onBeforeCreateRef = useRef(onBeforeCreate);
|
|
755
|
-
const onBlurRef = useRef(onBlur);
|
|
756
|
-
const onCreateRef = useRef(onCreate);
|
|
757
|
-
const onDestroyRef = useRef(onDestroy);
|
|
758
|
-
const onFocusRef = useRef(onFocus);
|
|
759
|
-
const onSelectionUpdateRef = useRef(onSelectionUpdate);
|
|
760
|
-
const onTransactionRef = useRef(onTransaction);
|
|
761
|
-
const onUpdateRef = useRef(onUpdate);
|
|
762
|
-
const onContentErrorRef = useRef(onContentError);
|
|
763
|
-
// This effect will handle updating the editor instance
|
|
764
|
-
// when the event handlers change.
|
|
765
|
-
useEffect(() => {
|
|
766
|
-
if (!editor) {
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
|
-
if (onBeforeCreate) {
|
|
770
|
-
editor.off('beforeCreate', onBeforeCreateRef.current);
|
|
771
|
-
editor.on('beforeCreate', onBeforeCreate);
|
|
772
|
-
onBeforeCreateRef.current = onBeforeCreate;
|
|
773
|
-
}
|
|
774
|
-
if (onBlur) {
|
|
775
|
-
editor.off('blur', onBlurRef.current);
|
|
776
|
-
editor.on('blur', onBlur);
|
|
777
|
-
onBlurRef.current = onBlur;
|
|
778
|
-
}
|
|
779
|
-
if (onCreate) {
|
|
780
|
-
editor.off('create', onCreateRef.current);
|
|
781
|
-
editor.on('create', onCreate);
|
|
782
|
-
onCreateRef.current = onCreate;
|
|
783
|
-
}
|
|
784
|
-
if (onDestroy) {
|
|
785
|
-
editor.off('destroy', onDestroyRef.current);
|
|
786
|
-
editor.on('destroy', onDestroy);
|
|
787
|
-
onDestroyRef.current = onDestroy;
|
|
788
|
-
}
|
|
789
|
-
if (onFocus) {
|
|
790
|
-
editor.off('focus', onFocusRef.current);
|
|
791
|
-
editor.on('focus', onFocus);
|
|
792
|
-
onFocusRef.current = onFocus;
|
|
793
|
-
}
|
|
794
|
-
if (onSelectionUpdate) {
|
|
795
|
-
editor.off('selectionUpdate', onSelectionUpdateRef.current);
|
|
796
|
-
editor.on('selectionUpdate', onSelectionUpdate);
|
|
797
|
-
onSelectionUpdateRef.current = onSelectionUpdate;
|
|
798
|
-
}
|
|
799
|
-
if (onTransaction) {
|
|
800
|
-
editor.off('transaction', onTransactionRef.current);
|
|
801
|
-
editor.on('transaction', onTransaction);
|
|
802
|
-
onTransactionRef.current = onTransaction;
|
|
803
|
-
}
|
|
804
|
-
if (onUpdate) {
|
|
805
|
-
editor.off('update', onUpdateRef.current);
|
|
806
|
-
editor.on('update', onUpdate);
|
|
807
|
-
onUpdateRef.current = onUpdate;
|
|
808
|
-
}
|
|
809
|
-
if (onContentError) {
|
|
810
|
-
editor.off('contentError', onContentErrorRef.current);
|
|
811
|
-
editor.on('contentError', onContentError);
|
|
812
|
-
onContentErrorRef.current = onContentError;
|
|
813
|
-
}
|
|
814
|
-
}, [
|
|
815
|
-
onBeforeCreate,
|
|
816
|
-
onBlur,
|
|
817
|
-
onCreate,
|
|
818
|
-
onDestroy,
|
|
819
|
-
onFocus,
|
|
820
|
-
onSelectionUpdate,
|
|
821
|
-
onTransaction,
|
|
822
|
-
onUpdate,
|
|
823
|
-
onContentError,
|
|
824
|
-
editor,
|
|
825
|
-
]);
|
|
767
|
+
}
|
|
826
768
|
/**
|
|
827
|
-
*
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
769
|
+
* Create a new editor instance. And attach event listeners.
|
|
770
|
+
*/
|
|
771
|
+
createEditor() {
|
|
772
|
+
const editor = new Editor(this.options.current);
|
|
773
|
+
// Always call the most recent version of the callback function by default
|
|
774
|
+
editor.on('beforeCreate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
775
|
+
editor.on('blur', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
776
|
+
editor.on('create', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
777
|
+
editor.on('destroy', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
778
|
+
editor.on('focus', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
779
|
+
editor.on('selectionUpdate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
780
|
+
editor.on('transaction', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
781
|
+
editor.on('update', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
782
|
+
editor.on('contentError', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
783
|
+
// no need to keep track of the event listeners, they will be removed when the editor is destroyed
|
|
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
|
|
833
814
|
return () => {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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);
|
|
846
830
|
}
|
|
831
|
+
return () => {
|
|
832
|
+
this.isComponentMounted = false;
|
|
833
|
+
this.scheduleDestroy();
|
|
834
|
+
};
|
|
847
835
|
};
|
|
848
|
-
}
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Recreate the editor instance if the dependencies have changed.
|
|
839
|
+
*/
|
|
840
|
+
refreshEditorInstance(deps) {
|
|
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
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (this.editor && !this.editor.isDestroyed) {
|
|
856
|
+
// Destroy the editor instance if it exists
|
|
857
|
+
this.editor.destroy();
|
|
858
|
+
}
|
|
859
|
+
this.setEditor(this.createEditor());
|
|
860
|
+
// Update the lastDeps to the current deps
|
|
861
|
+
this.previousDeps = deps;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Schedule the destruction of the editor instance.
|
|
865
|
+
* This will only destroy the editor if it was not mounted on the next tick.
|
|
866
|
+
* This is to avoid destroying the editor instance when it's actually still mounted.
|
|
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));
|
|
849
899
|
// The default behavior is to re-render on each transaction
|
|
850
900
|
// This is legacy behavior that will be removed in future versions
|
|
851
901
|
useEditorState({
|