@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.umd.js
CHANGED
|
@@ -125,17 +125,6 @@
|
|
|
125
125
|
});
|
|
126
126
|
const EditorContent = React.memo(EditorContentWithKey);
|
|
127
127
|
|
|
128
|
-
class Editor extends core.Editor {
|
|
129
|
-
constructor() {
|
|
130
|
-
super(...arguments);
|
|
131
|
-
this.contentComponent = null;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
var withSelector = {exports: {}};
|
|
136
|
-
|
|
137
|
-
var withSelector_production_min = {};
|
|
138
|
-
|
|
139
128
|
var shim = {exports: {}};
|
|
140
129
|
|
|
141
130
|
var useSyncExternalStoreShim_production_min = {};
|
|
@@ -406,20 +395,25 @@
|
|
|
406
395
|
return useSyncExternalStoreShim_development;
|
|
407
396
|
}
|
|
408
397
|
|
|
409
|
-
|
|
398
|
+
if (process.env.NODE_ENV === 'production') {
|
|
399
|
+
shim.exports = requireUseSyncExternalStoreShim_production_min();
|
|
400
|
+
} else {
|
|
401
|
+
shim.exports = requireUseSyncExternalStoreShim_development();
|
|
402
|
+
}
|
|
410
403
|
|
|
411
|
-
|
|
412
|
-
if (hasRequiredShim) return shim.exports;
|
|
413
|
-
hasRequiredShim = 1;
|
|
404
|
+
var shimExports = shim.exports;
|
|
414
405
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return shim.exports;
|
|
406
|
+
class Editor extends core.Editor {
|
|
407
|
+
constructor() {
|
|
408
|
+
super(...arguments);
|
|
409
|
+
this.contentComponent = null;
|
|
410
|
+
}
|
|
421
411
|
}
|
|
422
412
|
|
|
413
|
+
var withSelector = {exports: {}};
|
|
414
|
+
|
|
415
|
+
var withSelector_production_min = {};
|
|
416
|
+
|
|
423
417
|
/**
|
|
424
418
|
* @license React
|
|
425
419
|
* use-sync-external-store-shim/with-selector.production.min.js
|
|
@@ -435,7 +429,7 @@
|
|
|
435
429
|
function requireWithSelector_production_min () {
|
|
436
430
|
if (hasRequiredWithSelector_production_min) return withSelector_production_min;
|
|
437
431
|
hasRequiredWithSelector_production_min = 1;
|
|
438
|
-
var h=React,n=
|
|
432
|
+
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;
|
|
439
433
|
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]);
|
|
440
434
|
u(function(){f.hasValue=!0;f.value=d;},[d]);w(d);return d};
|
|
441
435
|
return withSelector_production_min;
|
|
@@ -471,7 +465,7 @@
|
|
|
471
465
|
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
|
|
472
466
|
}
|
|
473
467
|
var React$1 = React;
|
|
474
|
-
var shim =
|
|
468
|
+
var shim = shimExports;
|
|
475
469
|
|
|
476
470
|
/**
|
|
477
471
|
* inlined Object.is polyfill to avoid requiring consumers ship their own
|
|
@@ -625,71 +619,75 @@
|
|
|
625
619
|
* To synchronize the editor instance with the component state,
|
|
626
620
|
* we need to create a separate instance that is not affected by the component re-renders.
|
|
627
621
|
*/
|
|
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
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
622
|
+
class EditorStateManager {
|
|
623
|
+
constructor(initialEditor) {
|
|
624
|
+
this.transactionNumber = 0;
|
|
625
|
+
this.lastTransactionNumber = 0;
|
|
626
|
+
this.subscribers = new Set();
|
|
627
|
+
this.editor = initialEditor;
|
|
628
|
+
this.lastSnapshot = { editor: initialEditor, transactionNumber: 0 };
|
|
629
|
+
this.getSnapshot = this.getSnapshot.bind(this);
|
|
630
|
+
this.getServerSnapshot = this.getServerSnapshot.bind(this);
|
|
631
|
+
this.watch = this.watch.bind(this);
|
|
632
|
+
this.subscribe = this.subscribe.bind(this);
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Get the current editor instance.
|
|
636
|
+
*/
|
|
637
|
+
getSnapshot() {
|
|
638
|
+
if (this.transactionNumber === this.lastTransactionNumber) {
|
|
639
|
+
return this.lastSnapshot;
|
|
640
|
+
}
|
|
641
|
+
this.lastTransactionNumber = this.transactionNumber;
|
|
642
|
+
this.lastSnapshot = { editor: this.editor, transactionNumber: this.transactionNumber };
|
|
643
|
+
return this.lastSnapshot;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Always disable the editor on the server-side.
|
|
647
|
+
*/
|
|
648
|
+
getServerSnapshot() {
|
|
649
|
+
return { editor: null, transactionNumber: 0 };
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Subscribe to the editor instance's changes.
|
|
653
|
+
*/
|
|
654
|
+
subscribe(callback) {
|
|
655
|
+
this.subscribers.add(callback);
|
|
656
|
+
return () => {
|
|
657
|
+
this.subscribers.delete(callback);
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Watch the editor instance for changes.
|
|
662
|
+
*/
|
|
663
|
+
watch(nextEditor) {
|
|
664
|
+
this.editor = nextEditor;
|
|
665
|
+
if (this.editor) {
|
|
666
|
+
/**
|
|
667
|
+
* This will force a re-render when the editor state changes.
|
|
668
|
+
* This is to support things like `editor.can().toggleBold()` in components that `useEditor`.
|
|
669
|
+
* This could be more efficient, but it's a good trade-off for now.
|
|
670
|
+
*/
|
|
671
|
+
const fn = () => {
|
|
672
|
+
this.transactionNumber += 1;
|
|
673
|
+
this.subscribers.forEach(callback => callback());
|
|
674
|
+
};
|
|
675
|
+
const currentEditor = this.editor;
|
|
676
|
+
currentEditor.on('transaction', fn);
|
|
657
677
|
return () => {
|
|
658
|
-
|
|
678
|
+
currentEditor.off('transaction', fn);
|
|
659
679
|
};
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
*/
|
|
664
|
-
watch(nextEditor) {
|
|
665
|
-
editor = nextEditor;
|
|
666
|
-
if (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
|
-
transactionNumber += 1;
|
|
674
|
-
subscribers.forEach(callback => callback());
|
|
675
|
-
};
|
|
676
|
-
const currentEditor = editor;
|
|
677
|
-
currentEditor.on('transaction', fn);
|
|
678
|
-
return () => {
|
|
679
|
-
currentEditor.off('transaction', fn);
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
},
|
|
683
|
-
};
|
|
684
|
-
return editorInstance;
|
|
680
|
+
}
|
|
681
|
+
return undefined;
|
|
682
|
+
}
|
|
685
683
|
}
|
|
686
684
|
function useEditorState(options) {
|
|
687
|
-
const [editorInstance] = React.useState(() =>
|
|
685
|
+
const [editorInstance] = React.useState(() => new EditorStateManager(options.editor));
|
|
688
686
|
// Using the `useSyncExternalStore` hook to sync the editor instance with the component state
|
|
689
687
|
const selectedState = withSelectorExports.useSyncExternalStoreWithSelector(editorInstance.subscribe, editorInstance.getSnapshot, editorInstance.getServerSnapshot, options.selector, options.equalityFn);
|
|
690
688
|
React.useEffect(() => {
|
|
691
689
|
return editorInstance.watch(options.editor);
|
|
692
|
-
}, [options.editor]);
|
|
690
|
+
}, [options.editor, editorInstance]);
|
|
693
691
|
React.useDebugValue(selectedState);
|
|
694
692
|
return selectedState;
|
|
695
693
|
}
|
|
@@ -697,10 +695,51 @@
|
|
|
697
695
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
698
696
|
const isSSR = typeof window === 'undefined';
|
|
699
697
|
const isNext = isSSR || Boolean(typeof window !== 'undefined' && window.next);
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
698
|
+
/**
|
|
699
|
+
* This class handles the creation, destruction, and re-creation of the editor instance.
|
|
700
|
+
*/
|
|
701
|
+
class EditorInstanceManager {
|
|
702
|
+
constructor(options) {
|
|
703
|
+
/**
|
|
704
|
+
* The current editor instance.
|
|
705
|
+
*/
|
|
706
|
+
this.editor = null;
|
|
707
|
+
/**
|
|
708
|
+
* The subscriptions to notify when the editor instance
|
|
709
|
+
* has been created or destroyed.
|
|
710
|
+
*/
|
|
711
|
+
this.subscriptions = new Set();
|
|
712
|
+
/**
|
|
713
|
+
* Whether the editor has been mounted.
|
|
714
|
+
*/
|
|
715
|
+
this.isComponentMounted = false;
|
|
716
|
+
/**
|
|
717
|
+
* The most recent dependencies array.
|
|
718
|
+
*/
|
|
719
|
+
this.previousDeps = null;
|
|
720
|
+
/**
|
|
721
|
+
* The unique instance ID. This is used to identify the editor instance. And will be re-generated for each new instance.
|
|
722
|
+
*/
|
|
723
|
+
this.instanceId = '';
|
|
724
|
+
this.options = options;
|
|
725
|
+
this.subscriptions = new Set();
|
|
726
|
+
this.setEditor(this.getInitialEditor());
|
|
727
|
+
this.getEditor = this.getEditor.bind(this);
|
|
728
|
+
this.getServerSnapshot = this.getServerSnapshot.bind(this);
|
|
729
|
+
this.subscribe = this.subscribe.bind(this);
|
|
730
|
+
this.refreshEditorInstance = this.refreshEditorInstance.bind(this);
|
|
731
|
+
this.scheduleDestroy = this.scheduleDestroy.bind(this);
|
|
732
|
+
this.onRender = this.onRender.bind(this);
|
|
733
|
+
this.createEditor = this.createEditor.bind(this);
|
|
734
|
+
}
|
|
735
|
+
setEditor(editor) {
|
|
736
|
+
this.editor = editor;
|
|
737
|
+
this.instanceId = Math.random().toString(36).slice(2, 9);
|
|
738
|
+
// Notify all subscribers that the editor instance has been created
|
|
739
|
+
this.subscriptions.forEach(cb => cb());
|
|
740
|
+
}
|
|
741
|
+
getInitialEditor() {
|
|
742
|
+
if (this.options.current.immediatelyRender === undefined) {
|
|
704
743
|
if (isSSR || isNext) {
|
|
705
744
|
// TODO in the next major release, we should throw an error here
|
|
706
745
|
if (isDev) {
|
|
@@ -714,137 +753,148 @@
|
|
|
714
753
|
return null;
|
|
715
754
|
}
|
|
716
755
|
// Default to immediately rendering when client-side rendering
|
|
717
|
-
return
|
|
756
|
+
return this.createEditor();
|
|
718
757
|
}
|
|
719
|
-
if (options.immediatelyRender && isSSR && isDev) {
|
|
758
|
+
if (this.options.current.immediatelyRender && isSSR && isDev) {
|
|
720
759
|
// Warn in development, to make sure the developer is aware that tiptap cannot be SSR'd, set `immediatelyRender` to `false` to avoid hydration mismatches.
|
|
721
760
|
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.');
|
|
722
761
|
}
|
|
723
|
-
if (options.immediatelyRender) {
|
|
724
|
-
return
|
|
762
|
+
if (this.options.current.immediatelyRender) {
|
|
763
|
+
return this.createEditor();
|
|
725
764
|
}
|
|
726
765
|
return null;
|
|
727
|
-
}
|
|
728
|
-
React.useDebugValue(editor);
|
|
729
|
-
// This effect will handle creating/updating the editor instance
|
|
730
|
-
React.useEffect(() => {
|
|
731
|
-
let editorInstance = editor;
|
|
732
|
-
if (!editorInstance) {
|
|
733
|
-
editorInstance = new Editor(options);
|
|
734
|
-
// instantiate the editor if it doesn't exist
|
|
735
|
-
// for ssr, this is the first time the editor is created
|
|
736
|
-
setEditor(editorInstance);
|
|
737
|
-
}
|
|
738
|
-
else if (Array.isArray(deps) && deps.length) {
|
|
739
|
-
// We need to destroy the editor instance and re-initialize it
|
|
740
|
-
// when the deps array changes
|
|
741
|
-
editorInstance.destroy();
|
|
742
|
-
// the deps array is used to re-initialize the editor instance
|
|
743
|
-
editorInstance = new Editor(options);
|
|
744
|
-
setEditor(editorInstance);
|
|
745
|
-
}
|
|
746
|
-
else {
|
|
747
|
-
// if the editor does exist & deps are empty, we don't need to re-initialize the editor
|
|
748
|
-
// we can fast-path to update the editor options on the existing instance
|
|
749
|
-
editorInstance.setOptions(options);
|
|
750
|
-
}
|
|
751
|
-
}, deps);
|
|
752
|
-
const { onBeforeCreate, onBlur, onCreate, onDestroy, onFocus, onSelectionUpdate, onTransaction, onUpdate, onContentError, } = options;
|
|
753
|
-
const onBeforeCreateRef = React.useRef(onBeforeCreate);
|
|
754
|
-
const onBlurRef = React.useRef(onBlur);
|
|
755
|
-
const onCreateRef = React.useRef(onCreate);
|
|
756
|
-
const onDestroyRef = React.useRef(onDestroy);
|
|
757
|
-
const onFocusRef = React.useRef(onFocus);
|
|
758
|
-
const onSelectionUpdateRef = React.useRef(onSelectionUpdate);
|
|
759
|
-
const onTransactionRef = React.useRef(onTransaction);
|
|
760
|
-
const onUpdateRef = React.useRef(onUpdate);
|
|
761
|
-
const onContentErrorRef = React.useRef(onContentError);
|
|
762
|
-
// This effect will handle updating the editor instance
|
|
763
|
-
// when the event handlers change.
|
|
764
|
-
React.useEffect(() => {
|
|
765
|
-
if (!editor) {
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
if (onBeforeCreate) {
|
|
769
|
-
editor.off('beforeCreate', onBeforeCreateRef.current);
|
|
770
|
-
editor.on('beforeCreate', onBeforeCreate);
|
|
771
|
-
onBeforeCreateRef.current = onBeforeCreate;
|
|
772
|
-
}
|
|
773
|
-
if (onBlur) {
|
|
774
|
-
editor.off('blur', onBlurRef.current);
|
|
775
|
-
editor.on('blur', onBlur);
|
|
776
|
-
onBlurRef.current = onBlur;
|
|
777
|
-
}
|
|
778
|
-
if (onCreate) {
|
|
779
|
-
editor.off('create', onCreateRef.current);
|
|
780
|
-
editor.on('create', onCreate);
|
|
781
|
-
onCreateRef.current = onCreate;
|
|
782
|
-
}
|
|
783
|
-
if (onDestroy) {
|
|
784
|
-
editor.off('destroy', onDestroyRef.current);
|
|
785
|
-
editor.on('destroy', onDestroy);
|
|
786
|
-
onDestroyRef.current = onDestroy;
|
|
787
|
-
}
|
|
788
|
-
if (onFocus) {
|
|
789
|
-
editor.off('focus', onFocusRef.current);
|
|
790
|
-
editor.on('focus', onFocus);
|
|
791
|
-
onFocusRef.current = onFocus;
|
|
792
|
-
}
|
|
793
|
-
if (onSelectionUpdate) {
|
|
794
|
-
editor.off('selectionUpdate', onSelectionUpdateRef.current);
|
|
795
|
-
editor.on('selectionUpdate', onSelectionUpdate);
|
|
796
|
-
onSelectionUpdateRef.current = onSelectionUpdate;
|
|
797
|
-
}
|
|
798
|
-
if (onTransaction) {
|
|
799
|
-
editor.off('transaction', onTransactionRef.current);
|
|
800
|
-
editor.on('transaction', onTransaction);
|
|
801
|
-
onTransactionRef.current = onTransaction;
|
|
802
|
-
}
|
|
803
|
-
if (onUpdate) {
|
|
804
|
-
editor.off('update', onUpdateRef.current);
|
|
805
|
-
editor.on('update', onUpdate);
|
|
806
|
-
onUpdateRef.current = onUpdate;
|
|
807
|
-
}
|
|
808
|
-
if (onContentError) {
|
|
809
|
-
editor.off('contentError', onContentErrorRef.current);
|
|
810
|
-
editor.on('contentError', onContentError);
|
|
811
|
-
onContentErrorRef.current = onContentError;
|
|
812
|
-
}
|
|
813
|
-
}, [
|
|
814
|
-
onBeforeCreate,
|
|
815
|
-
onBlur,
|
|
816
|
-
onCreate,
|
|
817
|
-
onDestroy,
|
|
818
|
-
onFocus,
|
|
819
|
-
onSelectionUpdate,
|
|
820
|
-
onTransaction,
|
|
821
|
-
onUpdate,
|
|
822
|
-
onContentError,
|
|
823
|
-
editor,
|
|
824
|
-
]);
|
|
766
|
+
}
|
|
825
767
|
/**
|
|
826
|
-
*
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
768
|
+
* Create a new editor instance. And attach event listeners.
|
|
769
|
+
*/
|
|
770
|
+
createEditor() {
|
|
771
|
+
const editor = new Editor(this.options.current);
|
|
772
|
+
// Always call the most recent version of the callback function by default
|
|
773
|
+
editor.on('beforeCreate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
774
|
+
editor.on('blur', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
775
|
+
editor.on('create', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
776
|
+
editor.on('destroy', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
777
|
+
editor.on('focus', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
778
|
+
editor.on('selectionUpdate', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
779
|
+
editor.on('transaction', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
780
|
+
editor.on('update', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
781
|
+
editor.on('contentError', (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); });
|
|
782
|
+
// no need to keep track of the event listeners, they will be removed when the editor is destroyed
|
|
783
|
+
return editor;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Get the current editor instance.
|
|
787
|
+
*/
|
|
788
|
+
getEditor() {
|
|
789
|
+
return this.editor;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Always disable the editor on the server-side.
|
|
793
|
+
*/
|
|
794
|
+
getServerSnapshot() {
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Subscribe to the editor instance's changes.
|
|
799
|
+
*/
|
|
800
|
+
subscribe(onStoreChange) {
|
|
801
|
+
this.subscriptions.add(onStoreChange);
|
|
802
|
+
return () => {
|
|
803
|
+
this.subscriptions.delete(onStoreChange);
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* On each render, we will create, update, or destroy the editor instance.
|
|
808
|
+
* @param deps The dependencies to watch for changes
|
|
809
|
+
* @returns A cleanup function
|
|
810
|
+
*/
|
|
811
|
+
onRender(deps) {
|
|
812
|
+
// The returned callback will run on each render
|
|
832
813
|
return () => {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
814
|
+
this.isComponentMounted = true;
|
|
815
|
+
// Cleanup any scheduled destructions, since we are currently rendering
|
|
816
|
+
clearTimeout(this.scheduledDestructionTimeout);
|
|
817
|
+
if (this.editor && !this.editor.isDestroyed && deps.length === 0) {
|
|
818
|
+
// if the editor does exist & deps are empty, we don't need to re-initialize the editor
|
|
819
|
+
// we can fast-path to update the editor options on the existing instance
|
|
820
|
+
this.editor.setOptions(this.options.current);
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
// When the editor:
|
|
824
|
+
// - does not yet exist
|
|
825
|
+
// - is destroyed
|
|
826
|
+
// - the deps array changes
|
|
827
|
+
// We need to destroy the editor instance and re-initialize it
|
|
828
|
+
this.refreshEditorInstance(deps);
|
|
845
829
|
}
|
|
830
|
+
return () => {
|
|
831
|
+
this.isComponentMounted = false;
|
|
832
|
+
this.scheduleDestroy();
|
|
833
|
+
};
|
|
846
834
|
};
|
|
847
|
-
}
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Recreate the editor instance if the dependencies have changed.
|
|
838
|
+
*/
|
|
839
|
+
refreshEditorInstance(deps) {
|
|
840
|
+
if (this.editor && !this.editor.isDestroyed) {
|
|
841
|
+
// Editor instance already exists
|
|
842
|
+
if (this.previousDeps === null) {
|
|
843
|
+
// If lastDeps has not yet been initialized, reuse the current editor instance
|
|
844
|
+
this.previousDeps = deps;
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const depsAreEqual = this.previousDeps.length === deps.length
|
|
848
|
+
&& this.previousDeps.every((dep, index) => dep === deps[index]);
|
|
849
|
+
if (depsAreEqual) {
|
|
850
|
+
// deps exist and are equal, no need to recreate
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (this.editor && !this.editor.isDestroyed) {
|
|
855
|
+
// Destroy the editor instance if it exists
|
|
856
|
+
this.editor.destroy();
|
|
857
|
+
}
|
|
858
|
+
this.setEditor(this.createEditor());
|
|
859
|
+
// Update the lastDeps to the current deps
|
|
860
|
+
this.previousDeps = deps;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Schedule the destruction of the editor instance.
|
|
864
|
+
* This will only destroy the editor if it was not mounted on the next tick.
|
|
865
|
+
* This is to avoid destroying the editor instance when it's actually still mounted.
|
|
866
|
+
*/
|
|
867
|
+
scheduleDestroy() {
|
|
868
|
+
const currentInstanceId = this.instanceId;
|
|
869
|
+
const currentEditor = this.editor;
|
|
870
|
+
// Wait a tick to see if the component is still mounted
|
|
871
|
+
this.scheduledDestructionTimeout = setTimeout(() => {
|
|
872
|
+
if (this.isComponentMounted && this.instanceId === currentInstanceId) {
|
|
873
|
+
// If still mounted on the next tick, with the same instanceId, do not destroy the editor
|
|
874
|
+
if (currentEditor) {
|
|
875
|
+
// just re-apply options as they might have changed
|
|
876
|
+
currentEditor.setOptions(this.options.current);
|
|
877
|
+
}
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (currentEditor && !currentEditor.isDestroyed) {
|
|
881
|
+
currentEditor.destroy();
|
|
882
|
+
if (this.instanceId === currentInstanceId) {
|
|
883
|
+
this.setEditor(null);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}, 0);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function useEditor(options = {}, deps = []) {
|
|
890
|
+
const mostRecentOptions = React.useRef(options);
|
|
891
|
+
mostRecentOptions.current = options;
|
|
892
|
+
const [instanceManager] = React.useState(() => new EditorInstanceManager(mostRecentOptions));
|
|
893
|
+
const editor = shimExports.useSyncExternalStore(instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot);
|
|
894
|
+
React.useDebugValue(editor);
|
|
895
|
+
// This effect will handle creating/updating the editor instance
|
|
896
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
897
|
+
React.useEffect(instanceManager.onRender(deps));
|
|
848
898
|
// The default behavior is to re-render on each transaction
|
|
849
899
|
// This is legacy behavior that will be removed in future versions
|
|
850
900
|
useEditorState({
|