UNPKG

mobx-keystone

Version:

A MobX powered state management solution based on data trees with first class support for TypeScript, snapshots, patches and much more

149 lines (133 loc) 3.32 kB
import { observable } from "mobx" import { assertTweakedObject } from "../tweaker/core" import { onGlobalPatches, onPatches, OnPatchesDisposer, OnPatchesListener } from "./emitPatch" import type { Patch } from "./Patch" /** * Patch recorder event. */ export interface PatchRecorderEvent { /** * Target object. */ readonly target: object /** * Recorded patches. */ readonly patches: Patch[] /** * Recorded inverse patches. */ readonly inversePatches: Patch[] } /** * Patch recorder interface. */ export interface PatchRecorder { /** * Gets/sets if the patch recorder is currently recording. */ recording: boolean /** * Observable array of patching events. */ readonly events: PatchRecorderEvent[] /** * Dispose of the patch recorder. */ dispose(): void } /** * Patch recorder options. */ export interface PatchRecorderOptions { /** * If the patch recorder is initially recording when created. */ recording?: boolean /** * An optional callback filter to select wich patches to record/skip. * It will be executed before the event is added to the events list. * * @param patches Patches about to be recorded. * @param inversePatches Inverse patches about to be recorded. * @returns `true` to record the patch, `false` to skip it. */ filter?: (patches: Patch[], inversePatches: Patch[]) => boolean /** * An optional callback run once a patch is recorded. * It will be executed after the event is added to the events list. * * @param patches Patches just recorded. * @param inversePatches Inverse patches just recorded. */ onPatches?: OnPatchesListener } /** * Creates a patch recorder. * * @param subtreeRoot * @param [opts] * @returns The patch recorder. */ export function patchRecorder(subtreeRoot: object, opts?: PatchRecorderOptions): PatchRecorder { assertTweakedObject(subtreeRoot, "subtreeRoot") return internalPatchRecorder(subtreeRoot, opts) } /** * @internal * * Creates a global or local patch recorder. * * @param subtreeRoot * @param [opts] * @returns The patch recorder. */ export function internalPatchRecorder( subtreeRoot: object | undefined, opts?: PatchRecorderOptions ): PatchRecorder { let recording = opts?.recording ?? true const filter = opts?.filter ?? alwaysAcceptFilter const events = observable.array<PatchRecorderEvent>([], { deep: false, }) let onPatchesDisposer: OnPatchesDisposer if (subtreeRoot) { onPatchesDisposer = onPatches(subtreeRoot, (p, invP) => { if (recording && filter(p, invP)) { events.push({ target: subtreeRoot, patches: p, inversePatches: invP, }) opts?.onPatches?.(p, invP) } }) } else { onPatchesDisposer = onGlobalPatches((target, p, invP) => { if (recording && filter(p, invP)) { events.push({ target, patches: p, inversePatches: invP, }) opts?.onPatches?.(p, invP) } }) } return { get recording() { return recording }, set recording(enabled: boolean) { recording = enabled }, get events() { return events }, dispose() { onPatchesDisposer() }, } } const alwaysAcceptFilter = () => true