diff --git a/src/main.ts b/src/main.ts index 9a7f186..bb5db2c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -47,80 +47,22 @@ export default (options: ComponentOptions) => { this._initialize() } - private _initState() { - // copy state from options - this._states = new Proxy( - { ...(states || {}) }, - { - set: ( - target: Record, - 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 - } - } - // 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, 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 - } - 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' }) diff --git a/src/utils/index.ts b/src/utils/index.ts index 04e0aef..a2fb583 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -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, } diff --git a/src/utils/initState.ts b/src/utils/initState.ts new file mode 100644 index 0000000..afd5b59 --- /dev/null +++ b/src/utils/initState.ts @@ -0,0 +1,103 @@ +import triggerDomUpdates from "./triggerDomUpdates" + +export default function initState( + ops: { + stateToElementsMap: Record> + 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) => void + statesListeners: Record void> + conditionalElements: Map< + Element, + { + expr: string + placeholder: Comment + isPresent: boolean + } + > + evaluateIfCondition: (element: Element, expr: string) => void + currentRenderingElement?: HTMLElement + }, + states?: Record, +) { + console.log(states) + // copy state from options + return new Proxy( + { ...(states || {}) }, + { + set: ( + target: Record, + 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 + } + } + // 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, 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 + } + return undefined + }, + }, + ) +} \ No newline at end of file