UNPKG

@joshuafcole/fluorine

Version:

A highly reactive, optionally compiled, and strongly typed view layer for TypeScript.

90 lines (78 loc) 2.3 kB
import { DeepReadonly } from "utility-types"; import ObservableSlim from "observable-slim"; export type DeepMutable<T> = T extends (...args: any[]) => any ? T : T extends Readonly<[any, ...any[]]> ? _DeepMutableObject<T> : T extends ReadonlyArray<any> ? _DeepMutableArray<T[number]> : T extends object ? _DeepMutableObject<T> : T; export interface _DeepMutableArray<T> extends Array<DeepMutable<T>> {} export type _DeepMutableObject<T> = { -readonly [P in keyof T]: DeepMutable<T[P]> }; function shallow_copy<T>(x: T): T { if (x instanceof Array) return [...x] as any; else if (typeof x === "object") return { ...x }; else return x; } export type Transaction<T extends {}> = { source: string; changes: ObservableSlim.Change<T>[]; }; export interface Mutate<T extends object> { (mutator: (mutable: T) => void, no_log?: boolean): void; objs<T extends any[]>( objs: T, mutator: (...muts: DeepMutable<T>) => void, no_log?: boolean ): void; } export function stasis<T extends {}>( state: T, on_change?: (tx: Transaction<T>) => unknown ): { state: DeepReadonly<T>; log: Transaction<T>[]; mutate: Mutate<T>; } { let current_tx: Transaction<T>; let log: Transaction<T>[] = []; // let observer = (changes: ObservableSlim.Change<T>[]) => { // current_tx.changes.push( // ...changes.map(change => ({ // ...change, // previousValue: shallow_copy(change.previousValue) // })) // ); // }; // let proxy = ObservableSlim.create(state, false, observer); let mutate = function(mutator, no_log = false) { current_tx = { source: mutator.name, changes: [] }; mutator(state); // proxy if (!no_log) log.push(current_tx); if (on_change) on_change(current_tx); } as Mutate<T>; mutate.objs = function<T extends any[]>( objs: T, mutator: (...muts: DeepMutable<T>) => void, no_log = false ) { current_tx = { source: mutator.name, changes: [] }; let proxies = objs.map( obj => // ObservableSlim.create(obj, false, observer) obj as any ) as DeepMutable<T>; mutator(...proxies); if (!no_log) log.push(current_tx); if (on_change) on_change(current_tx); }; return { state: state as DeepReadonly<T>, log, mutate }; }