chore: split two-way binding logic into another file
All checks were successful
Publish to npm / quality (push) Successful in 20s
Publish to npm / publish (push) Successful in 24s

This commit is contained in:
Astrian Zheng 2025-05-21 14:36:30 +10:00
parent 9ea14fc2b9
commit 15a63968ea
Signed by: Astrian
SSH Key Fingerprint: SHA256:rVnhx3DAKjujCwWE13aDl7uV6+9U1MvydLkNRXJrBiA
3 changed files with 47 additions and 34 deletions

View File

@ -8,7 +8,9 @@
"build": "tsc && rollup -c && npm run cleanup-intermediate",
"prepare": "npm run build",
"cleanup-intermediate": "rimraf dist/main.js dist/types",
"quality-check": "biome ci ."
"quality-check": "biome ci .",
"qc": "npm run quality-check",
"lint": "biome format . --write"
},
"repository": {
"type": "git",

View File

@ -156,7 +156,6 @@ export default (options: ComponentOptions) => {
setupArrowFunctionHandler: this._setupArrowFunctionHandler.bind(this),
setupFunctionCallHandler: this._setupFunctionCallHandler.bind(this),
setupExpressionHandler: this._setupExpressionHandler.bind(this),
setupTwoWayBinding: this._setupTwoWayBinding.bind(this),
setupConditionRendering: this._setupConditionRendering.bind(this),
setupListRendering: this._setupListRendering.bind(this),
stateToElementsMap: this._stateToElementsMap,
@ -168,6 +167,7 @@ export default (options: ComponentOptions) => {
typeof (this as Record<string, unknown>)[name] === 'function' &&
name !== 'constructor',
),
stateListeners: this._statesListeners,
})
}
@ -200,36 +200,6 @@ export default (options: ComponentOptions) => {
}
}
// Handle two-way data binding (%connect macro)
private _setupTwoWayBinding(element: Element, expr: string) {
// Get the initial value
const value = this._getNestedState(expr)
// Set the initial value
if (value !== undefined)
element.setAttribute('data-laterano-connect', String(value))
else
console.error(
`State \`${expr}\` not found in the component state. Although Laterano will try to work with it, it may has potentially unexpected behavior.`,
)
// Add event listener for input events
element.addEventListener('input', (event: Event) => {
const target = event.target as HTMLInputElement
const newValue = target.value
// Update the state
this.setState(expr, newValue)
})
// Add event listener for state changes
this._statesListeners[expr] = (newValue: unknown) => {
if (element instanceof HTMLInputElement)
element.value = newValue as string
else element.setAttribute('data-laterano-connect', String(newValue))
}
}
// Handle condition rendering (%if macro)
private _setupConditionRendering(element: Element, expr: string) {
const placeholder = document.createComment(` %if: ${expr} `)

View File

@ -24,7 +24,6 @@ export default function processTemplateMacros(
eventName: string,
handlerValue: string,
) => void
setupTwoWayBinding: (element: Element, expr: string) => void
setupConditionRendering: (element: Element, expr: string) => void
setupListRendering: (element: Element, expr: string) => void
stateToElementsMap: Record<string, Set<HTMLElement>>
@ -34,6 +33,7 @@ export default function processTemplateMacros(
originalContent: string
}[]
availableFuncs: string[]
stateListeners: Record<string, (newValue: unknown) => void>
},
) {
/*
@ -194,7 +194,11 @@ export default function processTemplateMacros(
// Handle different types of macros
if (macroName === 'connect')
// Handle state connection: %connect="stateName"
options.setupTwoWayBinding(currentElementNode, expr)
setupTwoWayBinding(currentElementNode, expr, {
getNestedState: context.getState.bind(context),
setState: context.setState.bind(context),
statesListeners: options.stateListeners,
})
else if (macroName === 'if') {
ifDirectivesToProcess.push({ element: currentElementNode, expr })
} else if (macroName === 'for')
@ -213,3 +217,40 @@ export default function processTemplateMacros(
options.setupConditionRendering(ifElement, expr)
}
}
// Handle two-way data binding (%connect macro)
function setupTwoWayBinding(
element: Element,
expr: string,
ops: {
getNestedState: (path: string) => unknown
setState: (path: string, value: unknown) => void
statesListeners: Record<string, (newValue: unknown) => void>
},
) {
// Get the initial value
const value = ops.getNestedState(expr)
// Set the initial value
if (value !== undefined)
element.setAttribute('data-laterano-connect', String(value))
else
console.error(
`State \`${expr}\` not found in the component state. Although Laterano will try to work with it, it may has potentially unexpected behavior.`,
)
// Add event listener for input events
element.addEventListener('input', (event: Event) => {
const target = event.target as HTMLInputElement
const newValue = target.value
// Update the state
ops.setState(expr, newValue)
})
// Add event listener for state changes
ops.statesListeners[expr] = (newValue: unknown) => {
if (element instanceof HTMLInputElement) element.value = newValue as string
else element.setAttribute('data-laterano-connect', String(newValue))
}
}