@joshuafcole/fluorine
Version:
A highly reactive, optionally compiled, and strongly typed view layer for TypeScript.
90 lines (78 loc) • 2.3 kB
text/typescript
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
};
}