2024-04-23 18:18:43 +08:00
|
|
|
export interface $StateOption<T> {
|
|
|
|
format: (value: T) => string;
|
|
|
|
}
|
2024-02-03 09:33:34 +08:00
|
|
|
export class $State<T> {
|
2024-08-29 02:27:14 +08:00
|
|
|
protected _value!: T | $State<T>;
|
2024-04-26 18:41:29 +08:00
|
|
|
readonly attributes = new Map<Object, Set<string | number | symbol>>();
|
2024-08-29 02:27:14 +08:00
|
|
|
readonly linkStates = new Set<$State<T>>;
|
2024-04-23 18:18:43 +08:00
|
|
|
options: Partial<$StateOption<T>> = {}
|
|
|
|
constructor(value: T, options?: $StateOption<T>) {
|
2024-05-15 18:54:44 +08:00
|
|
|
this.set(value);
|
2024-04-23 18:18:43 +08:00
|
|
|
if (options) this.options = options;
|
2024-02-03 09:33:34 +08:00
|
|
|
}
|
2024-08-29 02:27:14 +08:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2024-10-17 11:54:28 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-08-29 02:27:14 +08:00
|
|
|
protected update() {
|
2024-05-15 18:54:44 +08:00
|
|
|
// update element content for eatch attributes
|
2024-02-13 19:38:46 +08:00
|
|
|
for (const [node, attrList] of this.attributes.entries()) {
|
|
|
|
for (const attr of attrList) {
|
|
|
|
//@ts-expect-error
|
2024-04-23 18:18:43 +08:00
|
|
|
if (node[attr] instanceof Function) {
|
|
|
|
//@ts-expect-error
|
2024-08-29 02:27:14 +08:00
|
|
|
if (this.options.format) node[attr](this.options.format(this.value))
|
2024-04-23 18:18:43 +08:00
|
|
|
//@ts-expect-error
|
2024-08-29 02:27:14 +08:00
|
|
|
else node[attr](this.value)
|
2024-04-23 18:18:43 +08:00
|
|
|
}
|
2024-04-26 18:41:29 +08:00
|
|
|
else if (attr in node) {
|
|
|
|
//@ts-expect-error
|
2024-08-29 02:27:14 +08:00
|
|
|
node[attr] = this.value
|
2024-04-26 18:41:29 +08:00
|
|
|
}
|
2024-02-03 09:33:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-26 18:41:29 +08:00
|
|
|
use<O extends Object, K extends keyof O>(object: O, attrName: K) {
|
|
|
|
const attrList = this.attributes.get(object)
|
2024-02-13 19:38:46 +08:00
|
|
|
if (attrList) attrList.add(attrName);
|
2024-04-26 18:41:29 +08:00
|
|
|
else this.attributes.set(object, new Set<string | number | symbol>().add(attrName))
|
|
|
|
}
|
|
|
|
|
2024-10-17 11:54:28 +08:00
|
|
|
convert(fn: (value: T) => string) {
|
|
|
|
return new $State<T>(this as any, {format: fn});
|
2024-02-13 19:38:46 +08:00
|
|
|
}
|
2024-08-29 02:27:14 +08:00
|
|
|
|
|
|
|
get value(): T {
|
|
|
|
return this._value instanceof $State ? this._value.value as T : this._value;
|
|
|
|
}
|
2024-10-17 11:54:28 +08:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2024-04-23 18:18:43 +08:00
|
|
|
};
|
|
|
|
|
2024-08-29 02:27:14 +08:00
|
|
|
export type $StateArgument<T> = $State<T> | undefined | (T extends (infer R)[] ? R : T);
|