@tldraw/state 5.3.2 → 5.4.0-canary.02cd0bd3b597
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +64 -63
- package/README.md +35 -36
- package/dist-cjs/index.d.ts +26 -27
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/ArraySet.js +47 -144
- package/dist-cjs/lib/ArraySet.js.map +2 -2
- package/dist-cjs/lib/Atom.js +12 -26
- package/dist-cjs/lib/Atom.js.map +2 -2
- package/dist-cjs/lib/Computed.js +36 -64
- package/dist-cjs/lib/Computed.js.map +2 -2
- package/dist-cjs/lib/EffectScheduler.js +1 -1
- package/dist-cjs/lib/EffectScheduler.js.map +2 -2
- package/dist-cjs/lib/HistoryBuffer.js +8 -8
- package/dist-cjs/lib/HistoryBuffer.js.map +2 -2
- package/dist-cjs/lib/capture.js +1 -3
- package/dist-cjs/lib/capture.js.map +2 -2
- package/dist-cjs/lib/constants.js.map +2 -2
- package/dist-cjs/lib/helpers.js +3 -11
- package/dist-cjs/lib/helpers.js.map +2 -2
- package/dist-cjs/lib/localStorageAtom.js +7 -2
- package/dist-cjs/lib/localStorageAtom.js.map +2 -2
- package/dist-cjs/lib/transactions.js +11 -19
- package/dist-cjs/lib/transactions.js.map +2 -2
- package/dist-cjs/lib/types.js.map +1 -1
- package/dist-cjs/lib/warnings.js +2 -4
- package/dist-cjs/lib/warnings.js.map +2 -2
- package/dist-esm/index.d.mts +26 -27
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ArraySet.mjs +47 -144
- package/dist-esm/lib/ArraySet.mjs.map +2 -2
- package/dist-esm/lib/Atom.mjs +12 -26
- package/dist-esm/lib/Atom.mjs.map +2 -2
- package/dist-esm/lib/Computed.mjs +36 -64
- package/dist-esm/lib/Computed.mjs.map +2 -2
- package/dist-esm/lib/EffectScheduler.mjs +1 -1
- package/dist-esm/lib/EffectScheduler.mjs.map +2 -2
- package/dist-esm/lib/HistoryBuffer.mjs +8 -8
- package/dist-esm/lib/HistoryBuffer.mjs.map +2 -2
- package/dist-esm/lib/capture.mjs +1 -3
- package/dist-esm/lib/capture.mjs.map +2 -2
- package/dist-esm/lib/constants.mjs.map +2 -2
- package/dist-esm/lib/helpers.mjs +3 -11
- package/dist-esm/lib/helpers.mjs.map +2 -2
- package/dist-esm/lib/localStorageAtom.mjs +7 -2
- package/dist-esm/lib/localStorageAtom.mjs.map +2 -2
- package/dist-esm/lib/transactions.mjs +11 -19
- package/dist-esm/lib/transactions.mjs.map +2 -2
- package/dist-esm/lib/types.mjs.map +1 -1
- package/dist-esm/lib/warnings.mjs +2 -4
- package/dist-esm/lib/warnings.mjs.map +2 -2
- package/package.json +2 -2
- package/src/lib/ArraySet.ts +68 -176
- package/src/lib/Atom.ts +27 -31
- package/src/lib/Computed.ts +68 -96
- package/src/lib/EffectScheduler.ts +9 -8
- package/src/lib/HistoryBuffer.ts +12 -10
- package/src/lib/__tests__/ArraySet.test.ts +39 -13
- package/src/lib/__tests__/EffectScheduler.test.ts +18 -0
- package/src/lib/__tests__/HistoryBuffer.test.ts +6 -3
- package/src/lib/__tests__/computed.test.ts +75 -0
- package/src/lib/__tests__/errors.test.ts +24 -0
- package/src/lib/__tests__/helpers.test.ts +7 -11
- package/src/lib/__tests__/history.test.ts +32 -2
- package/src/lib/__tests__/localStorageAtom.test.ts +15 -0
- package/src/lib/capture.ts +13 -13
- package/src/lib/constants.ts +3 -22
- package/src/lib/helpers.ts +15 -140
- package/src/lib/localStorageAtom.ts +9 -2
- package/src/lib/transactions.ts +23 -47
- package/src/lib/types.ts +7 -7
- package/src/lib/warnings.ts +2 -10
|
@@ -1,53 +1,26 @@
|
|
|
1
1
|
const ARRAY_SIZE_THRESHOLD = 8;
|
|
2
2
|
class ArraySet {
|
|
3
|
-
arraySize = 0;
|
|
4
|
-
array = Array(ARRAY_SIZE_THRESHOLD);
|
|
5
3
|
set = null;
|
|
4
|
+
// Slots [0, arraySize) hold the items; slots beyond are undefined. `add`/`has` scan the whole
|
|
5
|
+
// array with indexOf, which is why `clear` and `remove` must blank vacated slots.
|
|
6
|
+
array = null;
|
|
7
|
+
arraySize = 0;
|
|
6
8
|
/**
|
|
7
9
|
* Get whether this ArraySet has any elements.
|
|
8
|
-
*
|
|
9
|
-
* @returns True if this ArraySet has any elements, false otherwise.
|
|
10
10
|
*/
|
|
11
11
|
// eslint-disable-next-line tldraw/no-setter-getter
|
|
12
12
|
get isEmpty() {
|
|
13
|
-
if (this.array) {
|
|
14
|
-
return this.arraySize === 0;
|
|
15
|
-
}
|
|
16
13
|
if (this.set) {
|
|
17
14
|
return this.set.size === 0;
|
|
18
15
|
}
|
|
19
|
-
|
|
16
|
+
return this.arraySize === 0;
|
|
20
17
|
}
|
|
21
18
|
/**
|
|
22
19
|
* Add an element to the ArraySet if it is not already present.
|
|
23
20
|
*
|
|
24
|
-
* @param elem - The element to add to the set
|
|
25
21
|
* @returns `true` if the element was added, `false` if it was already present
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* const arraySet = new ArraySet<string>()
|
|
29
|
-
*
|
|
30
|
-
* console.log(arraySet.add('hello')) // true
|
|
31
|
-
* console.log(arraySet.add('hello')) // false (already exists)
|
|
32
|
-
* ```
|
|
33
22
|
*/
|
|
34
23
|
add(elem) {
|
|
35
|
-
if (this.array) {
|
|
36
|
-
const idx = this.array.indexOf(elem);
|
|
37
|
-
if (idx !== -1) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
if (this.arraySize < ARRAY_SIZE_THRESHOLD) {
|
|
41
|
-
this.array[this.arraySize] = elem;
|
|
42
|
-
this.arraySize++;
|
|
43
|
-
return true;
|
|
44
|
-
} else {
|
|
45
|
-
this.set = new Set(this.array);
|
|
46
|
-
this.array = null;
|
|
47
|
-
this.set.add(elem);
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
24
|
if (this.set) {
|
|
52
25
|
if (this.set.has(elem)) {
|
|
53
26
|
return false;
|
|
@@ -55,168 +28,98 @@ class ArraySet {
|
|
|
55
28
|
this.set.add(elem);
|
|
56
29
|
return true;
|
|
57
30
|
}
|
|
58
|
-
|
|
31
|
+
if (!this.array) {
|
|
32
|
+
this.array = Array(ARRAY_SIZE_THRESHOLD);
|
|
33
|
+
} else if (this.array.indexOf(elem) !== -1) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (this.arraySize < ARRAY_SIZE_THRESHOLD) {
|
|
37
|
+
this.array[this.arraySize] = elem;
|
|
38
|
+
this.arraySize++;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
this.set = new Set(this.array);
|
|
42
|
+
this.set.add(elem);
|
|
43
|
+
this.array = null;
|
|
44
|
+
this.arraySize = 0;
|
|
45
|
+
return true;
|
|
59
46
|
}
|
|
60
47
|
/**
|
|
61
48
|
* Remove an element from the ArraySet if it is present.
|
|
62
49
|
*
|
|
63
|
-
* @param elem - The element to remove from the set
|
|
64
50
|
* @returns `true` if the element was removed, `false` if it was not present
|
|
65
|
-
* @example
|
|
66
|
-
* ```ts
|
|
67
|
-
* const arraySet = new ArraySet<string>()
|
|
68
|
-
* arraySet.add('hello')
|
|
69
|
-
*
|
|
70
|
-
* console.log(arraySet.remove('hello')) // true
|
|
71
|
-
* console.log(arraySet.remove('hello')) // false (not present)
|
|
72
|
-
* ```
|
|
73
51
|
*/
|
|
74
52
|
remove(elem) {
|
|
75
|
-
if (this.array) {
|
|
76
|
-
const idx = this.array.indexOf(elem);
|
|
77
|
-
if (idx === -1) {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
this.array[idx] = void 0;
|
|
81
|
-
this.arraySize--;
|
|
82
|
-
if (idx !== this.arraySize) {
|
|
83
|
-
this.array[idx] = this.array[this.arraySize];
|
|
84
|
-
this.array[this.arraySize] = void 0;
|
|
85
|
-
}
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
53
|
if (this.set) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
54
|
+
return this.set.delete(elem);
|
|
55
|
+
}
|
|
56
|
+
if (!this.array) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const idx = this.array.indexOf(elem);
|
|
60
|
+
if (idx === -1) {
|
|
61
|
+
return false;
|
|
94
62
|
}
|
|
95
|
-
|
|
63
|
+
this.arraySize--;
|
|
64
|
+
this.array[idx] = this.array[this.arraySize];
|
|
65
|
+
this.array[this.arraySize] = void 0;
|
|
66
|
+
return true;
|
|
96
67
|
}
|
|
97
68
|
/**
|
|
98
69
|
* Execute a callback function for each element in the ArraySet.
|
|
99
|
-
*
|
|
100
|
-
* @param visitor - A function to call for each element in the set
|
|
101
|
-
* @example
|
|
102
|
-
* ```ts
|
|
103
|
-
* const arraySet = new ArraySet<string>()
|
|
104
|
-
* arraySet.add('hello')
|
|
105
|
-
* arraySet.add('world')
|
|
106
|
-
*
|
|
107
|
-
* arraySet.visit((item) => {
|
|
108
|
-
* console.log(item) // 'hello', 'world'
|
|
109
|
-
* })
|
|
110
|
-
* ```
|
|
111
70
|
*/
|
|
112
71
|
visit(visitor) {
|
|
113
|
-
if (this.array) {
|
|
114
|
-
for (let i = 0; i < this.arraySize; i++) {
|
|
115
|
-
const elem = this.array[i];
|
|
116
|
-
if (typeof elem !== "undefined") {
|
|
117
|
-
visitor(elem);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
72
|
if (this.set) {
|
|
123
73
|
this.set.forEach(visitor);
|
|
124
74
|
return;
|
|
125
75
|
}
|
|
126
|
-
|
|
76
|
+
if (!this.array) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
for (let i = 0; i < this.arraySize; i++) {
|
|
80
|
+
visitor(this.array[i]);
|
|
81
|
+
}
|
|
127
82
|
}
|
|
128
83
|
/**
|
|
129
84
|
* Make the ArraySet iterable, allowing it to be used in for...of loops and with spread syntax.
|
|
130
|
-
*
|
|
131
|
-
* @returns An iterator that yields each element in the set
|
|
132
|
-
* @example
|
|
133
|
-
* ```ts
|
|
134
|
-
* const arraySet = new ArraySet<number>()
|
|
135
|
-
* arraySet.add(1)
|
|
136
|
-
* arraySet.add(2)
|
|
137
|
-
*
|
|
138
|
-
* for (const item of arraySet) {
|
|
139
|
-
* console.log(item) // 1, 2
|
|
140
|
-
* }
|
|
141
|
-
*
|
|
142
|
-
* const items = [...arraySet] // [1, 2]
|
|
143
|
-
* ```
|
|
144
85
|
*/
|
|
145
86
|
*[Symbol.iterator]() {
|
|
146
|
-
if (this.
|
|
87
|
+
if (this.set) {
|
|
88
|
+
yield* this.set;
|
|
89
|
+
} else if (this.array) {
|
|
147
90
|
for (let i = 0; i < this.arraySize; i++) {
|
|
148
|
-
|
|
149
|
-
if (typeof elem !== "undefined") {
|
|
150
|
-
yield elem;
|
|
151
|
-
}
|
|
91
|
+
yield this.array[i];
|
|
152
92
|
}
|
|
153
|
-
} else if (this.set) {
|
|
154
|
-
yield* this.set;
|
|
155
|
-
} else {
|
|
156
|
-
throw new Error("no set or array");
|
|
157
93
|
}
|
|
158
94
|
}
|
|
159
95
|
/**
|
|
160
96
|
* Check whether an element is present in the ArraySet.
|
|
161
|
-
*
|
|
162
|
-
* @param elem - The element to check for
|
|
163
|
-
* @returns `true` if the element is present, `false` otherwise
|
|
164
|
-
* @example
|
|
165
|
-
* ```ts
|
|
166
|
-
* const arraySet = new ArraySet<string>()
|
|
167
|
-
* arraySet.add('hello')
|
|
168
|
-
*
|
|
169
|
-
* console.log(arraySet.has('hello')) // true
|
|
170
|
-
* console.log(arraySet.has('world')) // false
|
|
171
|
-
* ```
|
|
172
97
|
*/
|
|
173
98
|
has(elem) {
|
|
174
|
-
if (this.
|
|
175
|
-
return this.array.indexOf(elem) !== -1;
|
|
176
|
-
} else {
|
|
99
|
+
if (this.set) {
|
|
177
100
|
return this.set.has(elem);
|
|
178
101
|
}
|
|
102
|
+
return this.array ? this.array.indexOf(elem) !== -1 : false;
|
|
179
103
|
}
|
|
180
104
|
/**
|
|
181
105
|
* Remove all elements from the ArraySet.
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* ```ts
|
|
185
|
-
* const arraySet = new ArraySet<string>()
|
|
186
|
-
* arraySet.add('hello')
|
|
187
|
-
* arraySet.add('world')
|
|
188
|
-
*
|
|
189
|
-
* arraySet.clear()
|
|
190
|
-
* console.log(arraySet.size()) // 0
|
|
191
|
-
* ```
|
|
192
106
|
*/
|
|
193
107
|
clear() {
|
|
194
108
|
if (this.set) {
|
|
195
109
|
this.set.clear();
|
|
196
|
-
} else {
|
|
110
|
+
} else if (this.array) {
|
|
111
|
+
this.array.fill(void 0, 0, this.arraySize);
|
|
197
112
|
this.arraySize = 0;
|
|
198
|
-
this.array = [];
|
|
199
113
|
}
|
|
200
114
|
}
|
|
201
115
|
/**
|
|
202
116
|
* Get the number of elements in the ArraySet.
|
|
203
|
-
*
|
|
204
|
-
* @returns The number of elements in the set
|
|
205
|
-
* @example
|
|
206
|
-
* ```ts
|
|
207
|
-
* const arraySet = new ArraySet<string>()
|
|
208
|
-
* console.log(arraySet.size()) // 0
|
|
209
|
-
*
|
|
210
|
-
* arraySet.add('hello')
|
|
211
|
-
* console.log(arraySet.size()) // 1
|
|
212
|
-
* ```
|
|
213
117
|
*/
|
|
214
118
|
size() {
|
|
215
119
|
if (this.set) {
|
|
216
120
|
return this.set.size;
|
|
217
|
-
} else {
|
|
218
|
-
return this.arraySize;
|
|
219
121
|
}
|
|
122
|
+
return this.arraySize;
|
|
220
123
|
}
|
|
221
124
|
}
|
|
222
125
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/ArraySet.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * The number of items an ArraySet holds in array mode before switching to a Set.\n * Exported only for tests.\n * @internal\n */\nexport const ARRAY_SIZE_THRESHOLD = 8\n\n/**\n * An ArraySet operates as an array until it reaches a certain size, after which a Set is used\n * instead. In either case, the same methods are used to get, set, remove, and visit the items.\n *\n * `set` and `array` are never both non-null. `set` being null means array mode, but the array\n * itself is only allocated on the first `add` (most signals never get a child, and an empty\n * ArraySet is created for every atom and effect, and two for every computed), so array-mode code\n * must handle `array === null`; `arraySize` is 0 in that state. Once promoted to a set, an\n * ArraySet never goes back.\n * @internal\n */\nexport class ArraySet<T> {\n\tprivate set: Set<T> | null = null\n\n\t// Slots [0, arraySize) hold the items; slots beyond are undefined. `add`/`has` scan the whole\n\t// array with indexOf, which is why `clear` and `remove` must blank vacated slots.\n\tprivate array: (T | undefined)[] | null = null\n\n\tprivate arraySize = 0\n\n\t/**\n\t * Get whether this ArraySet has any elements.\n\t */\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget isEmpty() {\n\t\tif (this.set) {\n\t\t\treturn this.set.size === 0\n\t\t}\n\n\t\treturn this.arraySize === 0\n\t}\n\n\t/**\n\t * Add an element to the ArraySet if it is not already present.\n\t *\n\t * @returns `true` if the element was added, `false` if it was already present\n\t */\n\tadd(elem: T) {\n\t\tif (this.set) {\n\t\t\tif (this.set.has(elem)) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tthis.set.add(elem)\n\t\t\treturn true\n\t\t}\n\n\t\tif (!this.array) {\n\t\t\tthis.array = Array(ARRAY_SIZE_THRESHOLD)\n\t\t} else if (this.array.indexOf(elem) !== -1) {\n\t\t\treturn false\n\t\t}\n\n\t\tif (this.arraySize < ARRAY_SIZE_THRESHOLD) {\n\t\t\tthis.array[this.arraySize] = elem\n\t\t\tthis.arraySize++\n\n\t\t\treturn true\n\t\t}\n\n\t\t// The array is full: promote to a set.\n\t\tthis.set = new Set(this.array as T[])\n\t\tthis.set.add(elem)\n\t\tthis.array = null\n\t\tthis.arraySize = 0\n\n\t\treturn true\n\t}\n\n\t/**\n\t * Remove an element from the ArraySet if it is present.\n\t *\n\t * @returns `true` if the element was removed, `false` if it was not present\n\t */\n\tremove(elem: T) {\n\t\tif (this.set) {\n\t\t\treturn this.set.delete(elem)\n\t\t}\n\n\t\tif (!this.array) {\n\t\t\treturn false\n\t\t}\n\n\t\tconst idx = this.array.indexOf(elem)\n\n\t\tif (idx === -1) {\n\t\t\treturn false\n\t\t}\n\n\t\tthis.arraySize--\n\n\t\t// Move the last item into the vacated slot so the items stay dense.\n\t\tthis.array[idx] = this.array[this.arraySize]\n\t\tthis.array[this.arraySize] = undefined\n\n\t\treturn true\n\t}\n\n\t/**\n\t * Execute a callback function for each element in the ArraySet.\n\t */\n\tvisit(visitor: (item: T) => void) {\n\t\tif (this.set) {\n\t\t\tthis.set.forEach(visitor)\n\n\t\t\treturn\n\t\t}\n\n\t\tif (!this.array) {\n\t\t\treturn\n\t\t}\n\n\t\tfor (let i = 0; i < this.arraySize; i++) {\n\t\t\tvisitor(this.array[i]!)\n\t\t}\n\t}\n\n\t/**\n\t * Make the ArraySet iterable, allowing it to be used in for...of loops and with spread syntax.\n\t */\n\t*[Symbol.iterator]() {\n\t\tif (this.set) {\n\t\t\tyield* this.set\n\t\t} else if (this.array) {\n\t\t\tfor (let i = 0; i < this.arraySize; i++) {\n\t\t\t\tyield this.array[i]!\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check whether an element is present in the ArraySet.\n\t */\n\thas(elem: T) {\n\t\tif (this.set) {\n\t\t\treturn this.set.has(elem)\n\t\t}\n\n\t\treturn this.array ? this.array.indexOf(elem) !== -1 : false\n\t}\n\n\t/**\n\t * Remove all elements from the ArraySet.\n\t */\n\tclear() {\n\t\tif (this.set) {\n\t\t\tthis.set.clear()\n\t\t} else if (this.array) {\n\t\t\t// Blank the used slots in place rather than allocating a new array: this runs on every\n\t\t\t// computed derive and effect run.\n\t\t\tthis.array.fill(undefined, 0, this.arraySize)\n\t\t\tthis.arraySize = 0\n\t\t}\n\t}\n\n\t/**\n\t * Get the number of elements in the ArraySet.\n\t */\n\tsize() {\n\t\tif (this.set) {\n\t\t\treturn this.set.size\n\t\t}\n\n\t\treturn this.arraySize\n\t}\n}\n"],
|
|
5
|
+
"mappings": "AAKO,MAAM,uBAAuB;AAa7B,MAAM,SAAY;AAAA,EAChB,MAAqB;AAAA;AAAA;AAAA,EAIrB,QAAkC;AAAA,EAElC,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,IAAI,UAAU;AACb,QAAI,KAAK,KAAK;AACb,aAAO,KAAK,IAAI,SAAS;AAAA,IAC1B;AAEA,WAAO,KAAK,cAAc;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,MAAS;AACZ,QAAI,KAAK,KAAK;AACb,UAAI,KAAK,IAAI,IAAI,IAAI,GAAG;AACvB,eAAO;AAAA,MACR;AAEA,WAAK,IAAI,IAAI,IAAI;AACjB,aAAO;AAAA,IACR;AAEA,QAAI,CAAC,KAAK,OAAO;AAChB,WAAK,QAAQ,MAAM,oBAAoB;AAAA,IACxC,WAAW,KAAK,MAAM,QAAQ,IAAI,MAAM,IAAI;AAC3C,aAAO;AAAA,IACR;AAEA,QAAI,KAAK,YAAY,sBAAsB;AAC1C,WAAK,MAAM,KAAK,SAAS,IAAI;AAC7B,WAAK;AAEL,aAAO;AAAA,IACR;AAGA,SAAK,MAAM,IAAI,IAAI,KAAK,KAAY;AACpC,SAAK,IAAI,IAAI,IAAI;AACjB,SAAK,QAAQ;AACb,SAAK,YAAY;AAEjB,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,MAAS;AACf,QAAI,KAAK,KAAK;AACb,aAAO,KAAK,IAAI,OAAO,IAAI;AAAA,IAC5B;AAEA,QAAI,CAAC,KAAK,OAAO;AAChB,aAAO;AAAA,IACR;AAEA,UAAM,MAAM,KAAK,MAAM,QAAQ,IAAI;AAEnC,QAAI,QAAQ,IAAI;AACf,aAAO;AAAA,IACR;AAEA,SAAK;AAGL,SAAK,MAAM,GAAG,IAAI,KAAK,MAAM,KAAK,SAAS;AAC3C,SAAK,MAAM,KAAK,SAAS,IAAI;AAE7B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAA4B;AACjC,QAAI,KAAK,KAAK;AACb,WAAK,IAAI,QAAQ,OAAO;AAExB;AAAA,IACD;AAEA,QAAI,CAAC,KAAK,OAAO;AAChB;AAAA,IACD;AAEA,aAAS,IAAI,GAAG,IAAI,KAAK,WAAW,KAAK;AACxC,cAAQ,KAAK,MAAM,CAAC,CAAE;AAAA,IACvB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,EAAE,OAAO,QAAQ,IAAI;AACpB,QAAI,KAAK,KAAK;AACb,aAAO,KAAK;AAAA,IACb,WAAW,KAAK,OAAO;AACtB,eAAS,IAAI,GAAG,IAAI,KAAK,WAAW,KAAK;AACxC,cAAM,KAAK,MAAM,CAAC;AAAA,MACnB;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAS;AACZ,QAAI,KAAK,KAAK;AACb,aAAO,KAAK,IAAI,IAAI,IAAI;AAAA,IACzB;AAEA,WAAO,KAAK,QAAQ,KAAK,MAAM,QAAQ,IAAI,MAAM,KAAK;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ;AACP,QAAI,KAAK,KAAK;AACb,WAAK,IAAI,MAAM;AAAA,IAChB,WAAW,KAAK,OAAO;AAGtB,WAAK,MAAM,KAAK,QAAW,GAAG,KAAK,SAAS;AAC5C,WAAK,YAAY;AAAA,IAClB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO;AACN,QAAI,KAAK,KAAK;AACb,aAAO,KAAK,IAAI;AAAA,IACjB;AAEA,WAAO,KAAK;AAAA,EACb;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/lib/Atom.mjs
CHANGED
|
@@ -17,30 +17,15 @@ class __Atom__ {
|
|
|
17
17
|
}
|
|
18
18
|
name;
|
|
19
19
|
current;
|
|
20
|
-
/**
|
|
21
|
-
* Custom equality function for comparing values, or null to use default equality.
|
|
22
|
-
* @internal
|
|
23
|
-
*/
|
|
20
|
+
/** @internal */
|
|
24
21
|
isEqual;
|
|
25
|
-
/**
|
|
26
|
-
* Optional function to compute diffs between old and new values.
|
|
27
|
-
* @internal
|
|
28
|
-
*/
|
|
22
|
+
/** @internal */
|
|
29
23
|
computeDiff;
|
|
30
|
-
/**
|
|
31
|
-
* The global epoch when this atom was last changed.
|
|
32
|
-
* @internal
|
|
33
|
-
*/
|
|
24
|
+
/** @internal */
|
|
34
25
|
lastChangedEpoch = getGlobalEpoch();
|
|
35
|
-
/**
|
|
36
|
-
* Set of child signals that depend on this atom.
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
26
|
+
/** @internal */
|
|
39
27
|
children = new ArraySet();
|
|
40
|
-
/**
|
|
41
|
-
* Optional history buffer for tracking changes over time.
|
|
42
|
-
* @internal
|
|
43
|
-
*/
|
|
28
|
+
/** @internal */
|
|
44
29
|
historyBuffer;
|
|
45
30
|
/**
|
|
46
31
|
* Gets the current value without capturing it as a dependency in the current reactive context.
|
|
@@ -84,15 +69,16 @@ class __Atom__ {
|
|
|
84
69
|
if (this.isEqual?.(this.current, value) ?? equals(this.current, value)) {
|
|
85
70
|
return this.current;
|
|
86
71
|
}
|
|
72
|
+
let historyDiff;
|
|
73
|
+
if (this.historyBuffer) {
|
|
74
|
+
historyDiff = diff !== void 0 ? diff : this.computeDiff ? this.computeDiff(this.current, value, this.lastChangedEpoch, getGlobalEpoch() + 1) : RESET_VALUE;
|
|
75
|
+
}
|
|
87
76
|
advanceGlobalEpoch();
|
|
77
|
+
const epoch = getGlobalEpoch();
|
|
88
78
|
if (this.historyBuffer) {
|
|
89
|
-
this.historyBuffer.pushEntry(
|
|
90
|
-
this.lastChangedEpoch,
|
|
91
|
-
getGlobalEpoch(),
|
|
92
|
-
diff ?? this.computeDiff?.(this.current, value, this.lastChangedEpoch, getGlobalEpoch()) ?? RESET_VALUE
|
|
93
|
-
);
|
|
79
|
+
this.historyBuffer.pushEntry(this.lastChangedEpoch, epoch, historyDiff);
|
|
94
80
|
}
|
|
95
|
-
this.lastChangedEpoch =
|
|
81
|
+
this.lastChangedEpoch = epoch;
|
|
96
82
|
const oldValue = this.current;
|
|
97
83
|
this.current = value;
|
|
98
84
|
atomDidChange(this, oldValue);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/Atom.ts"],
|
|
4
|
-
"sourcesContent": ["import { ArraySet } from './ArraySet'\nimport { maybeCaptureParent } from './capture'\nimport { EMPTY_ARRAY, equals, singleton } from './helpers'\nimport { HistoryBuffer } from './HistoryBuffer'\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
|
|
5
|
-
"mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,0BAA0B;AACnC,SAAS,aAAa,QAAQ,iBAAiB;AAC/C,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB,eAAe,sBAAsB;AAClE,SAA6B,mBAA2B;AAmExD,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,EAbiB;AAAA,EACR;AAAA;AAAA
|
|
4
|
+
"sourcesContent": ["import { ArraySet } from './ArraySet'\nimport { maybeCaptureParent } from './capture'\nimport { EMPTY_ARRAY, equals, singleton } from './helpers'\nimport { HistoryBuffer } from './HistoryBuffer'\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 diffs, leave this as `undefined` and no history buffer will be created. Diffs passed to {@link Atom.set} or produced by {@link AtomOptions.computeDiff} are only recorded when this is set.\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 True if the values are equal, false otherwise.\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 implementation of the Atom interface. This class should not be used directly - use the {@link atom} function instead.\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\t/** @internal */\n\treadonly isEqual: null | ((a: any, b: any) => boolean)\n\n\t/** @internal */\n\tcomputeDiff?: ComputeDiff<Value, Diff>\n\n\t/** @internal */\n\tlastChangedEpoch = getGlobalEpoch()\n\n\t/** @internal */\n\tchildren = new ArraySet<Child>()\n\n\t/** @internal */\n\thistoryBuffer?: HistoryBuffer<Diff>\n\n\t/**\n\t * Gets the current value without capturing it as a dependency in the current reactive context.\n\t * This is unsafe because it breaks the reactivity chain - use with caution.\n\t *\n\t * @param _ignoreErrors - Unused parameter for API compatibility\n\t * @returns The current value\n\t * @internal\n\t */\n\t__unsafe__getWithoutCapture(_ignoreErrors?: boolean): Value {\n\t\treturn this.current\n\t}\n\n\t/**\n\t * Gets the current value of this atom. When called within a computed signal or reaction,\n\t * this atom will be automatically captured as a dependency.\n\t *\n\t * @returns The current value\n\t * @example\n\t * ```ts\n\t * const count = atom('count', 5)\n\t * console.log(count.get()) // 5\n\t * ```\n\t */\n\tget() {\n\t\tmaybeCaptureParent(this)\n\t\treturn this.current\n\t}\n\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 * @returns The new value\n\t * @example\n\t * ```ts\n\t * const count = atom('count', 0)\n\t * count.set(5) // count.get() is now 5\n\t * ```\n\t */\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// `computeDiff` is user code: run it before ticking the epoch, so that any signal it reads is\n\t\t// checked against the epoch this write has not yet happened in. Reading a dependent computed\n\t\t// after the tick would stamp it as checked at the new epoch while the atom was still being\n\t\t// written, and it would then never see the change. Only `undefined` means \"no diff\n\t\t// supplied\"; `null` can be a legitimate diff.\n\t\tlet historyDiff: Diff | RESET_VALUE | undefined\n\t\tif (this.historyBuffer) {\n\t\t\thistoryDiff =\n\t\t\t\tdiff !== undefined\n\t\t\t\t\t? diff\n\t\t\t\t\t: this.computeDiff\n\t\t\t\t\t\t? this.computeDiff(this.current, value, this.lastChangedEpoch, getGlobalEpoch() + 1)\n\t\t\t\t\t\t: RESET_VALUE\n\t\t}\n\n\t\t// Tick forward the global epoch. This write belongs to that one epoch, so read it once and\n\t\t// use it everywhere below \u2014 otherwise a `computeDiff` that touches other atoms could leave\n\t\t// the history entry and `lastChangedEpoch` disagreeing.\n\t\tadvanceGlobalEpoch()\n\t\tconst epoch = getGlobalEpoch()\n\n\t\t// Add the diff to the history buffer.\n\t\tif (this.historyBuffer) {\n\t\t\tthis.historyBuffer.pushEntry(this.lastChangedEpoch, epoch, historyDiff)\n\t\t}\n\n\t\t// Update the atom's record of the epoch when last changed.\n\t\tthis.lastChangedEpoch = epoch\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\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 * @returns The new value\n\t * @example\n\t * ```ts\n\t * const count = atom('count', 5)\n\t * count.update(n => n + 1) // count.get() is now 6\n\t * ```\n\t */\n\tupdate(updater: (value: Value) => Value): Value {\n\t\treturn this.set(updater(this.current))\n\t}\n\n\t/**\n\t * Gets all the diffs that have occurred since the given epoch. When called within a computed\n\t * signal or reaction, this atom will be automatically captured as a dependency.\n\t *\n\t * @param epoch - The epoch to get changes since\n\t * @returns An array of diffs, or RESET_VALUE if history is insufficient\n\t * @internal\n\t */\n\tgetDiffSince(epoch: number): RESET_VALUE | Diff[] {\n\t\tmaybeCaptureParent(this)\n\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\n/**\n * Singleton reference to the Atom constructor. Used internally to create atom instances.\n * @internal\n */\nexport const _Atom = singleton('Atom', () => __Atom__)\n\n/**\n * Type alias for instances of the internal Atom class.\n * @internal\n */\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 *\n * @param value - The value to check\n * @returns True if the value is an Atom, false otherwise\n * @example\n * ```ts\n * const myAtom = atom('test', 42)\n * const notAtom = 'hello'\n *\n * console.log(isAtom(myAtom)) // true\n * console.log(isAtom(notAtom)) // false\n * ```\n * @public\n */\nexport function isAtom(value: unknown): value is Atom<unknown> {\n\treturn value instanceof _Atom\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,0BAA0B;AACnC,SAAS,aAAa,QAAQ,iBAAiB;AAC/C,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB,eAAe,sBAAsB;AAClE,SAA6B,mBAA2B;AAmExD,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,EAbiB;AAAA,EACR;AAAA;AAAA,EAeA;AAAA;AAAA,EAGT;AAAA;AAAA,EAGA,mBAAmB,eAAe;AAAA;AAAA,EAGlC,WAAW,IAAI,SAAgB;AAAA;AAAA,EAG/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,4BAA4B,eAAgC;AAC3D,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM;AACL,uBAAmB,IAAI;AACvB,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,IAAI,OAAc,MAAoB;AAErC,QAAI,KAAK,UAAU,KAAK,SAAS,KAAK,KAAK,OAAO,KAAK,SAAS,KAAK,GAAG;AACvE,aAAO,KAAK;AAAA,IACb;AAOA,QAAI;AACJ,QAAI,KAAK,eAAe;AACvB,oBACC,SAAS,SACN,OACA,KAAK,cACJ,KAAK,YAAY,KAAK,SAAS,OAAO,KAAK,kBAAkB,eAAe,IAAI,CAAC,IACjF;AAAA,IACN;AAKA,uBAAmB;AACnB,UAAM,QAAQ,eAAe;AAG7B,QAAI,KAAK,eAAe;AACvB,WAAK,cAAc,UAAU,KAAK,kBAAkB,OAAO,WAAW;AAAA,IACvE;AAGA,SAAK,mBAAmB;AAExB,UAAM,WAAW,KAAK;AACtB,SAAK,UAAU;AAGf,kBAAc,MAAa,QAAQ;AAEnC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,OAAO,SAAyC;AAC/C,WAAO,KAAK,IAAI,QAAQ,KAAK,OAAO,CAAC;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,aAAa,OAAqC;AACjD,uBAAmB,IAAI;AAEvB,QAAI,SAAS,KAAK,kBAAkB;AACnC,aAAO;AAAA,IACR;AAEA,WAAO,KAAK,eAAe,gBAAgB,KAAK,KAAK;AAAA,EACtD;AACD;AAMO,MAAM,QAAQ,UAAU,QAAQ,MAAM,QAAQ;AA0B9C,SAAS,KAIf,MAIA,cAIA,SACoB;AACpB,SAAO,IAAI,MAAM,MAAM,cAAc,OAAO;AAC7C;AAiBO,SAAS,OAAO,OAAwC;AAC9D,SAAO,iBAAiB;AACzB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -80,27 +80,29 @@ class __UNSAFE__Computed {
|
|
|
80
80
|
const result = this.derive(this.state, this.lastCheckedEpoch);
|
|
81
81
|
const newState = result instanceof WithDiff ? result.value : result;
|
|
82
82
|
const isUninitialized2 = this.state === UNINITIALIZED;
|
|
83
|
+
const epoch = getGlobalEpoch();
|
|
83
84
|
if (isUninitialized2 || !this.isEqual(this.state, newState)) {
|
|
84
85
|
if (this.historyBuffer && !isUninitialized2) {
|
|
85
86
|
const diff = result instanceof WithDiff ? result.diff : void 0;
|
|
86
87
|
this.historyBuffer.pushEntry(
|
|
87
88
|
this.lastChangedEpoch,
|
|
88
|
-
|
|
89
|
-
diff
|
|
89
|
+
epoch,
|
|
90
|
+
diff !== void 0 ? diff : this.computeDiff ? this.computeDiff(this.state, newState, this.lastCheckedEpoch, epoch) : RESET_VALUE
|
|
90
91
|
);
|
|
91
92
|
}
|
|
92
|
-
this.lastChangedEpoch =
|
|
93
|
+
this.lastChangedEpoch = epoch;
|
|
93
94
|
this.state = newState;
|
|
94
95
|
}
|
|
95
96
|
this.error = null;
|
|
96
|
-
this.lastCheckedEpoch =
|
|
97
|
+
this.lastCheckedEpoch = epoch;
|
|
97
98
|
return this.state;
|
|
98
99
|
} catch (e) {
|
|
99
|
-
|
|
100
|
+
const epoch = getGlobalEpoch();
|
|
101
|
+
if (this.error === null) {
|
|
100
102
|
this.state = UNINITIALIZED;
|
|
101
|
-
this.lastChangedEpoch =
|
|
103
|
+
this.lastChangedEpoch = epoch;
|
|
102
104
|
}
|
|
103
|
-
this.lastCheckedEpoch =
|
|
105
|
+
this.lastCheckedEpoch = epoch;
|
|
104
106
|
if (this.historyBuffer) {
|
|
105
107
|
this.historyBuffer.clear();
|
|
106
108
|
}
|
|
@@ -128,13 +130,13 @@ class __UNSAFE__Computed {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
const _Computed = singleton("Computed", () => __UNSAFE__Computed);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const derivationKey = /* @__PURE__ */ Symbol
|
|
134
|
-
|
|
133
|
+
const derivationKeyKey = "@@__computedDerivationKey__@@";
|
|
134
|
+
function makeComputedWrapper(name, compute, options) {
|
|
135
|
+
const derivationKey = /* @__PURE__ */ Symbol("__@tldraw/state__computed__" + name);
|
|
136
|
+
const wrapper = function() {
|
|
135
137
|
let d = this[derivationKey];
|
|
136
138
|
if (!d) {
|
|
137
|
-
d = new _Computed(
|
|
139
|
+
d = new _Computed(name, compute.bind(this), options);
|
|
138
140
|
Object.defineProperty(this, derivationKey, {
|
|
139
141
|
enumerable: false,
|
|
140
142
|
configurable: false,
|
|
@@ -144,75 +146,45 @@ function computedMethodLegacyDecorator(options = {}, _target, key, descriptor) {
|
|
|
144
146
|
}
|
|
145
147
|
return d.get();
|
|
146
148
|
};
|
|
147
|
-
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
function computedGetterLegacyDecorator(options = {}, _target, key, descriptor) {
|
|
151
|
-
const originalMethod = descriptor.get;
|
|
152
|
-
const derivationKey = /* @__PURE__ */ Symbol.for("__@tldraw/state__computed__" + key);
|
|
153
|
-
descriptor.get = function() {
|
|
154
|
-
let d = this[derivationKey];
|
|
155
|
-
if (!d) {
|
|
156
|
-
d = new _Computed(key, originalMethod.bind(this), options);
|
|
157
|
-
Object.defineProperty(this, derivationKey, {
|
|
158
|
-
enumerable: false,
|
|
159
|
-
configurable: false,
|
|
160
|
-
writable: false,
|
|
161
|
-
value: d
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
return d.get();
|
|
165
|
-
};
|
|
166
|
-
return descriptor;
|
|
167
|
-
}
|
|
168
|
-
function computedMethodTc39Decorator(options, compute, context) {
|
|
169
|
-
assert(context.kind === "method", "@computed can only be used on methods");
|
|
170
|
-
const derivationKey = /* @__PURE__ */ Symbol.for("__@tldraw/state__computed__" + String(context.name));
|
|
171
|
-
const fn = function() {
|
|
172
|
-
let d = this[derivationKey];
|
|
173
|
-
if (!d) {
|
|
174
|
-
d = new _Computed(String(context.name), compute.bind(this), options);
|
|
175
|
-
Object.defineProperty(this, derivationKey, {
|
|
176
|
-
enumerable: false,
|
|
177
|
-
configurable: false,
|
|
178
|
-
writable: false,
|
|
179
|
-
value: d
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
return d.get();
|
|
183
|
-
};
|
|
184
|
-
fn[isComputedMethodKey] = true;
|
|
185
|
-
return fn;
|
|
149
|
+
wrapper[derivationKeyKey] = derivationKey;
|
|
150
|
+
return wrapper;
|
|
186
151
|
}
|
|
187
152
|
function computedDecorator(options = {}, args) {
|
|
188
153
|
if (args.length === 2) {
|
|
189
154
|
const [originalMethod, context] = args;
|
|
190
|
-
|
|
155
|
+
assert(context.kind === "method", "@computed can only be used on methods");
|
|
156
|
+
return makeComputedWrapper(String(context.name), originalMethod, options);
|
|
191
157
|
} else {
|
|
192
158
|
const [_target, key, descriptor] = args;
|
|
193
159
|
if (descriptor.get) {
|
|
194
160
|
logComputedGetterWarning();
|
|
195
|
-
|
|
161
|
+
descriptor.get = makeComputedWrapper(key, descriptor.get, options);
|
|
196
162
|
} else {
|
|
197
|
-
|
|
163
|
+
descriptor.value = makeComputedWrapper(key, descriptor.value, options);
|
|
198
164
|
}
|
|
165
|
+
return descriptor;
|
|
199
166
|
}
|
|
200
167
|
}
|
|
201
|
-
const isComputedMethodKey = "@@__isComputedMethod__@@";
|
|
202
168
|
function getComputedInstance(obj, propertyName) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
208
|
-
|
|
169
|
+
for (let proto = obj; proto; proto = Object.getPrototypeOf(proto)) {
|
|
170
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, propertyName);
|
|
171
|
+
const wrapper = descriptor?.get ?? descriptor?.value;
|
|
172
|
+
const key = wrapper?.[derivationKeyKey];
|
|
173
|
+
if (!key) continue;
|
|
174
|
+
let inst = obj[key];
|
|
175
|
+
if (!inst) {
|
|
176
|
+
try {
|
|
177
|
+
wrapper.call(obj);
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
inst = obj[key];
|
|
209
181
|
}
|
|
210
|
-
inst
|
|
182
|
+
return inst;
|
|
211
183
|
}
|
|
212
|
-
return
|
|
184
|
+
return void 0;
|
|
213
185
|
}
|
|
214
186
|
function computed() {
|
|
215
|
-
if (arguments.length
|
|
187
|
+
if (arguments.length <= 1) {
|
|
216
188
|
const options = arguments[0];
|
|
217
189
|
return (...args) => computedDecorator(options, args);
|
|
218
190
|
} else if (typeof arguments[0] === "string") {
|