@oscarpalmer/mora 0.22.0 → 0.23.0
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/dist/batch.js +8 -9
- package/dist/constants.js +34 -0
- package/dist/effect.js +7 -8
- package/dist/helpers/is.js +8 -18
- package/dist/helpers/proxy.js +3 -2
- package/dist/helpers/value.js +7 -8
- package/dist/index.js +1 -1
- package/dist/models.js +0 -0
- package/dist/mora.full.js +218 -130
- package/dist/subscription.js +7 -5
- package/dist/value/array.js +24 -24
- package/dist/value/computed.js +18 -21
- package/dist/value/signal.js +2 -2
- package/dist/value/store.js +8 -2
- package/package.json +9 -10
- package/src/batch.ts +11 -13
- package/src/constants.ts +49 -0
- package/src/effect.ts +11 -16
- package/src/helpers/is.ts +41 -19
- package/src/helpers/proxy.ts +34 -38
- package/src/helpers/value.ts +19 -14
- package/src/index.ts +1 -0
- package/src/models.ts +66 -0
- package/src/subscription.ts +14 -16
- package/src/value/array.ts +86 -44
- package/src/value/computed.ts +29 -39
- package/src/value/reactive.ts +9 -24
- package/src/value/signal.ts +9 -3
- package/src/value/store.ts +40 -9
- package/types/batch.d.ts +3 -5
- package/types/constants.d.ts +14 -0
- package/types/effect.d.ts +2 -1
- package/types/helpers/is.d.ts +23 -10
- package/types/helpers/proxy.d.ts +2 -3
- package/types/helpers/value.d.ts +1 -1
- package/types/index.d.ts +1 -0
- package/types/models.d.ts +56 -0
- package/types/subscription.d.ts +3 -6
- package/types/value/array.d.ts +48 -7
- package/types/value/computed.d.ts +2 -11
- package/types/value/reactive.d.ts +8 -17
- package/types/value/signal.d.ts +7 -1
- package/types/value/store.d.ts +28 -4
package/dist/mora.full.js
CHANGED
|
@@ -1,20 +1,92 @@
|
|
|
1
|
+
const ACTIVE = {};
|
|
2
|
+
const ARRAY_THRESHOLD = 100;
|
|
3
|
+
const ARRAY_OFFSET = 25;
|
|
4
|
+
const ARRAY_PEEK = 10;
|
|
5
|
+
const BATCH = {
|
|
6
|
+
depth: 0,
|
|
7
|
+
handlers: new Set(),
|
|
8
|
+
};
|
|
9
|
+
const METHODS_AFFECTING_LENGTH = new Set([
|
|
10
|
+
'pop',
|
|
11
|
+
'push',
|
|
12
|
+
'shift',
|
|
13
|
+
'unshift',
|
|
14
|
+
]);
|
|
15
|
+
const METHODS_UPDATE = new Set([
|
|
16
|
+
...METHODS_AFFECTING_LENGTH,
|
|
17
|
+
'copyWithin',
|
|
18
|
+
'fill',
|
|
19
|
+
'reverse',
|
|
20
|
+
'sort',
|
|
21
|
+
'splice',
|
|
22
|
+
]);
|
|
23
|
+
const NAME_ARRAY = 'array';
|
|
24
|
+
const NAME_COMPUTED = 'computed';
|
|
25
|
+
const NAME_EFFECT = 'effect';
|
|
26
|
+
const NAME_SIGNAL = 'signal';
|
|
27
|
+
const NAME_STORE = 'store';
|
|
28
|
+
const NAMES = new Set([
|
|
29
|
+
NAME_ARRAY,
|
|
30
|
+
NAME_COMPUTED,
|
|
31
|
+
NAME_SIGNAL,
|
|
32
|
+
NAME_STORE,
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
class Effect {
|
|
36
|
+
constructor(callback) {
|
|
37
|
+
Object.defineProperty(this, '$mora', {
|
|
38
|
+
value: NAME_EFFECT,
|
|
39
|
+
});
|
|
40
|
+
this.state = {
|
|
41
|
+
callback,
|
|
42
|
+
};
|
|
43
|
+
runEffect(this);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function runEffect(effect) {
|
|
47
|
+
const previousEffect = ACTIVE.effect;
|
|
48
|
+
ACTIVE.effect = effect;
|
|
49
|
+
try {
|
|
50
|
+
effect.state.callback();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
ACTIVE.effect = previousEffect;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create an effect
|
|
58
|
+
* @param callback Callback for handling signal effects
|
|
59
|
+
* @returns Effect
|
|
60
|
+
*/
|
|
61
|
+
function effect(callback) {
|
|
62
|
+
return typeof callback === 'function'
|
|
63
|
+
? new Effect(callback)
|
|
64
|
+
: undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
1
67
|
/**
|
|
2
68
|
* Is the value a reactive array?
|
|
69
|
+
* @param value Value to check
|
|
70
|
+
* @returns True if value is a {@link ReactiveArray}
|
|
3
71
|
*/
|
|
4
72
|
function isArray(value) {
|
|
5
|
-
return isMora(value,
|
|
73
|
+
return isMora(value, NAME_ARRAY);
|
|
6
74
|
}
|
|
7
75
|
/**
|
|
8
76
|
* Is the value a computed signal?
|
|
77
|
+
* @param value Value to check
|
|
78
|
+
* @returns True if value is a {@link Computed}
|
|
9
79
|
*/
|
|
10
80
|
function isComputed(value) {
|
|
11
|
-
return isMora(value,
|
|
81
|
+
return isMora(value, NAME_COMPUTED);
|
|
12
82
|
}
|
|
13
83
|
/**
|
|
14
84
|
* Is the value an effect?
|
|
85
|
+
* @param value Value to check
|
|
86
|
+
* @returns True if value is an {@link Effect}
|
|
15
87
|
*/
|
|
16
88
|
function isEffect(value) {
|
|
17
|
-
return isMora(value,
|
|
89
|
+
return isMora(value, NAME_EFFECT);
|
|
18
90
|
}
|
|
19
91
|
function isMora(value, name) {
|
|
20
92
|
return (typeof value === 'object' &&
|
|
@@ -24,55 +96,27 @@ function isMora(value, name) {
|
|
|
24
96
|
? value.$mora === name
|
|
25
97
|
: name.has(value.$mora)));
|
|
26
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Is the value reactive?
|
|
101
|
+
* @param value Value to check
|
|
102
|
+
* @returns True if value is a {@link Reactive}
|
|
103
|
+
*/
|
|
27
104
|
function isReactive(value) {
|
|
28
|
-
return isMora(value,
|
|
105
|
+
return isMora(value, NAMES);
|
|
29
106
|
}
|
|
30
107
|
/**
|
|
31
108
|
* Is the value a signal?
|
|
109
|
+
* @param value Value to check
|
|
110
|
+
* @returns True if value is a {@link Signal}
|
|
32
111
|
*/
|
|
33
112
|
function isSignal(value) {
|
|
34
|
-
return isMora(value,
|
|
35
|
-
}
|
|
36
|
-
const arrayName = 'array';
|
|
37
|
-
const computedName = 'computed';
|
|
38
|
-
const effectName = 'effect';
|
|
39
|
-
const signalName = 'signal';
|
|
40
|
-
const storeName = 'store';
|
|
41
|
-
const reactiveNames = new Set([arrayName, computedName, signalName, storeName]);
|
|
42
|
-
|
|
43
|
-
class Effect {
|
|
44
|
-
constructor(callback) {
|
|
45
|
-
Object.defineProperty(this, '$mora', {
|
|
46
|
-
value: effectName,
|
|
47
|
-
});
|
|
48
|
-
this.state = {
|
|
49
|
-
callback,
|
|
50
|
-
};
|
|
51
|
-
runEffect(this);
|
|
52
|
-
}
|
|
113
|
+
return isMora(value, NAME_SIGNAL);
|
|
53
114
|
}
|
|
54
|
-
function runEffect(effect) {
|
|
55
|
-
const previousEffect = activeEffect;
|
|
56
|
-
activeEffect = effect;
|
|
57
|
-
try {
|
|
58
|
-
effect.state.callback();
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
activeEffect = previousEffect;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Create an effect
|
|
66
|
-
*/
|
|
67
|
-
function effect(callback) {
|
|
68
|
-
return new Effect(callback);
|
|
69
|
-
}
|
|
70
|
-
let activeEffect;
|
|
71
115
|
|
|
72
116
|
function flushHandlers() {
|
|
73
|
-
while (
|
|
74
|
-
const handlers = [...
|
|
75
|
-
|
|
117
|
+
while (BATCH.depth === 0 && BATCH.handlers.size > 0) {
|
|
118
|
+
const handlers = [...BATCH.handlers];
|
|
119
|
+
BATCH.handlers.clear();
|
|
76
120
|
for (const handler of handlers) {
|
|
77
121
|
if (isEffect(handler)) {
|
|
78
122
|
runEffect(handler);
|
|
@@ -84,31 +128,35 @@ function flushHandlers() {
|
|
|
84
128
|
}
|
|
85
129
|
}
|
|
86
130
|
/**
|
|
87
|
-
* Start batching effects
|
|
131
|
+
* Start batching effects
|
|
132
|
+
*
|
|
133
|
+
* _(Use {@link stopBatch} to flush and run batched effects)_
|
|
88
134
|
*/
|
|
89
135
|
function startBatch() {
|
|
90
|
-
|
|
136
|
+
BATCH.depth += 1;
|
|
91
137
|
}
|
|
92
138
|
/**
|
|
93
139
|
* Stop batching effects and flush _(run)_ them
|
|
94
140
|
*/
|
|
95
141
|
function stopBatch() {
|
|
96
|
-
if (
|
|
97
|
-
|
|
142
|
+
if (BATCH.depth > 0) {
|
|
143
|
+
BATCH.depth -= 1;
|
|
98
144
|
}
|
|
99
145
|
flushHandlers();
|
|
100
146
|
}
|
|
101
|
-
const batchedHandlers = new Set();
|
|
102
|
-
let batchDepth = 0;
|
|
103
147
|
|
|
104
148
|
class Subscription {
|
|
105
|
-
state;
|
|
106
149
|
callback;
|
|
150
|
+
state;
|
|
107
151
|
constructor(state, callback) {
|
|
108
152
|
this.state = state;
|
|
109
153
|
this.callback = callback;
|
|
110
154
|
callback(state.value);
|
|
111
155
|
}
|
|
156
|
+
destroy() {
|
|
157
|
+
this.callback = noop;
|
|
158
|
+
this.state = undefined;
|
|
159
|
+
}
|
|
112
160
|
}
|
|
113
161
|
function noop() { }
|
|
114
162
|
function subscribe(state, callback) {
|
|
@@ -121,12 +169,8 @@ function subscribe(state, callback) {
|
|
|
121
169
|
};
|
|
122
170
|
}
|
|
123
171
|
function unsubscribe(state, callback) {
|
|
124
|
-
|
|
172
|
+
state.subscriptions.get(callback)?.destroy();
|
|
125
173
|
state.subscriptions.delete(callback);
|
|
126
|
-
if (subscription != null) {
|
|
127
|
-
subscription.callback = noop;
|
|
128
|
-
subscription.state = undefined;
|
|
129
|
-
}
|
|
130
174
|
}
|
|
131
175
|
|
|
132
176
|
class Reactive {
|
|
@@ -148,77 +192,83 @@ class Reactive {
|
|
|
148
192
|
}
|
|
149
193
|
/**
|
|
150
194
|
* Get the value _(without reactivity)_
|
|
195
|
+
* @return Current value
|
|
151
196
|
*/
|
|
152
197
|
peek() {
|
|
153
198
|
return this.state.value;
|
|
154
199
|
}
|
|
155
200
|
/**
|
|
156
201
|
* Subscribe to changes
|
|
202
|
+
* @param callback Callback for changes
|
|
203
|
+
* @return Unsubscribe callback
|
|
157
204
|
*/
|
|
158
205
|
subscribe(callback) {
|
|
159
206
|
return subscribe(this.state, callback);
|
|
160
207
|
}
|
|
161
208
|
/**
|
|
162
209
|
* JSON representation of the value
|
|
210
|
+
* @return JSON value
|
|
163
211
|
*/
|
|
164
212
|
toJSON() {
|
|
165
213
|
return this.get();
|
|
166
214
|
}
|
|
167
215
|
/**
|
|
168
216
|
* String representation of the value
|
|
217
|
+
* @return Value as string
|
|
169
218
|
*/
|
|
170
219
|
toString() {
|
|
171
220
|
return String(this.get());
|
|
172
221
|
}
|
|
173
222
|
/**
|
|
174
223
|
* Unsubscribe from changes
|
|
224
|
+
* @param callback Callback to unsubscribe
|
|
175
225
|
*/
|
|
176
226
|
unsubscribe(callback) {
|
|
177
227
|
unsubscribe(this.state, callback);
|
|
178
228
|
}
|
|
179
229
|
}
|
|
180
230
|
|
|
181
|
-
let activeComputed;
|
|
182
231
|
class Computed extends Reactive {
|
|
183
232
|
effect = {
|
|
184
233
|
dirty: true,
|
|
185
234
|
instance: undefined,
|
|
186
235
|
};
|
|
187
236
|
constructor(callback, options) {
|
|
188
|
-
super(
|
|
237
|
+
super(NAME_COMPUTED, undefined, options);
|
|
189
238
|
this.effect.instance = effect(() => {
|
|
190
|
-
if (this.effect.dirty) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
239
|
+
if (!this.effect.dirty) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const previousComputed = ACTIVE.computed;
|
|
243
|
+
ACTIVE.computed = this;
|
|
244
|
+
const value = callback();
|
|
245
|
+
ACTIVE.computed = previousComputed;
|
|
246
|
+
if (!this.state.equal(this.state.value, value)) {
|
|
247
|
+
this.state.value = value;
|
|
248
|
+
for (const computed of this.state.computeds) {
|
|
249
|
+
computed.effect.dirty = true;
|
|
250
|
+
}
|
|
251
|
+
for (const effect of this.state.effects) {
|
|
252
|
+
BATCH.handlers.add(effect);
|
|
253
|
+
}
|
|
254
|
+
for (const [, subscription] of this.state.subscriptions) {
|
|
255
|
+
subscription.callback(value);
|
|
206
256
|
}
|
|
207
|
-
this.effect.dirty = false;
|
|
208
257
|
}
|
|
258
|
+
this.effect.dirty = false;
|
|
209
259
|
});
|
|
210
260
|
}
|
|
211
261
|
/**
|
|
212
262
|
* @inheritdoc
|
|
213
263
|
*/
|
|
214
264
|
get() {
|
|
215
|
-
if (
|
|
216
|
-
this.state.computeds.add(
|
|
265
|
+
if (ACTIVE.computed != null && this !== ACTIVE.computed) {
|
|
266
|
+
this.state.computeds.add(ACTIVE.computed);
|
|
217
267
|
}
|
|
218
|
-
if (
|
|
219
|
-
this.state.effects.add(
|
|
268
|
+
if (ACTIVE.effect != null && ACTIVE.effect !== this.effect.instance) {
|
|
269
|
+
this.state.effects.add(ACTIVE.effect);
|
|
220
270
|
}
|
|
221
|
-
if (this.effect.dirty &&
|
|
271
|
+
if (this.effect.dirty && BATCH.depth === 0) {
|
|
222
272
|
runEffect(this.effect.instance);
|
|
223
273
|
}
|
|
224
274
|
return this.state.value;
|
|
@@ -236,12 +286,12 @@ function emitValue(state) {
|
|
|
236
286
|
computed.effect.dirty = true;
|
|
237
287
|
}
|
|
238
288
|
for (const effect of state.effects) {
|
|
239
|
-
|
|
289
|
+
BATCH.handlers.add(effect);
|
|
240
290
|
}
|
|
241
291
|
for (const [, subscription] of state.subscriptions) {
|
|
242
|
-
|
|
292
|
+
BATCH.handlers.add(subscription);
|
|
243
293
|
}
|
|
244
|
-
if (
|
|
294
|
+
if (BATCH.depth === 0) {
|
|
245
295
|
flushHandlers();
|
|
246
296
|
}
|
|
247
297
|
}
|
|
@@ -251,9 +301,9 @@ function equalArrays(state, first, second) {
|
|
|
251
301
|
return false;
|
|
252
302
|
}
|
|
253
303
|
let offset = 0;
|
|
254
|
-
if (length >=
|
|
255
|
-
offset = Math.round(length /
|
|
256
|
-
offset = offset >
|
|
304
|
+
if (length >= ARRAY_THRESHOLD) {
|
|
305
|
+
offset = Math.round(length / ARRAY_PEEK);
|
|
306
|
+
offset = offset > ARRAY_OFFSET ? ARRAY_OFFSET : offset;
|
|
257
307
|
for (let index = 0; index < offset; index += 1) {
|
|
258
308
|
if (!state.equal(first[index], second[index])) {
|
|
259
309
|
return false;
|
|
@@ -269,11 +319,11 @@ function equalArrays(state, first, second) {
|
|
|
269
319
|
return true;
|
|
270
320
|
}
|
|
271
321
|
function getValue(state) {
|
|
272
|
-
if (
|
|
273
|
-
state.computeds.add(
|
|
322
|
+
if (ACTIVE.computed != null) {
|
|
323
|
+
state.computeds.add(ACTIVE.computed);
|
|
274
324
|
}
|
|
275
|
-
if (
|
|
276
|
-
state.effects.add(
|
|
325
|
+
if (ACTIVE.effect != null) {
|
|
326
|
+
state.effects.add(ACTIVE.effect);
|
|
277
327
|
}
|
|
278
328
|
return state.value;
|
|
279
329
|
}
|
|
@@ -312,11 +362,12 @@ function setProxyValue(proxy, value) {
|
|
|
312
362
|
}
|
|
313
363
|
stopBatch();
|
|
314
364
|
}
|
|
315
|
-
function setValueInProxy(
|
|
365
|
+
function setValueInProxy(parameters) {
|
|
366
|
+
const { isArray, length, property, state, target, value } = parameters;
|
|
316
367
|
if (isArray) {
|
|
317
368
|
const isIndex = !Number.isNaN(Number(property));
|
|
318
369
|
const isLength = property === 'length';
|
|
319
|
-
if (!isIndex
|
|
370
|
+
if (!(isIndex || isLength)) {
|
|
320
371
|
return Reflect.set(target, property, value);
|
|
321
372
|
}
|
|
322
373
|
}
|
|
@@ -333,7 +384,7 @@ function setValueInProxy(target, property, value, state, isArray, length) {
|
|
|
333
384
|
|
|
334
385
|
class Signal extends Reactive {
|
|
335
386
|
constructor(value, options) {
|
|
336
|
-
super(
|
|
387
|
+
super(NAME_SIGNAL, value, options);
|
|
337
388
|
}
|
|
338
389
|
/**
|
|
339
390
|
* @inheritdoc
|
|
@@ -343,6 +394,7 @@ class Signal extends Reactive {
|
|
|
343
394
|
}
|
|
344
395
|
/**
|
|
345
396
|
* Set the value
|
|
397
|
+
* @param value New value
|
|
346
398
|
*/
|
|
347
399
|
set(value) {
|
|
348
400
|
if (!this.state.equal(this.state.value, value)) {
|
|
@@ -352,6 +404,7 @@ class Signal extends Reactive {
|
|
|
352
404
|
}
|
|
353
405
|
/**
|
|
354
406
|
* Update the value _(based on the current value)_
|
|
407
|
+
* @param callback Callback to update the value
|
|
355
408
|
*/
|
|
356
409
|
update(callback) {
|
|
357
410
|
this.set(callback(this.state.value));
|
|
@@ -359,6 +412,9 @@ class Signal extends Reactive {
|
|
|
359
412
|
}
|
|
360
413
|
/**
|
|
361
414
|
* Create a reactive value
|
|
415
|
+
* @param value Initial value
|
|
416
|
+
* @param options Optional reactivity options
|
|
417
|
+
* @returns Reactive value
|
|
362
418
|
*/
|
|
363
419
|
function signal(value, options) {
|
|
364
420
|
return new Signal(value, options);
|
|
@@ -384,11 +440,18 @@ class ReactiveArray extends Reactive {
|
|
|
384
440
|
}
|
|
385
441
|
}
|
|
386
442
|
constructor(value, options) {
|
|
387
|
-
super(
|
|
388
|
-
get: (target, property) =>
|
|
443
|
+
super(NAME_ARRAY, new Proxy(value, {
|
|
444
|
+
get: (target, property) => METHODS_UPDATE.has(property)
|
|
389
445
|
? updateArray(property, target, this.state, this.#size)
|
|
390
446
|
: Reflect.get(target, property),
|
|
391
|
-
set: (target, property, value) => setValueInProxy(
|
|
447
|
+
set: (target, property, value) => setValueInProxy({
|
|
448
|
+
property,
|
|
449
|
+
target,
|
|
450
|
+
value,
|
|
451
|
+
isArray: true,
|
|
452
|
+
state: this.state,
|
|
453
|
+
length: this.#size,
|
|
454
|
+
}),
|
|
392
455
|
}), options);
|
|
393
456
|
this.#size.set(value.length);
|
|
394
457
|
}
|
|
@@ -398,6 +461,14 @@ class ReactiveArray extends Reactive {
|
|
|
398
461
|
clear() {
|
|
399
462
|
this.length = 0;
|
|
400
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Create a computed, filtered array
|
|
466
|
+
* @param callback Callback to evaluate each item
|
|
467
|
+
* @return Computed array of filtered items
|
|
468
|
+
*/
|
|
469
|
+
filter(callback) {
|
|
470
|
+
return computed(() => this.get().filter(callback));
|
|
471
|
+
}
|
|
401
472
|
get(first) {
|
|
402
473
|
if (typeof first === 'number') {
|
|
403
474
|
return getReactiveValueInProxy(this, this.#indiced, first, true).get();
|
|
@@ -407,20 +478,25 @@ class ReactiveArray extends Reactive {
|
|
|
407
478
|
}
|
|
408
479
|
return getValue(this.state);
|
|
409
480
|
}
|
|
410
|
-
/**
|
|
411
|
-
* Create a computed, filtered array
|
|
412
|
-
*/
|
|
413
|
-
filter(callback) {
|
|
414
|
-
return computed(() => this.get().filter(callback));
|
|
415
|
-
}
|
|
416
481
|
/**
|
|
417
482
|
* Create a computed, mapped array
|
|
483
|
+
* @param callback Callback to transform each item
|
|
484
|
+
* @return Computed array of mapped items
|
|
418
485
|
*/
|
|
419
486
|
map(callback) {
|
|
420
487
|
return computed(() => this.get().map(callback));
|
|
421
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* Notify dependents of changes
|
|
491
|
+
*
|
|
492
|
+
* _This bypasses equality checks and will immediately notify dependents.
|
|
493
|
+
* Use this only if you're modifying nested data that would be ignored by equality checks._
|
|
494
|
+
*/
|
|
495
|
+
notify() {
|
|
496
|
+
emitValue(this.state);
|
|
497
|
+
}
|
|
422
498
|
peek(value) {
|
|
423
|
-
if (value ===
|
|
499
|
+
if (value === 'length') {
|
|
424
500
|
return this.#size.peek();
|
|
425
501
|
}
|
|
426
502
|
if (typeof value === 'number') {
|
|
@@ -430,12 +506,15 @@ class ReactiveArray extends Reactive {
|
|
|
430
506
|
}
|
|
431
507
|
/**
|
|
432
508
|
* Remove and return the last item of the array
|
|
509
|
+
* @returns Removed item, or `undefined` if the array is empty
|
|
433
510
|
*/
|
|
434
511
|
pop() {
|
|
435
512
|
return this.state.value.pop();
|
|
436
513
|
}
|
|
437
514
|
/**
|
|
438
515
|
* Add items to the end of the array
|
|
516
|
+
* @param items Items to add
|
|
517
|
+
* @returns New array length
|
|
439
518
|
*/
|
|
440
519
|
push(...items) {
|
|
441
520
|
return this.state.value.push(...items);
|
|
@@ -447,18 +526,23 @@ class ReactiveArray extends Reactive {
|
|
|
447
526
|
else if (first === 'length') {
|
|
448
527
|
this.length = second;
|
|
449
528
|
}
|
|
450
|
-
else if (typeof first === 'number') {
|
|
451
|
-
this.state.value
|
|
529
|
+
else if (typeof first === 'number' && !Number.isNaN(first)) {
|
|
530
|
+
setAtIndex(this.state.value, first, second);
|
|
452
531
|
}
|
|
453
532
|
}
|
|
454
533
|
/**
|
|
455
534
|
* Remove and return the first item of the array
|
|
535
|
+
* @returns Removed item, or `undefined` if the array is empty
|
|
456
536
|
*/
|
|
457
537
|
shift() {
|
|
458
538
|
return this.state.value.shift();
|
|
459
539
|
}
|
|
460
540
|
/**
|
|
461
541
|
* Remove and return items from the array _(and optionally add new items)_
|
|
542
|
+
* @param from Index to start removing items from
|
|
543
|
+
* @param to Index to stop removing items at _(defaults to the end of the array)_
|
|
544
|
+
* @param items Optional items to add
|
|
545
|
+
* @returns Removed items
|
|
462
546
|
*/
|
|
463
547
|
splice(from, to, ...items) {
|
|
464
548
|
return this.state.value.splice(from, to ?? this.state.value.length, ...items);
|
|
@@ -471,12 +555,15 @@ class ReactiveArray extends Reactive {
|
|
|
471
555
|
}
|
|
472
556
|
/**
|
|
473
557
|
* Add items to the beginning of the array
|
|
558
|
+
* @param items Items to add
|
|
559
|
+
* @returns New array length
|
|
474
560
|
*/
|
|
475
561
|
unshift(...items) {
|
|
476
562
|
return this.state.value.unshift(...items);
|
|
477
563
|
}
|
|
478
564
|
/**
|
|
479
565
|
* Update the value _(based on the current value)_
|
|
566
|
+
* @param callback Callback to update the value
|
|
480
567
|
*/
|
|
481
568
|
update(callback) {
|
|
482
569
|
const updated = callback(this.state.value);
|
|
@@ -487,12 +574,15 @@ class ReactiveArray extends Reactive {
|
|
|
487
574
|
}
|
|
488
575
|
/**
|
|
489
576
|
* Create a reactive array
|
|
577
|
+
* @param value Initial array of items
|
|
578
|
+
* @param options Optional reactivity options
|
|
579
|
+
* @returns Reactive array
|
|
490
580
|
*/
|
|
491
581
|
function array(value, options) {
|
|
492
582
|
return new ReactiveArray(Array.isArray(value) ? value : [], options);
|
|
493
583
|
}
|
|
494
584
|
function updateArray(type, array, state, length) {
|
|
495
|
-
const affectsLength =
|
|
585
|
+
const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
|
|
496
586
|
const previousArray = affectsLength ? [] : [...array];
|
|
497
587
|
const previousLength = array.length;
|
|
498
588
|
return (...args) => {
|
|
@@ -506,40 +596,34 @@ function updateArray(type, array, state, length) {
|
|
|
506
596
|
return result;
|
|
507
597
|
};
|
|
508
598
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const updateMethods = new Set([
|
|
516
|
-
...lengthAffectingMethods,
|
|
517
|
-
'copyWithin',
|
|
518
|
-
'fill',
|
|
519
|
-
'reverse',
|
|
520
|
-
'sort',
|
|
521
|
-
'splice',
|
|
522
|
-
]);
|
|
599
|
+
function setAtIndex(array, index, value) {
|
|
600
|
+
const actual = index < 0 ? array.length + index : index;
|
|
601
|
+
if (actual > -1) {
|
|
602
|
+
array[actual] = value;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
523
605
|
|
|
524
606
|
function isKey(value) {
|
|
525
|
-
|
|
607
|
+
return typeof value === "number" || typeof value === "string";
|
|
526
608
|
}
|
|
527
609
|
function isPlainObject(value) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
const prototype = Object.getPrototypeOf(value);
|
|
535
|
-
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
610
|
+
if (value === null || typeof value !== "object") return false;
|
|
611
|
+
if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
|
|
612
|
+
const prototype = Object.getPrototypeOf(value);
|
|
613
|
+
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
536
614
|
}
|
|
537
615
|
|
|
538
616
|
class Store extends Reactive {
|
|
539
617
|
#keyed = new Map();
|
|
540
618
|
constructor(value, options) {
|
|
541
|
-
super(
|
|
542
|
-
set: (target, property, value) => setValueInProxy(
|
|
619
|
+
super(NAME_STORE, new Proxy(value, {
|
|
620
|
+
set: (target, property, value) => setValueInProxy({
|
|
621
|
+
target,
|
|
622
|
+
property,
|
|
623
|
+
value,
|
|
624
|
+
isArray: false,
|
|
625
|
+
state: this.state,
|
|
626
|
+
}),
|
|
543
627
|
}), options);
|
|
544
628
|
}
|
|
545
629
|
get(key) {
|
|
@@ -566,6 +650,7 @@ class Store extends Reactive {
|
|
|
566
650
|
}
|
|
567
651
|
/**
|
|
568
652
|
* Update the value _(based on the current value)_
|
|
653
|
+
* @param callback Callback to update the value
|
|
569
654
|
*/
|
|
570
655
|
update(callback) {
|
|
571
656
|
const updated = callback({ ...this.state.value });
|
|
@@ -576,6 +661,9 @@ class Store extends Reactive {
|
|
|
576
661
|
}
|
|
577
662
|
/**
|
|
578
663
|
* Create a reactive store
|
|
664
|
+
* @param value Initial object value
|
|
665
|
+
* @param options Optional reactivity options
|
|
666
|
+
* @returns Reactive store
|
|
579
667
|
*/
|
|
580
668
|
function store(value, options) {
|
|
581
669
|
return new Store((isPlainObject(value) ? value : {}), options);
|
package/dist/subscription.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
var Subscription = class {
|
|
2
|
+
callback;
|
|
3
|
+
state;
|
|
2
4
|
constructor(state, callback) {
|
|
3
5
|
this.state = state;
|
|
4
6
|
this.callback = callback;
|
|
5
7
|
callback(state.value);
|
|
6
8
|
}
|
|
9
|
+
destroy() {
|
|
10
|
+
this.callback = noop;
|
|
11
|
+
this.state = void 0;
|
|
12
|
+
}
|
|
7
13
|
};
|
|
8
14
|
function noop() {}
|
|
9
15
|
function subscribe(state, callback) {
|
|
@@ -14,11 +20,7 @@ function subscribe(state, callback) {
|
|
|
14
20
|
};
|
|
15
21
|
}
|
|
16
22
|
function unsubscribe(state, callback) {
|
|
17
|
-
|
|
23
|
+
state.subscriptions.get(callback)?.destroy();
|
|
18
24
|
state.subscriptions.delete(callback);
|
|
19
|
-
if (subscription != null) {
|
|
20
|
-
subscription.callback = noop;
|
|
21
|
-
subscription.state = void 0;
|
|
22
|
-
}
|
|
23
25
|
}
|
|
24
26
|
export { Subscription, noop, subscribe, unsubscribe };
|