Refactor CustomElement implementation: update interface for onMount and onUnmount, enhance setState logic, and rename class to CustomElementImpl

This commit is contained in:
Astrian Zheng 2025-05-14 16:02:50 +10:00
parent cbd2c67d2d
commit 0aee6488d2
Signed by: Astrian
SSH Key Fingerprint: SHA256:rVnhx3DAKjujCwWE13aDl7uV6+9U1MvydLkNRXJrBiA

View File

@ -1,8 +1,12 @@
interface CustomElement extends HTMLElement {
setState(key_path: string, value: any): void
}
interface ComponentOptions { interface ComponentOptions {
tag: string tag: string
template: string template: string
style?: string style?: string
onMount?: () => void onMount?: (this: CustomElement) => void
onUnmount?: () => void onUnmount?: () => void
onAttributeChanged?: (attrName: string, oldValue: string, newValue: string) => void onAttributeChanged?: (attrName: string, oldValue: string, newValue: string) => void
states?: Record<string, any> states?: Record<string, any>
@ -13,7 +17,7 @@ export default (options: ComponentOptions) => {
const componentRegistry = new Map() const componentRegistry = new Map()
componentRegistry.set(tag, options) componentRegistry.set(tag, options)
class CustomElement extends HTMLElement { class CustomElementImpl extends HTMLElement {
private _states: Record<string, any> = {} private _states: Record<string, any> = {}
constructor() { constructor() {
@ -22,7 +26,20 @@ export default (options: ComponentOptions) => {
// copy state from options // copy state from options
this._states = new Proxy({ ...(states || {}) }, { this._states = new Proxy({ ...(states || {}) }, {
set: (target: Record<string, any>, prop: string, value: any) => { set: (target: Record<string, any>, prop: string, value: any) => {
target[prop] = value const valueRoute = prop.split('.')
let currentTarget = target
for (let i in valueRoute) {
const key = valueRoute[i]
if (parseInt(i) === valueRoute.length - 1) {
currentTarget[key] = value
} else {
if (!currentTarget[key]) {
currentTarget[key] = {}
}
currentTarget = currentTarget[key]
}
}
console.log(`State updated: ${JSON.stringify(this._states)}`)
// TODO: trigger dom updates // TODO: trigger dom updates
// TODO: trigger state update events // TODO: trigger state update events
return true return true
@ -62,11 +79,11 @@ export default (options: ComponentOptions) => {
} }
connectedCallback() { connectedCallback() {
if (onMount) onMount() if (onMount) onMount.call(this)
} }
disconnectedCallback() { disconnectedCallback() {
if (onUnmount) onUnmount() if (onUnmount) onUnmount.call(this)
} }
static get observedAttributes() { static get observedAttributes() {
@ -76,7 +93,23 @@ export default (options: ComponentOptions) => {
attributeChangedCallback(attrName: string, oldValue: string, newValue: string) { attributeChangedCallback(attrName: string, oldValue: string, newValue: string) {
if (onAttributeChanged) onAttributeChanged(attrName, oldValue, newValue) if (onAttributeChanged) onAttributeChanged(attrName, oldValue, newValue)
} }
// state manager
setState(key_path: string, value: any) {
const valueRoute = key_path.split('.')
let currentTarget = this._states
for (let i in valueRoute) {
const key = valueRoute[i]
if (parseInt(i) === valueRoute.length - 1) {
currentTarget[key] = value
} else {
if (!currentTarget[key]) currentTarget[key] = {}
currentTarget = currentTarget[key]
}
}
console.log(`State updated: ${this._states}`)
}
} }
customElements.define(tag, CustomElement) customElements.define(tag, CustomElementImpl)
} }