0.0.3: Exotic Type Gymnastics #3

Merged
Astrian merged 40 commits from dev into main 2025-05-22 01:17:51 +00:00
5 changed files with 70 additions and 46 deletions
Showing only changes of commit 4aed034100 - Show all commits

View File

@ -59,7 +59,7 @@ export default (options: ComponentOptions) => {
constructor() { constructor() {
super() super()
// initialize dom tree and append to shadow root // initialize dom tree and append to shadow root, as well as initialize state
this._initialize() this._initialize()
} }
@ -85,7 +85,14 @@ export default (options: ComponentOptions) => {
} }
} }
// trigger dom updates // 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]) if (this._statesListeners[keyPath])
this._statesListeners[keyPath](value) this._statesListeners[keyPath](value)
@ -160,45 +167,6 @@ export default (options: ComponentOptions) => {
}) })
} }
private _triggerDomUpdates(keyPath: string) {
if (this._stateToElementsMap[keyPath]) {
const updateQueue = new Set<HTMLElement>()
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<HTMLElement>) { private _scheduleUpdate(elements: Set<HTMLElement>) {
requestAnimationFrame(() => { requestAnimationFrame(() => {
@ -1025,4 +993,4 @@ export default (options: ComponentOptions) => {
} }
customElements.define(tag, CustomElementImpl) customElements.define(tag, CustomElementImpl)
} }

View File

@ -1,7 +1,9 @@
import { parseTemplate } from './parseTemplate' import parseTemplate from './parseTemplate'
import { processTemplateMacros } from './processTemplateMarco' import processTemplateMacros from './processTemplateMarco'
import triggerDomUpdates from './triggerDomUpdates'
export default { export default {
parseTemplate, parseTemplate,
processTemplateMacros, processTemplateMacros,
triggerDomUpdates,
} }

View File

@ -1,4 +1,4 @@
export function parseTemplate(template: string): Element { export default function parseTemplate(template: string): Element {
const parser = new DOMParser() const parser = new DOMParser()
const doc = parser.parseFromString(template, 'text/html') const doc = parser.parseFromString(template, 'text/html')

View File

@ -1,4 +1,4 @@
export function processTemplateMacros( export default function processTemplateMacros(
element: Element, element: Element,
context: CustomElement, context: CustomElement,
options: { options: {

View File

@ -0,0 +1,54 @@
export default function triggerDomUpdates(keyPath: string, ops: {
stateToElementsMap: Record<string, Set<HTMLElement>>,
scheduleUpdate: (elements: Set<HTMLElement>) => 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<HTMLElement>()
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))
}
}
}