0.0.3: Exotic Type Gymnastics #3

Merged
Astrian merged 40 commits from dev into main 2025-05-22 01:17:51 +00:00
3 changed files with 58 additions and 61 deletions
Showing only changes of commit 9ea14fc2b9 - Show all commits

View File

@ -161,13 +161,16 @@ export default (options: ComponentOptions) => {
setupListRendering: this._setupListRendering.bind(this),
stateToElementsMap: this._stateToElementsMap,
textBindings: this._textBindings,
availableFuncs: Object.getOwnPropertyNames(Object.getPrototypeOf(this)).filter(
name => typeof (this as Record<string, unknown>)[name] === 'function' && name !== 'constructor'
availableFuncs: Object.getOwnPropertyNames(
Object.getPrototypeOf(this),
).filter(
(name) =>
typeof (this as Record<string, unknown>)[name] === 'function' &&
name !== 'constructor',
),
})
}
private _scheduleUpdate(elements: Set<HTMLElement>) {
requestAnimationFrame(() => {
for (const element of elements) this._updateElement(element)
@ -325,12 +328,12 @@ export default (options: ComponentOptions) => {
// Determine the key for this item
const key = keyAttr
? this._evaluateExpressionWithItemContext(
keyAttr ?? '',
item,
index,
itemVar,
indexVar ? indexVar : undefined,
)
keyAttr ?? '',
item,
index,
itemVar,
indexVar ? indexVar : undefined,
)
: index
// Check if we can reuse an existing element
@ -415,7 +418,7 @@ export default (options: ComponentOptions) => {
itemContext: Record<string, unknown>,
) {
// 1. Store the item context of the element so that subsequent updates can find it
; (element as { _itemContext?: Record<string, unknown> })._itemContext =
;(element as { _itemContext?: Record<string, unknown> })._itemContext =
itemContext
// 2. Process bindings in text nodes

View File

@ -32,7 +32,7 @@ export default function processTemplateMacros(
node: Text
expr: string
originalContent: string
}[],
}[]
availableFuncs: string[]
},
) {
@ -129,9 +129,9 @@ export default function processTemplateMacros(
}
// Process @event bindings, such as @click="handleClick"
const eventBindings = Array.from(
currentElementNode.attributes,
).filter((attr) => attr.name.startsWith('@'))
const eventBindings = Array.from(currentElementNode.attributes).filter(
(attr) => attr.name.startsWith('@'),
)
// eventBindings.forEach((attr) => {
for (const attr of eventBindings) {
const eventName = attr.name.substring(1) // Remove '@'
@ -141,16 +141,15 @@ export default function processTemplateMacros(
currentElementNode.removeAttribute(attr.name)
// Handle different types of event handlers
if (handlerValue.includes('=>')) { // Handle arrow function: @click="e => setState('count', count + 1)"
if (handlerValue.includes('=>')) {
// Handle arrow function: @click="e => setState('count', count + 1)"
options.setupArrowFunctionHandler(
currentElementNode,
eventName,
handlerValue,
)
} else if (
handlerValue.includes('(') &&
handlerValue.includes(')')
) { // Handle function call: @click="increment(5)"
} else if (handlerValue.includes('(') && handlerValue.includes(')')) {
// Handle function call: @click="increment(5)"
options.setupFunctionCallHandler(
currentElementNode,
eventName,
@ -159,16 +158,14 @@ export default function processTemplateMacros(
} else if (
options.availableFuncs.includes(handlerValue) &&
typeof (context as unknown as Record<string, unknown>)[
handlerValue
handlerValue
] === 'function'
) { // Handle method reference: @click="handleClick"
) {
// Handle method reference: @click="handleClick"
currentElementNode.addEventListener(
eventName,
(
context as unknown as Record<
string,
(...args: unknown[]) => void
>
context as unknown as Record<string, (...args: unknown[]) => void>
)[handlerValue].bind(context),
)
} else {
@ -182,9 +179,9 @@ export default function processTemplateMacros(
}
// Process %-started macros, such as %connect="stateName", %if="condition", %for="item in items"
const macroBindings = Array.from(
currentElementNode.attributes,
).filter((attr) => attr.name.startsWith('%'))
const macroBindings = Array.from(currentElementNode.attributes).filter(
(attr) => attr.name.startsWith('%'),
)
// macroBindings.forEach((attr) => {
for (const attr of macroBindings) {

View File

@ -1,20 +1,27 @@
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,
}) {
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>()
@ -28,24 +35,14 @@ export default function triggerDomUpdates(keyPath: string, ops: {
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,
)
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}.`)
) {
if (binding.expr === keyPath || binding.expr.startsWith(`${keyPath}.`)) {
const value = ops.getNestedState(binding.expr)
if (value !== undefined)
binding.element.setAttribute(binding.attrName, String(value))