export class EditorUIManager {
+ public dropdowns: DropDownManager = new DropDownManager();
+
protected modalDefinitionsByKey: Record<string, EditorFormModalDefinition> = {};
protected activeModalsByKey: Record<string, EditorFormModal> = {};
protected decoratorConstructorsByType: Record<string, typeof EditorDecorator> = {};
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);
}
setToolbar(toolbar: EditorContainerUiElement) {
if (this.toolbar) {
- this.toolbar.getDOMElement().remove();
+ this.toolbar.teardown();
}
this.toolbar = toolbar;
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);
}
});
}
- 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