Files
framework/src/module/ComponentBase.ts
2026-03-15 15:09:36 +09:00

68 lines
2.5 KiB
TypeScript

import type { PropsBase, PropStates, StatesBase } from "../types.js";
import { State, type ValuesOfStateArray } from "./State.js";
export abstract class ComponentBase<Props extends PropsBase, States extends StatesBase> {
static propsToPropStates<Props extends PropsBase>(props: Props | PropStates<Props>) {
const propsState: Record<string, State<any>> = {};
Object.entries(props).forEach(([key, value]) => {
if (value instanceof State) {
propsState[key] = value;
}
else {
propsState[key] = new State(value);
}
});
return propsState as PropStates<Props>;
}
states: PropStates<States> = {} as PropStates<States>;
propStates: PropStates<Props>;
children: ComponentBase<any, any>[] = [];
mounted: boolean = false;
subscriberSymbols: { state: State<any>, symbol: Symbol }[] = [];
abstract node: Node;
constructor(props: PropStates<Props> | Props) {
this.propStates = ComponentBase.propsToPropStates(props);
this.init();
}
init(): void { }
protected abstract createNode(): Node;
abstract mount(target: Node): any;
abstract destroy(): any;
onMount(): void { };
onDestoy(): void { };
effect<const T extends State<any>[]>(callback: (...values: ValuesOfStateArray<T>) => void, states: T) {
states.forEach((state) => {
const symbol = state.subscribe(() => callback(...states.map(({ value }) => value) as ValuesOfStateArray<T>));
this.subscriberSymbols.push({ state, symbol });
})
}
derived<const T extends State<any>[], U>(callback: (...values: ValuesOfStateArray<T>) => U, states: T): State<U> {
const derivedState = new State<U>(undefined as U);
states.forEach((state) => {
const symbol = state.subscribe(() => {
const derivedValue = callback(...states.map(({ value }) => value) as ValuesOfStateArray<T>);
derivedState.set(derivedValue);
});
this.subscriberSymbols.push({ state, symbol });
});
return derivedState;
}
update<T extends (keyof Props | (string & {})), U>(
key: T,
value: T extends keyof Props ?
Props[T] extends State<U> ? U : Props[T] :
U
) {
let propState: State<any> = this.propStates[key];
if (!propState) {
propState = new State(value);
this.propStates[key] = propState;
}
propState.set(value);
}
}