elexis/lib/$State.ts
defaultkavy 4c078c26b6
v0.3.0
- remove: class $EventMethod, $Event.
- change: $EventManager rewrite logic.
- change: most $Node base node element generic type have new EM(EventMap) parameter.
- new: $EventTarget.
- change: $Node extends $EventTarget.
- change: mix dom events and $EventManager events into $EventTarget.on().off().once().
- fix: $Container.insert() process synchronous when passing an not async function
- new: $Window element.
- fix: $(document.documentElement) throw error.
- new: $KeyboardManager, $FocusManager, $PointerManager.
- new: $ global methods:
  - $.events() return new $EventManager.
  - $.pointers() return new $PointerManager.
  - $.keys() return new $KeyboardManager.
  - $.focus() return new $FocusManager.
  - $.call()
- change: $Media extends $HTMLElement
- change: $Anchor.href() support $State parameter.
- new: $State.convert()
2024-10-17 11:54:28 +08:00

76 lines
2.6 KiB
TypeScript

export interface $StateOption<T> {
format: (value: T) => string;
}
export class $State<T> {
protected _value!: T | $State<T>;
readonly attributes = new Map<Object, Set<string | number | symbol>>();
readonly linkStates = new Set<$State<T>>;
options: Partial<$StateOption<T>> = {}
constructor(value: T, options?: $StateOption<T>) {
this.set(value);
if (options) this.options = options;
}
set(value: T | $State<T>) {
this._value = value;
if (value instanceof $State) value.linkStates.add(this as any);
this.update();
this.linkStates.forEach($state => $state.update());
}
static toJSON(object: Object): Object {
const data = {};
for (let [key, value] of Object.entries(object)) {
if (value instanceof $State) value = value.toJSON();
else if (value instanceof Object) $State.toJSON(value);
Object.assign(data, {[key]: value})
}
return data;
}
protected update() {
// update element content for eatch attributes
for (const [node, attrList] of this.attributes.entries()) {
for (const attr of attrList) {
//@ts-expect-error
if (node[attr] instanceof Function) {
//@ts-expect-error
if (this.options.format) node[attr](this.options.format(this.value))
//@ts-expect-error
else node[attr](this.value)
}
else if (attr in node) {
//@ts-expect-error
node[attr] = this.value
}
}
}
}
use<O extends Object, K extends keyof O>(object: O, attrName: K) {
const attrList = this.attributes.get(object)
if (attrList) attrList.add(attrName);
else this.attributes.set(object, new Set<string | number | symbol>().add(attrName))
}
convert(fn: (value: T) => string) {
return new $State<T>(this as any, {format: fn});
}
get value(): T {
return this._value instanceof $State ? this._value.value as T : this._value;
}
toString(): string {
if (this.options.format) return this.options.format(this.value);
if (this.value instanceof Object) return JSON.stringify(this.toJSON());
return `${this.value}`
}
toJSON(): Object {
if (this.value instanceof $State) return this.value.toJSON();
if (this.value instanceof Object) return $State.toJSON(this.value);
else return this.toString();
}
};
export type $StateArgument<T> = $State<T> | undefined | (T extends (infer R)[] ? R : T);