js/state.js

53 lines
1.2 KiB
JavaScript

export class ChangeEvent extends Event {
constructor(...changes) {
super('change')
this.changes = changes
}
}
export class State extends EventTarget {
#target
#options
#queue
constructor(target={}, options={}) {
super()
this.#options = options
this.#target = target
this.proxy = new Proxy(target, {
set: (_target, prop, value) => { this.set(prop, value); return true },
get: (target, prop) => target[prop],
})
this.addEventListener
if (options.methods ?? true) {
this.addEventListener("change", ({changes}) => {
new Map(changes).forEach((value, prop) => {
if (`${prop}Changed` in this) this[`${prop}Changed`](value)
})
})
}
}
set state(value) { this.proxy.state = value }
get state() { return this.proxy.state }
set(prop, value) {
if (this.#options.defer ?? true) {
if (!this.#queue) {
this.#queue = []
queueMicrotask(() => {
this.dispatchEvent(new ChangeEvent(...this.#queue))
this.#queue = undefined
})
}
this.#queue.push([prop, value])
this.#target[prop] = value
} else {
this.#target[prop] = value
this.dispatchEvent(new ChangeEvent([prop, value]))
}
}
}
export default State