UNPKG

@tldraw/state

Version:

tldraw infinite canvas SDK (state).

8 lines (7 loc) 7.45 kB
{ "version": 3, "sources": ["../../src/lib/Atom.ts"], "sourcesContent": ["import { ArraySet } from './ArraySet'\nimport { HistoryBuffer } from './HistoryBuffer'\nimport { maybeCaptureParent } from './capture'\nimport { EMPTY_ARRAY, equals, singleton } from './helpers'\nimport { advanceGlobalEpoch, atomDidChange, getGlobalEpoch } from './transactions'\nimport { Child, ComputeDiff, RESET_VALUE, Signal } from './types'\n\n/**\n * The options to configure an atom, passed into the {@link atom} function.\n * @public\n */\nexport interface AtomOptions<Value, Diff> {\n\t/**\n\t * The maximum number of diffs to keep in the history buffer.\n\t *\n\t * If you don't need to compute diffs, or if you will supply diffs manually via {@link Atom.set}, you can leave this as `undefined` and no history buffer will be created.\n\t *\n\t * If you expect the value to be part of an active effect subscription all the time, and to not change multiple times inside of a single transaction, you can set this to a relatively low number (e.g. 10).\n\t *\n\t * Otherwise, set this to a higher number based on your usage pattern and memory constraints.\n\t *\n\t */\n\thistoryLength?: number\n\t/**\n\t * A method used to compute a diff between the atom's old and new values. If provided, it will not be used unless you also specify {@link AtomOptions.historyLength}.\n\t */\n\tcomputeDiff?: ComputeDiff<Value, Diff>\n\t/**\n\t * If provided, this will be used to compare the old and new values of the atom to determine if the value has changed.\n\t * By default, values are compared using first using strict equality (`===`), then `Object.is`, and finally any `.equals` method present in the object's prototype chain.\n\t * @param a - The old value\n\t * @param b - The new value\n\t * @returns\n\t */\n\tisEqual?(a: any, b: any): boolean\n}\n\n/**\n * An Atom is a signal that can be updated directly by calling {@link Atom.set} or {@link Atom.update}.\n *\n * Atoms are created using the {@link atom} function.\n *\n * @example\n * ```ts\n * const name = atom('name', 'John')\n *\n * print(name.get()) // 'John'\n * ```\n *\n * @public\n */\nexport interface Atom<Value, Diff = unknown> extends Signal<Value, Diff> {\n\t/**\n\t * Sets the value of this atom to the given value. If the value is the same as the current value, this is a no-op.\n\t *\n\t * @param value - The new value to set.\n\t * @param diff - The diff to use for the update. If not provided, the diff will be computed using {@link AtomOptions.computeDiff}.\n\t */\n\tset(value: Value, diff?: Diff): Value\n\t/**\n\t * Updates the value of this atom using the given updater function. If the returned value is the same as the current value, this is a no-op.\n\t *\n\t * @param updater - A function that takes the current value and returns the new value.\n\t */\n\tupdate(updater: (value: Value) => Value): Value\n}\n\n/**\n * @internal\n */\nclass __Atom__<Value, Diff = unknown> implements Atom<Value, Diff> {\n\tconstructor(\n\t\tpublic readonly name: string,\n\t\tprivate current: Value,\n\t\toptions?: AtomOptions<Value, Diff>\n\t) {\n\t\tthis.isEqual = options?.isEqual ?? null\n\n\t\tif (!options) return\n\n\t\tif (options.historyLength) {\n\t\t\tthis.historyBuffer = new HistoryBuffer(options.historyLength)\n\t\t}\n\n\t\tthis.computeDiff = options.computeDiff\n\t}\n\n\treadonly isEqual: null | ((a: any, b: any) => boolean)\n\n\tcomputeDiff?: ComputeDiff<Value, Diff>\n\n\tlastChangedEpoch = getGlobalEpoch()\n\n\tchildren = new ArraySet<Child>()\n\n\thistoryBuffer?: HistoryBuffer<Diff>\n\n\t__unsafe__getWithoutCapture(_ignoreErrors?: boolean): Value {\n\t\treturn this.current\n\t}\n\n\tget() {\n\t\tmaybeCaptureParent(this)\n\t\treturn this.current\n\t}\n\n\tset(value: Value, diff?: Diff): Value {\n\t\t// If the value has not changed, do nothing.\n\t\tif (this.isEqual?.(this.current, value) ?? equals(this.current, value)) {\n\t\t\treturn this.current\n\t\t}\n\n\t\t// Tick forward the global epoch\n\t\tadvanceGlobalEpoch()\n\n\t\t// Add the diff to the history buffer.\n\t\tif (this.historyBuffer) {\n\t\t\tthis.historyBuffer.pushEntry(\n\t\t\t\tthis.lastChangedEpoch,\n\t\t\t\tgetGlobalEpoch(),\n\t\t\t\tdiff ??\n\t\t\t\t\tthis.computeDiff?.(this.current, value, this.lastChangedEpoch, getGlobalEpoch()) ??\n\t\t\t\t\tRESET_VALUE\n\t\t\t)\n\t\t}\n\n\t\t// Update the atom's record of the epoch when last changed.\n\t\tthis.lastChangedEpoch = getGlobalEpoch()\n\n\t\tconst oldValue = this.current\n\t\tthis.current = value\n\n\t\t// Notify all children that this atom has changed.\n\t\tatomDidChange(this as any, oldValue)\n\n\t\treturn value\n\t}\n\n\tupdate(updater: (value: Value) => Value): Value {\n\t\treturn this.set(updater(this.current))\n\t}\n\n\tgetDiffSince(epoch: number): RESET_VALUE | Diff[] {\n\t\tmaybeCaptureParent(this)\n\n\t\t// If no changes have occurred since the given epoch, return an empty array.\n\t\tif (epoch >= this.lastChangedEpoch) {\n\t\t\treturn EMPTY_ARRAY\n\t\t}\n\n\t\treturn this.historyBuffer?.getChangesSince(epoch) ?? RESET_VALUE\n\t}\n}\n\nexport const _Atom = singleton('Atom', () => __Atom__)\nexport type _Atom = InstanceType<typeof _Atom>\n\n/**\n * Creates a new {@link Atom}.\n *\n * An Atom is a signal that can be updated directly by calling {@link Atom.set} or {@link Atom.update}.\n *\n * @example\n * ```ts\n * const name = atom('name', 'John')\n *\n * name.get() // 'John'\n *\n * name.set('Jane')\n *\n * name.get() // 'Jane'\n * ```\n *\n * @public\n */\nexport function atom<Value, Diff = unknown>(\n\t/**\n\t * A name for the signal. This is used for debugging and profiling purposes, it does not need to be unique.\n\t */\n\tname: string,\n\t/**\n\t * The initial value of the signal.\n\t */\n\tinitialValue: Value,\n\t/**\n\t * The options to configure the atom. See {@link AtomOptions}.\n\t */\n\toptions?: AtomOptions<Value, Diff>\n): Atom<Value, Diff> {\n\treturn new _Atom(name, initialValue, options)\n}\n\n/**\n * Returns true if the given value is an {@link Atom}.\n * @public\n */\nexport function isAtom(value: unknown): value is Atom<unknown> {\n\treturn value instanceof _Atom\n}\n"], "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAC9B,SAAS,0BAA0B;AACnC,SAAS,aAAa,QAAQ,iBAAiB;AAC/C,SAAS,oBAAoB,eAAe,sBAAsB;AAClE,SAA6B,mBAA2B;AAiExD,MAAM,SAA6D;AAAA,EAClE,YACiB,MACR,SACR,SACC;AAHe;AACR;AAGR,SAAK,UAAU,SAAS,WAAW;AAEnC,QAAI,CAAC,QAAS;AAEd,QAAI,QAAQ,eAAe;AAC1B,WAAK,gBAAgB,IAAI,cAAc,QAAQ,aAAa;AAAA,IAC7D;AAEA,SAAK,cAAc,QAAQ;AAAA,EAC5B;AAAA,EAES;AAAA,EAET;AAAA,EAEA,mBAAmB,eAAe;AAAA,EAElC,WAAW,IAAI,SAAgB;AAAA,EAE/B;AAAA,EAEA,4BAA4B,eAAgC;AAC3D,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM;AACL,uBAAmB,IAAI;AACvB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,IAAI,OAAc,MAAoB;AAErC,QAAI,KAAK,UAAU,KAAK,SAAS,KAAK,KAAK,OAAO,KAAK,SAAS,KAAK,GAAG;AACvE,aAAO,KAAK;AAAA,IACb;AAGA,uBAAmB;AAGnB,QAAI,KAAK,eAAe;AACvB,WAAK,cAAc;AAAA,QAClB,KAAK;AAAA,QACL,eAAe;AAAA,QACf,QACC,KAAK,cAAc,KAAK,SAAS,OAAO,KAAK,kBAAkB,eAAe,CAAC,KAC/E;AAAA,MACF;AAAA,IACD;AAGA,SAAK,mBAAmB,eAAe;AAEvC,UAAM,WAAW,KAAK;AACtB,SAAK,UAAU;AAGf,kBAAc,MAAa,QAAQ;AAEnC,WAAO;AAAA,EACR;AAAA,EAEA,OAAO,SAAyC;AAC/C,WAAO,KAAK,IAAI,QAAQ,KAAK,OAAO,CAAC;AAAA,EACtC;AAAA,EAEA,aAAa,OAAqC;AACjD,uBAAmB,IAAI;AAGvB,QAAI,SAAS,KAAK,kBAAkB;AACnC,aAAO;AAAA,IACR;AAEA,WAAO,KAAK,eAAe,gBAAgB,KAAK,KAAK;AAAA,EACtD;AACD;AAEO,MAAM,QAAQ,UAAU,QAAQ,MAAM,QAAQ;AAqB9C,SAAS,KAIf,MAIA,cAIA,SACoB;AACpB,SAAO,IAAI,MAAM,MAAM,cAAc,OAAO;AAC7C;AAMO,SAAS,OAAO,OAAwC;AAC9D,SAAO,iBAAiB;AACzB;", "names": [] }