UNPKG

@tldraw/store

Version:

tldraw infinite canvas SDK (store).

8 lines (7 loc) 7.78 kB
{ "version": 3, "sources": ["../../src/lib/IncrementalSetConstructor.ts"], "sourcesContent": ["import { CollectionDiff } from './Store'\n\n/**\n * A utility class for incrementally building a set while tracking changes. This class allows\n * you to add and remove items from a set while maintaining a diff of what was added and\n * removed from the original set. It's optimized for cases where you need to track changes\n * to a set over time and get both the final result and the change delta.\n *\n * @example\n * ```ts\n * const originalSet = new Set(['a', 'b', 'c'])\n * const constructor = new IncrementalSetConstructor(originalSet)\n *\n * constructor.add('d') // Add new item\n * constructor.remove('b') // Remove existing item\n * constructor.add('a') // Re-add removed item (no-op since already present)\n *\n * const result = constructor.get()\n * // result.value contains Set(['a', 'c', 'd'])\n * // result.diff contains { added: Set(['d']), removed: Set(['b']) }\n * ```\n *\n * @internal\n */\nexport class IncrementalSetConstructor<T> {\n\t/**\n\t * The next value of the set.\n\t *\n\t * @internal\n\t */\n\tprivate nextValue?: Set<T>\n\n\t/**\n\t * The diff of the set.\n\t *\n\t * @internal\n\t */\n\tprivate diff?: CollectionDiff<T>\n\n\tconstructor(\n\t\t/**\n\t\t * The previous value of the set.\n\t\t *\n\t\t * @internal\n\t\t * @readonly\n\t\t */\n\t\tprivate readonly previousValue: Set<T>\n\t) {}\n\n\t/**\n\t * Gets the result of the incremental set construction if any changes were made.\n\t * Returns undefined if no additions or removals occurred.\n\t *\n\t * @returns An object containing the final set value and the diff of changes,\n\t * or undefined if no changes were made\n\t *\n\t * @example\n\t * ```ts\n\t * const constructor = new IncrementalSetConstructor(new Set(['a', 'b']))\n\t * constructor.add('c')\n\t *\n\t * const result = constructor.get()\n\t * // result = {\n\t * // value: Set(['a', 'b', 'c']),\n\t * // diff: { added: Set(['c']) }\n\t * // }\n\t * ```\n\t *\n\t * @public\n\t */\n\tpublic get() {\n\t\tconst numRemoved = this.diff?.removed?.size ?? 0\n\t\tconst numAdded = this.diff?.added?.size ?? 0\n\t\tif (numRemoved === 0 && numAdded === 0) {\n\t\t\treturn undefined\n\t\t}\n\t\treturn { value: this.nextValue!, diff: this.diff! }\n\t}\n\n\t/**\n\t * Add an item to the set.\n\t *\n\t * @param item - The item to add.\n\t * @param wasAlreadyPresent - Whether the item was already present in the set.\n\t * @internal\n\t */\n\tprivate _add(item: T, wasAlreadyPresent: boolean) {\n\t\tthis.nextValue ??= new Set(this.previousValue)\n\t\tthis.nextValue.add(item)\n\n\t\tthis.diff ??= {}\n\t\tif (wasAlreadyPresent) {\n\t\t\tthis.diff.removed?.delete(item)\n\t\t} else {\n\t\t\tthis.diff.added ??= new Set()\n\t\t\tthis.diff.added.add(item)\n\t\t}\n\t}\n\n\t/**\n\t * Adds an item to the set. If the item was already present in the original set\n\t * and was previously removed during this construction, it will be restored.\n\t * If the item is already present and wasn't removed, this is a no-op.\n\t *\n\t * @param item - The item to add to the set\n\t *\n\t * @example\n\t * ```ts\n\t * const constructor = new IncrementalSetConstructor(new Set(['a', 'b']))\n\t * constructor.add('c') // Adds new item\n\t * constructor.add('a') // No-op, already present\n\t * constructor.remove('b')\n\t * constructor.add('b') // Restores previously removed item\n\t * ```\n\t *\n\t * @public\n\t */\n\tadd(item: T) {\n\t\tconst wasAlreadyPresent = this.previousValue.has(item)\n\t\tif (wasAlreadyPresent) {\n\t\t\tconst wasRemoved = this.diff?.removed?.has(item)\n\t\t\t// if it wasn't removed during the lifetime of this set constructor, there's no need to add it again\n\t\t\tif (!wasRemoved) return\n\t\t\treturn this._add(item, wasAlreadyPresent)\n\t\t}\n\t\tconst isCurrentlyPresent = this.nextValue?.has(item)\n\t\t// if it's already there, no need to add it again\n\t\tif (isCurrentlyPresent) return\n\t\t// otherwise add it\n\t\tthis._add(item, wasAlreadyPresent)\n\t}\n\n\t/**\n\t * Remove an item from the set.\n\t *\n\t * @param item - The item to remove.\n\t * @param wasAlreadyPresent - Whether the item was already present in the set.\n\t * @internal\n\t */\n\tprivate _remove(item: T, wasAlreadyPresent: boolean) {\n\t\tthis.nextValue ??= new Set(this.previousValue)\n\t\tthis.nextValue.delete(item)\n\n\t\tthis.diff ??= {}\n\t\tif (wasAlreadyPresent) {\n\t\t\t// it was in the original set, so we need to add it to the removed diff\n\t\t\tthis.diff.removed ??= new Set()\n\t\t\tthis.diff.removed.add(item)\n\t\t} else {\n\t\t\t// if it was added during the lifetime of this set constructor, we need to remove it from the added diff\n\t\t\tthis.diff.added?.delete(item)\n\t\t}\n\t}\n\n\t/**\n\t * Removes an item from the set. If the item wasn't present in the original set\n\t * and was added during this construction, it will be removed from the added diff.\n\t * If the item is not present at all, this is a no-op.\n\t *\n\t * @param item - The item to remove from the set\n\t *\n\t * @example\n\t * ```ts\n\t * const constructor = new IncrementalSetConstructor(new Set(['a', 'b']))\n\t * constructor.remove('a') // Removes existing item\n\t * constructor.remove('c') // No-op, not present\n\t * constructor.add('d')\n\t * constructor.remove('d') // Removes recently added item\n\t * ```\n\t *\n\t * @public\n\t */\n\tremove(item: T) {\n\t\tconst wasAlreadyPresent = this.previousValue.has(item)\n\t\tif (!wasAlreadyPresent) {\n\t\t\tconst wasAdded = this.diff?.added?.has(item)\n\t\t\t// if it wasn't added during the lifetime of this set constructor, there's no need to remove it\n\t\t\tif (!wasAdded) return\n\t\t\treturn this._remove(item, wasAlreadyPresent)\n\t\t}\n\t\tconst hasAlreadyBeenRemoved = this.diff?.removed?.has(item)\n\t\t// if it's already removed, no need to remove it again\n\t\tif (hasAlreadyBeenRemoved) return\n\t\t// otherwise remove it\n\t\tthis._remove(item, wasAlreadyPresent)\n\t}\n}\n"], "mappings": "AAwBO,MAAM,0BAA6B;AAAA,EAezC,YAOkB,eAChB;AADgB;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAjBK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiCD,MAAM;AACZ,UAAM,aAAa,KAAK,MAAM,SAAS,QAAQ;AAC/C,UAAM,WAAW,KAAK,MAAM,OAAO,QAAQ;AAC3C,QAAI,eAAe,KAAK,aAAa,GAAG;AACvC,aAAO;AAAA,IACR;AACA,WAAO,EAAE,OAAO,KAAK,WAAY,MAAM,KAAK,KAAM;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,KAAK,MAAS,mBAA4B;AACjD,SAAK,cAAc,IAAI,IAAI,KAAK,aAAa;AAC7C,SAAK,UAAU,IAAI,IAAI;AAEvB,SAAK,SAAS,CAAC;AACf,QAAI,mBAAmB;AACtB,WAAK,KAAK,SAAS,OAAO,IAAI;AAAA,IAC/B,OAAO;AACN,WAAK,KAAK,UAAU,oBAAI,IAAI;AAC5B,WAAK,KAAK,MAAM,IAAI,IAAI;AAAA,IACzB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,IAAI,MAAS;AACZ,UAAM,oBAAoB,KAAK,cAAc,IAAI,IAAI;AACrD,QAAI,mBAAmB;AACtB,YAAM,aAAa,KAAK,MAAM,SAAS,IAAI,IAAI;AAE/C,UAAI,CAAC,WAAY;AACjB,aAAO,KAAK,KAAK,MAAM,iBAAiB;AAAA,IACzC;AACA,UAAM,qBAAqB,KAAK,WAAW,IAAI,IAAI;AAEnD,QAAI,mBAAoB;AAExB,SAAK,KAAK,MAAM,iBAAiB;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,QAAQ,MAAS,mBAA4B;AACpD,SAAK,cAAc,IAAI,IAAI,KAAK,aAAa;AAC7C,SAAK,UAAU,OAAO,IAAI;AAE1B,SAAK,SAAS,CAAC;AACf,QAAI,mBAAmB;AAEtB,WAAK,KAAK,YAAY,oBAAI,IAAI;AAC9B,WAAK,KAAK,QAAQ,IAAI,IAAI;AAAA,IAC3B,OAAO;AAEN,WAAK,KAAK,OAAO,OAAO,IAAI;AAAA,IAC7B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,OAAO,MAAS;AACf,UAAM,oBAAoB,KAAK,cAAc,IAAI,IAAI;AACrD,QAAI,CAAC,mBAAmB;AACvB,YAAM,WAAW,KAAK,MAAM,OAAO,IAAI,IAAI;AAE3C,UAAI,CAAC,SAAU;AACf,aAAO,KAAK,QAAQ,MAAM,iBAAiB;AAAA,IAC5C;AACA,UAAM,wBAAwB,KAAK,MAAM,SAAS,IAAI,IAAI;AAE1D,QAAI,sBAAuB;AAE3B,SAAK,QAAQ,MAAM,iBAAiB;AAAA,EACrC;AACD;", "names": [] }