Add event handling support in ComponentOptions and implement event binding logic in CustomElement
This commit is contained in:
parent
841f02bb38
commit
c44c9fc2c0
153
src/main.ts
153
src/main.ts
|
@ -12,6 +12,7 @@ interface ComponentOptions {
|
|||
onAttributeChanged?: (attrName: string, oldValue: string, newValue: string) => void
|
||||
states?: Record<string, any>
|
||||
statesListeners?: { [key: string]: (value: any) => void }
|
||||
events?: { [key: string]: (event: Event) => void }
|
||||
}
|
||||
|
||||
export default (options: ComponentOptions) => {
|
||||
|
@ -254,7 +255,31 @@ export default (options: ComponentOptions) => {
|
|||
}
|
||||
})
|
||||
|
||||
// Can also handle event bindings and other features here
|
||||
// Process @event bindings, such as @click="handleClick"
|
||||
const eventBindings = Array.from(element.attributes).filter(attr => attr.name.startsWith('@'))
|
||||
eventBindings.forEach(attr => {
|
||||
const eventName = attr.name.substring(1) // Remove '@'
|
||||
const handlerValue = attr.value.trim()
|
||||
|
||||
// Remove the attribute, as it is not a standard HTML attribute
|
||||
element.removeAttribute(attr.name)
|
||||
|
||||
// Handle different types of event handlers
|
||||
if (handlerValue.includes('=>')) {
|
||||
// Handle arrow function: @click="e => setState('count', count + 1)"
|
||||
this._setupArrowFunctionHandler(element, eventName, handlerValue)
|
||||
} else if (handlerValue.includes('(') && handlerValue.includes(')')) {
|
||||
// Handle function call: @click="increment(5)"
|
||||
this._setupFunctionCallHandler(element, eventName, handlerValue)
|
||||
} else if (typeof (this as any)[handlerValue] === 'function') {
|
||||
// Handle method reference: @click="handleClick"
|
||||
element.addEventListener(eventName, (this as any)[handlerValue].bind(this))
|
||||
} else {
|
||||
// Handle simple expression: @click="count++" or @input="name = $event.target.value"
|
||||
this._setupExpressionHandler(element, eventName, handlerValue)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,6 +287,132 @@ export default (options: ComponentOptions) => {
|
|||
this._textBindings = textBindings
|
||||
}
|
||||
|
||||
// Handle arrow function
|
||||
private _setupArrowFunctionHandler(element: Element, eventName: string, handlerValue: string) {
|
||||
element.addEventListener(eventName, (event: Event) => {
|
||||
try {
|
||||
// Arrow function parsing
|
||||
const arrowIndex = handlerValue.indexOf('=>')
|
||||
const paramsStr = handlerValue.substring(0, arrowIndex).trim()
|
||||
let bodyStr = handlerValue.substring(arrowIndex + 2).trim()
|
||||
|
||||
// Check if the function body is wrapped in {}
|
||||
const isMultiline = bodyStr.startsWith('{') && bodyStr.endsWith('}')
|
||||
|
||||
// If it is a multiline function body, remove the outer braces
|
||||
if (isMultiline) {
|
||||
// Remove the outer braces
|
||||
bodyStr = bodyStr.substring(1, bodyStr.length - 1)
|
||||
|
||||
// Build code for multiline arrow function
|
||||
const functionCode = `
|
||||
return function(${paramsStr}) {
|
||||
${bodyStr}
|
||||
}
|
||||
`
|
||||
|
||||
// Create context object
|
||||
const context = this._createHandlerContext(event, element)
|
||||
|
||||
// Create and call function
|
||||
const handlerFn = new Function(functionCode).call(null)
|
||||
handlerFn.apply(context, [event])
|
||||
} else {
|
||||
// Single line arrow function, directly return expression result
|
||||
const functionCode = `
|
||||
return function(${paramsStr}) {
|
||||
return ${bodyStr}
|
||||
}
|
||||
`
|
||||
|
||||
// Create context object
|
||||
const context = this._createHandlerContext(event, element)
|
||||
|
||||
// Create and call function
|
||||
const handlerFn = new Function(functionCode).call(null)
|
||||
handlerFn.apply(context, [event])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error executing arrow function handler: ${handlerValue}`, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create handler context
|
||||
private _createHandlerContext(event: Event, element: Element) {
|
||||
// Basic context, including state
|
||||
const context: {
|
||||
[key: string]: any
|
||||
$event: Event
|
||||
$el: Element
|
||||
this: CustomElementImpl // Provide reference to the component instance
|
||||
setState: (keyPath: string, value: any) => void
|
||||
getState: (keyPath: string) => any
|
||||
} = {
|
||||
...this._states,
|
||||
$event: event,
|
||||
$el: element,
|
||||
this: this, // Provide reference to the component instance
|
||||
setState: this.setState.bind(this),
|
||||
getState: this.getState.bind(this)
|
||||
}
|
||||
|
||||
// Add all methods of the component
|
||||
Object.getOwnPropertyNames(Object.getPrototypeOf(this)).forEach(name => {
|
||||
if (typeof (this as any)[name] === 'function' && name !== 'constructor') {
|
||||
context[name] = (this as any)[name].bind(this)
|
||||
}
|
||||
})
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
// Handle function call, such as @click="increment(5)"
|
||||
private _setupFunctionCallHandler(element: Element, eventName: string, handlerValue: string) {
|
||||
element.addEventListener(eventName, (event: Event) => {
|
||||
try {
|
||||
// Create context object
|
||||
const context = this._createHandlerContext(event, element)
|
||||
|
||||
// Create and execute function call
|
||||
const fnStr = `
|
||||
with(this) {
|
||||
${handlerValue}
|
||||
}
|
||||
`
|
||||
|
||||
new Function(fnStr).call(context)
|
||||
} catch (err) {
|
||||
console.error(`Error executing function call handler: ${handlerValue}`, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Handle simple expression, such as @click="count++" or @input="name = $event.target.value"
|
||||
private _setupExpressionHandler(element: Element, eventName: string, handlerValue: string) {
|
||||
element.addEventListener(eventName, (event: Event) => {
|
||||
try {
|
||||
// Create context object
|
||||
const context = this._createHandlerContext(event, element)
|
||||
|
||||
// Create expression function
|
||||
const fnStr = `
|
||||
with(this) {
|
||||
${handlerValue}
|
||||
}
|
||||
`
|
||||
|
||||
// Execute expression
|
||||
const result = new Function(fnStr).call(context)
|
||||
|
||||
// If the expression returns a value, it can be used for two-way binding
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error(`Error executing expression handler: ${handlerValue}`, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Update text node
|
||||
private _updateTextNode(node: Text, expr: string, template: string) {
|
||||
// Replace all expressions with the current state value
|
||||
|
|
Loading…
Reference in New Issue
Block a user