import { ComponentHelper } from "./ComponentHelper.js"; import { IEditableAttribute, IEditableElement } from "./EditableAttributes.js"; import EventRegistry from "./EventRegistry.js"; export default abstract class TemplateControl extends HTMLElement implements IEditableElement { observer: MutationObserver; events: EventRegistry = new EventRegistry(); get editableAttributes(): IEditableAttribute[] { return []; } onMutation = (records: MutationRecord[]) => { for (const { addedNodes, removedNodes } of records) { for (let index = 0; index < removedNodes.length; index++) { const element = removedNodes[index]; this.onChildRemoved(element)?.catch(console.error); } for (let index = 0; index < addedNodes.length; index++) { const element = addedNodes[index]; this.onChildAdded(element)?.catch(console.error); } } }; /** * This will be called only first time */ async onFirstPrepare() { } connectedCallback() { this.onConnected().then(() => { this.observer = new MutationObserver(this.onMutation); this.observer.observe(this, { childList: true }); this.onAfterConnected().catch(console.error); }); } disconnectedCallback() { this.events[Symbol.dispose](); this.events = new EventRegistry(); this.observer.disconnect(); } /** * This will be called every time */ async onAfterConnected() { } onConnected() { const ready = (async () => { await ComponentHelper.waitForReady(); const children = Array.from(this.children); await this.prepare(); for (let index = 0; index < children.length; index++) { const element = children[index]; this.onChildAdded(element); } await this.onFirstPrepare(); })(); Object.defineProperty(this, "onConnected", { value: () => ready}) return ready; } /** * This will be called only first time */ abstract prepare(): Promise; abstract onChildAdded(child: Node): any; abstract onChildRemoved(child: Node): any; protected bindEvent(target: HTMLElement, name, eh: (e: Event) => void, extra?) { target.addEventListener(name, eh, extra); return { [Symbol.dispose]() { target.removeEventListener(name, eh, extra); } } } }