]> BookStack Code Mirror - bookstack/blobdiff - resources/js/wysiwyg/ui/framework/manager.ts
Comments: Switched to lexical editor
[bookstack] / resources / js / wysiwyg / ui / framework / manager.ts
index c40206607c62585da1140557a72104d388314752..3f46455da630e7618d2c5e63298d8f5508459b6b 100644 (file)
@@ -12,6 +12,8 @@ export type SelectionChangeHandler = (selection: BaseSelection|null) => void;
 
 export class EditorUIManager {
 
+    public dropdowns: DropDownManager = new DropDownManager();
+
     protected modalDefinitionsByKey: Record<string, EditorFormModalDefinition> = {};
     protected activeModalsByKey: Record<string, EditorFormModal> = {};
     protected decoratorConstructorsByType: Record<string, typeof EditorDecorator> = {};
@@ -21,12 +23,12 @@ export class EditorUIManager {
     protected contextToolbarDefinitionsByKey: Record<string, EditorContextToolbarDefinition> = {};
     protected activeContextToolbars: EditorContextToolbar[] = [];
     protected selectionChangeHandlers: Set<SelectionChangeHandler> = new Set();
-
-    public dropdowns: DropDownManager = new DropDownManager();
+    protected domEventAbortController = new AbortController();
+    protected teardownCallbacks: (()=>void)[] = [];
 
     setContext(context: EditorUiContext) {
         this.context = context;
-        this.setupEventListeners(context);
+        this.setupEventListeners();
         this.setupEditor(context.editor);
     }
 
@@ -99,7 +101,7 @@ export class EditorUIManager {
 
     setToolbar(toolbar: EditorContainerUiElement) {
         if (this.toolbar) {
-            this.toolbar.getDOMElement().remove();
+            this.toolbar.teardown();
         }
 
         this.toolbar = toolbar;
@@ -170,10 +172,40 @@ export class EditorUIManager {
         return this.getContext().options.textDirection === 'rtl' ? 'rtl' : 'ltr';
     }
 
+    onTeardown(callback: () => void): void {
+        this.teardownCallbacks.push(callback);
+    }
+
+    teardown(): void {
+        this.domEventAbortController.abort('teardown');
+
+        for (const [_, modal] of Object.entries(this.activeModalsByKey)) {
+            modal.teardown();
+        }
+
+        for (const [_, decorator] of Object.entries(this.decoratorInstancesByNodeKey)) {
+            decorator.teardown();
+        }
+
+        if (this.toolbar) {
+            this.toolbar.teardown();
+        }
+
+        for (const toolbar of this.activeContextToolbars) {
+            toolbar.teardown();
+        }
+
+        this.dropdowns.teardown();
+
+        for (const callback of this.teardownCallbacks) {
+            callback();
+        }
+    }
+
     protected updateContextToolbars(update: EditorUiStateUpdate): void {
         for (let i = this.activeContextToolbars.length - 1; i >= 0; i--) {
             const toolbar = this.activeContextToolbars[i];
-            toolbar.destroy();
+            toolbar.teardown();
             this.activeContextToolbars.splice(i, 1);
         }
 
@@ -253,9 +285,9 @@ export class EditorUIManager {
         });
     }
 
-    protected setupEventListeners(context: EditorUiContext) {
+    protected setupEventListeners() {
         const layoutUpdate = this.triggerLayoutUpdate.bind(this);
-        window.addEventListener('scroll', layoutUpdate, {capture: true, passive: true});
-        window.addEventListener('resize', layoutUpdate, {passive: true});
+        window.addEventListener('scroll', layoutUpdate, {capture: true, passive: true, signal: this.domEventAbortController.signal});
+        window.addEventListener('resize', layoutUpdate, {passive: true, signal: this.domEventAbortController.signal});
     }
 }
\ No newline at end of file