refactor: split initState function into indipendent file
Some checks failed
Quality Check & Publish / quality (push) Failing after 19s
Quality Check & Publish / quality-failed-webhook (push) Has been skipped
Quality Check & Publish / publish (push) Has been skipped
Quality Check & Publish / publish-failed-webhook (push) Has been skipped

This commit is contained in:
Astrian Zheng 2025-05-21 21:27:11 +10:00
parent 2c04be8607
commit 0a971f16d9
Signed by: Astrian
SSH Key Fingerprint: SHA256:rVnhx3DAKjujCwWE13aDl7uV6+9U1MvydLkNRXJrBiA
3 changed files with 119 additions and 72 deletions

View File

@ -47,80 +47,22 @@ export default (options: ComponentOptions) => {
this._initialize()
}
private _initState() {
// copy state from options
this._states = new Proxy(
{ ...(states || {}) },
{
set: (
target: Record<string, unknown>,
keyPath: string,
value: unknown,
) => {
const valueRoute = keyPath.split('.')
let currentTarget = target
for (const i in valueRoute) {
const key = valueRoute[i]
if (Number.parseInt(i) === valueRoute.length - 1) {
currentTarget[key] = value
} else {
if (!currentTarget[key]) currentTarget[key] = {}
currentTarget = currentTarget[key] as Record<string, unknown>
}
}
// trigger dom updates
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)
// trigger %if macros
if (this._conditionalElements.size > 0)
this._conditionalElements.forEach((info, element) => {
if (info.expr.includes(keyPath))
this._evaluateIfCondition(element, info.expr)
})
// trigger state update events
statesListeners?.[keyPath]?.(value)
return true
},
get: (target: Record<string, unknown>, keyPath: string) => {
// collect state dependencies
if (this._currentRenderingElement) {
if (!this._stateToElementsMap[keyPath])
this._stateToElementsMap[keyPath] = new Set()
this._stateToElementsMap[keyPath].add(
this._currentRenderingElement,
)
}
const valueRoute = keyPath.split('.')
let currentTarget = target
for (const i in valueRoute) {
const key = valueRoute[i]
if (Number.parseInt(i) === valueRoute.length - 1)
return currentTarget[key]
if (!currentTarget[key]) currentTarget[key] = {}
currentTarget = currentTarget[key] as Record<string, unknown>
}
return undefined
},
},
)
}
private _initialize() {
// initialize state
this._initState()
this._states = utils.initState(
{
stateToElementsMap: this._stateToElementsMap,
textBindings: this._textBindings,
attributeBindings: this._attributeBindings,
updateTextNode: (node: Text, value: string) => this._updateTextNode(node, value, value),
getNestedState: (keyPath: string) => this._getNestedState(keyPath),
scheduleUpdate: this._scheduleUpdate.bind(this),
statesListeners: this._statesListeners,
conditionalElements: this._conditionalElements,
evaluateIfCondition: this._evaluateIfCondition.bind(this),
},
options.states,
)
// initialize shadow dom
const shadow = this.attachShadow({ mode: 'open' })

View File

@ -2,10 +2,12 @@ import parseTemplate from './parseTemplate'
import processTemplateMacros from './processTemplateMarcos'
import setupArrowFunctionHandler from './setupArrowFunctionHandler'
import triggerDomUpdates from './triggerDomUpdates'
import initState from './initState'
export default {
parseTemplate,
processTemplateMacros,
setupArrowFunctionHandler,
triggerDomUpdates,
initState,
}

103
src/utils/initState.ts Normal file
View File

@ -0,0 +1,103 @@
import triggerDomUpdates from "./triggerDomUpdates"
export default function initState(
ops: {
stateToElementsMap: Record<string, Set<HTMLElement>>
textBindings: Array<{
node: Text
expr: string
originalContent: string
}>
attributeBindings: Array<{
element: Element
attrName: string
expr: string
template: string
}>
updateTextNode: (node: Text, value: string) => void
getNestedState: (keyPath: string) => unknown
scheduleUpdate: (elements: Set<HTMLElement>) => void
statesListeners: Record<string, (value: unknown) => void>
conditionalElements: Map<
Element,
{
expr: string
placeholder: Comment
isPresent: boolean
}
>
evaluateIfCondition: (element: Element, expr: string) => void
currentRenderingElement?: HTMLElement
},
states?: Record<string, unknown>,
) {
console.log(states)
// copy state from options
return new Proxy(
{ ...(states || {}) },
{
set: (
target: Record<string, unknown>,
keyPath: string,
value: unknown,
) => {
const valueRoute = keyPath.split('.')
let currentTarget = target
for (const i in valueRoute) {
const key = valueRoute[i]
if (Number.parseInt(i) === valueRoute.length - 1) {
currentTarget[key] = value
} else {
if (!currentTarget[key]) currentTarget[key] = {}
currentTarget = currentTarget[key] as Record<string, unknown>
}
}
// trigger dom updates
triggerDomUpdates(keyPath, {
stateToElementsMap: ops.stateToElementsMap,
textBindings: ops.textBindings,
attributeBindings: ops.attributeBindings,
updateTextNode: ops.updateTextNode,
getNestedState: ops.getNestedState,
scheduleUpdate: ops.scheduleUpdate,
})
if (ops.statesListeners[keyPath])
ops.statesListeners[keyPath](value)
// trigger %if macros
if (ops.conditionalElements.size > 0)
ops.conditionalElements.forEach((info, element) => {
if (info.expr.includes(keyPath))
ops.evaluateIfCondition(element, info.expr)
})
// trigger state update events
ops.statesListeners?.[keyPath]?.(value)
return true
},
get: (target: Record<string, unknown>, keyPath: string) => {
// collect state dependencies
if (ops.currentRenderingElement) {
if (!ops.stateToElementsMap[keyPath])
ops.stateToElementsMap[keyPath] = new Set()
ops.stateToElementsMap[keyPath].add(
ops.currentRenderingElement,
)
}
const valueRoute = keyPath.split('.')
let currentTarget = target
for (const i in valueRoute) {
const key = valueRoute[i]
if (Number.parseInt(i) === valueRoute.length - 1)
return currentTarget[key]
if (!currentTarget[key]) currentTarget[key] = {}
currentTarget = currentTarget[key] as Record<string, unknown>
}
return undefined
},
},
)
}