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