From 4aed0341007b898ac63d59931cb050a0f0f37448 Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Wed, 21 May 2025 14:24:11 +1000 Subject: [PATCH] feat: add triggerDomUpdates utility and refactor DOM update logic in main component --- src/main.ts | 52 ++++++----------------------- src/utils/index.ts | 6 ++-- src/utils/parseTemplate.ts | 2 +- src/utils/processTemplateMarco.ts | 2 +- src/utils/triggerDomUpdates.ts | 54 +++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 46 deletions(-) create mode 100644 src/utils/triggerDomUpdates.ts diff --git a/src/main.ts b/src/main.ts index 808ef7b..9fba2e6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -59,7 +59,7 @@ export default (options: ComponentOptions) => { constructor() { super() - // initialize dom tree and append to shadow root + // initialize dom tree and append to shadow root, as well as initialize state this._initialize() } @@ -85,7 +85,14 @@ export default (options: ComponentOptions) => { } } // trigger dom updates - this._triggerDomUpdates(keyPath) + utils.triggerDomUpdates(keyPath, { + stateToElementsMap: this._stateToElementsMap, + textBindings: this._textBindings, + attributeBindings: this._attributeBindings, + updateTextNode: this._updateTextNode.bind(this), + getNestedState: this._getNestedState.bind(this), + scheduleUpdate: this._scheduleUpdate.bind(this), + }) if (this._statesListeners[keyPath]) this._statesListeners[keyPath](value) @@ -160,45 +167,6 @@ export default (options: ComponentOptions) => { }) } - private _triggerDomUpdates(keyPath: string) { - if (this._stateToElementsMap[keyPath]) { - const updateQueue = new Set() - - for (const element of this._stateToElementsMap[keyPath]) { - updateQueue.add(element) - } - - this._scheduleUpdate(updateQueue) - } - - // Update text bindings that depend on this state - if (this._textBindings) { - // this._textBindings.forEach((binding) => { - for (const binding of this._textBindings) - if ( - binding.expr === keyPath || - binding.expr.startsWith(`${keyPath}.`) - ) - this._updateTextNode( - binding.node, - binding.expr, - binding.originalContent, - ) - } - - // Update attribute bindings that depend on this state - if (this._attributeBindings) { - for (const binding of this._attributeBindings) - if ( - binding.expr === keyPath || - binding.expr.startsWith(`${keyPath}.`) - ) { - const value = this._getNestedState(binding.expr) - if (value !== undefined) - binding.element.setAttribute(binding.attrName, String(value)) - } - } - } private _scheduleUpdate(elements: Set) { requestAnimationFrame(() => { @@ -1025,4 +993,4 @@ export default (options: ComponentOptions) => { } customElements.define(tag, CustomElementImpl) -} +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index bc56952..46c846c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,7 +1,9 @@ -import { parseTemplate } from './parseTemplate' -import { processTemplateMacros } from './processTemplateMarco' +import parseTemplate from './parseTemplate' +import processTemplateMacros from './processTemplateMarco' +import triggerDomUpdates from './triggerDomUpdates' export default { parseTemplate, processTemplateMacros, + triggerDomUpdates, } diff --git a/src/utils/parseTemplate.ts b/src/utils/parseTemplate.ts index 0da7669..b946b57 100644 --- a/src/utils/parseTemplate.ts +++ b/src/utils/parseTemplate.ts @@ -1,4 +1,4 @@ -export function parseTemplate(template: string): Element { +export default function parseTemplate(template: string): Element { const parser = new DOMParser() const doc = parser.parseFromString(template, 'text/html') diff --git a/src/utils/processTemplateMarco.ts b/src/utils/processTemplateMarco.ts index ca9f1b2..55d417e 100644 --- a/src/utils/processTemplateMarco.ts +++ b/src/utils/processTemplateMarco.ts @@ -1,4 +1,4 @@ -export function processTemplateMacros( +export default function processTemplateMacros( element: Element, context: CustomElement, options: { diff --git a/src/utils/triggerDomUpdates.ts b/src/utils/triggerDomUpdates.ts new file mode 100644 index 0000000..565a5c3 --- /dev/null +++ b/src/utils/triggerDomUpdates.ts @@ -0,0 +1,54 @@ +export default function triggerDomUpdates(keyPath: string, ops: { + stateToElementsMap: Record>, + scheduleUpdate: (elements: Set) => void, + textBindings: Array<{ + node: Text + expr: string + originalContent: string + }> | undefined, + attributeBindings: Array<{ + element: Element + attrName: string + expr: string + template: string + }> | undefined, + updateTextNode: (node: Text, expr: string, template: string) => void, + getNestedState: (path: string) => unknown, +}) { + if (ops.stateToElementsMap[keyPath]) { + const updateQueue = new Set() + + for (const element of ops.stateToElementsMap[keyPath]) + updateQueue.add(element) + + ops.scheduleUpdate(updateQueue) + } + + // Update text bindings that depend on this state + if (ops.textBindings) { + // this._textBindings.forEach((binding) => { + for (const binding of ops.textBindings) + if ( + binding.expr === keyPath || + binding.expr.startsWith(`${keyPath}.`) + ) + ops.updateTextNode( + binding.node, + binding.expr, + binding.originalContent, + ) + } + + // Update attribute bindings that depend on this state + if (ops.attributeBindings) { + for (const binding of ops.attributeBindings) + if ( + binding.expr === keyPath || + binding.expr.startsWith(`${keyPath}.`) + ) { + const value = ops.getNestedState(binding.expr) + if (value !== undefined) + binding.element.setAttribute(binding.attrName, String(value)) + } + } +} \ No newline at end of file