@oscarpalmer/mora 0.32.0 → 0.33.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.mjs +2 -2
- package/dist/constants.d.mts +2 -0
- package/dist/constants.mjs +3 -1
- package/dist/effect.d.mts +1 -0
- package/dist/effect.mjs +8 -5
- package/dist/helpers/is.d.mts +6 -6
- package/dist/helpers/misc.d.mts +4 -0
- package/dist/helpers/misc.mjs +9 -0
- package/dist/helpers/proxy.d.mts +8 -10
- package/dist/helpers/proxy.mjs +71 -33
- package/dist/helpers/subscription.d.mts +4 -4
- package/dist/helpers/subscription.mjs +16 -13
- package/dist/helpers/value.d.mts +8 -7
- package/dist/helpers/value.mjs +35 -22
- package/dist/models.d.mts +47 -24
- package/dist/models.mjs +1 -0
- package/dist/mora.full.mjs +307 -236
- package/dist/value/array.d.mts +1 -0
- package/dist/value/array.mjs +69 -61
- package/dist/value/computed.d.mts +3 -1
- package/dist/value/computed.mjs +39 -25
- package/dist/value/readonly.d.mts +3 -3
- package/dist/value/readonly.mjs +31 -24
- package/dist/value/signal.d.mts +1 -0
- package/dist/value/signal.mjs +27 -20
- package/dist/value/store.d.mts +1 -0
- package/dist/value/store.mjs +23 -41
- package/package.json +1 -1
- package/src/batch.ts +2 -2
- package/src/constants.ts +4 -0
- package/src/effect.ts +16 -11
- package/src/helpers/is.ts +6 -6
- package/src/helpers/misc.ts +15 -0
- package/src/helpers/proxy.ts +116 -89
- package/src/helpers/subscription.ts +46 -35
- package/src/helpers/value.ts +61 -35
- package/src/models.ts +54 -28
- package/src/value/array.ts +103 -124
- package/src/value/computed.ts +70 -40
- package/src/value/readonly.ts +54 -41
- package/src/value/signal.ts +46 -35
- package/src/value/store.ts +36 -83
- package/dist/value/reactive.d.mts +0 -4
- package/dist/value/reactive.mjs +0 -15
- package/src/value/reactive.ts +0 -27
package/src/models.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {GenericCallback, Key, PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import type {Subscription, Subscriptions} from '@oscarpalmer/atoms/subscription';
|
|
3
|
-
import type
|
|
3
|
+
import {SYMBOL_EFFECT, SYMBOL_STATE, type PROPERTY_LENGTH} from './constants';
|
|
4
4
|
|
|
5
5
|
// #region Types
|
|
6
6
|
|
|
@@ -28,20 +28,37 @@ export type EffectState = {
|
|
|
28
28
|
callback: GenericCallback;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
export type InternalComputed = {
|
|
32
|
+
[SYMBOL_EFFECT]: ComputedEffect;
|
|
33
|
+
} & InternalSignal;
|
|
34
|
+
|
|
35
|
+
export type InternalProxy = {
|
|
36
|
+
[SYMBOL_STATE]: ReactiveProxyState;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type InternalReadonly = {
|
|
40
|
+
frozen: boolean;
|
|
41
|
+
} & InternalSignal;
|
|
42
|
+
|
|
43
|
+
export type InternalSignal = {
|
|
44
|
+
[SYMBOL_STATE]: SignalState;
|
|
45
|
+
};
|
|
46
|
+
|
|
31
47
|
export type Reactive<Value> = {
|
|
32
48
|
/**
|
|
33
|
-
*
|
|
49
|
+
* _JSON_ representation of the value
|
|
34
50
|
*
|
|
35
|
-
* @returns
|
|
51
|
+
* @returns _JSON_ value
|
|
36
52
|
*/
|
|
37
53
|
toJSON(): Value;
|
|
38
54
|
|
|
39
55
|
/**
|
|
40
56
|
* String representation of the value
|
|
41
57
|
*
|
|
58
|
+
* @param json When `true`, returns the _JSON_ string representation of the value _(defaults to `false`)_
|
|
42
59
|
* @returns Value as string
|
|
43
60
|
*/
|
|
44
|
-
toString(): string;
|
|
61
|
+
toString(json?: boolean): string;
|
|
45
62
|
};
|
|
46
63
|
|
|
47
64
|
export type ReactiveArray<Item> = {
|
|
@@ -230,25 +247,21 @@ export type ReactiveArray<Item> = {
|
|
|
230
247
|
/**
|
|
231
248
|
* Subscribe to changes
|
|
232
249
|
*
|
|
233
|
-
* @param
|
|
250
|
+
* @param subscriber Callback for changes
|
|
234
251
|
* @param copy Copy the array? _(defaults to `false`)_
|
|
235
252
|
* @returns Subscription
|
|
236
253
|
*/
|
|
237
|
-
subscribe(
|
|
254
|
+
subscribe(subscriber: Subscriber<Item[]>, copy?: boolean): Subscription;
|
|
238
255
|
|
|
239
256
|
/**
|
|
240
257
|
* Subscribe to changes at a specific index
|
|
241
258
|
*
|
|
242
259
|
* @param index Index of item to subscribe to
|
|
243
|
-
* @param
|
|
260
|
+
* @param subscriber Callback for changes
|
|
244
261
|
* @param copy If the item is an array or record, should it be copied? _(defaults to `false`)_
|
|
245
262
|
* @returns Subscription
|
|
246
263
|
*/
|
|
247
|
-
subscribe(
|
|
248
|
-
index: number,
|
|
249
|
-
callback: (value: Item | undefined) => void,
|
|
250
|
-
copy?: boolean,
|
|
251
|
-
): Subscription;
|
|
264
|
+
subscribe(index: number, subscriber: Subscriber<Item | undefined>, copy?: boolean): Subscription;
|
|
252
265
|
|
|
253
266
|
/**
|
|
254
267
|
* Add items to the beginning of the array
|
|
@@ -276,14 +289,20 @@ export type ReactiveOptions<Value> = {
|
|
|
276
289
|
equal?: (first: Value, second: Value) => boolean;
|
|
277
290
|
};
|
|
278
291
|
|
|
279
|
-
export type
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
292
|
+
export type ReactiveProxyState = {
|
|
293
|
+
isArray: boolean;
|
|
294
|
+
length?: Signal<number>;
|
|
295
|
+
mapped?: Map<string, [Computed<unknown>, ComputedEffect]>;
|
|
296
|
+
} & SignalState;
|
|
297
|
+
|
|
298
|
+
export type ReactiveState = {
|
|
299
|
+
computeds?: Set<ComputedEffect>;
|
|
300
|
+
effects?: Set<EffectState>;
|
|
301
|
+
equal?: (first: unknown, second: unknown) => boolean;
|
|
302
|
+
promise?: Promise<unknown>;
|
|
284
303
|
promises?: Map<Key, Promise<never>>;
|
|
285
304
|
subscriptions?: Subscriptions<GenericCallback>;
|
|
286
|
-
value:
|
|
305
|
+
value: unknown;
|
|
287
306
|
};
|
|
288
307
|
|
|
289
308
|
export type ReactiveStore<Store> = {
|
|
@@ -396,23 +415,23 @@ export type ReactiveStore<Store> = {
|
|
|
396
415
|
/**
|
|
397
416
|
* Subscribe to changes
|
|
398
417
|
*
|
|
399
|
-
* @param
|
|
418
|
+
* @param subscriber Callback for changes
|
|
400
419
|
* @param copy Copy the store? _(defaults to `false`)_
|
|
401
420
|
* @returns Subscription
|
|
402
421
|
*/
|
|
403
|
-
subscribe(
|
|
422
|
+
subscribe(subscriber: Subscriber<Store>, copy?: boolean): Subscription;
|
|
404
423
|
|
|
405
424
|
/**
|
|
406
425
|
* Subscribe to changes for a specific key
|
|
407
426
|
*
|
|
408
427
|
* @param key Key of the value to subscribe to
|
|
409
|
-
* @param
|
|
428
|
+
* @param subscriber Callback for changes
|
|
410
429
|
* @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
|
|
411
430
|
* @returns Subscription
|
|
412
431
|
*/
|
|
413
432
|
subscribe<Key extends keyof Store>(
|
|
414
433
|
key: Key,
|
|
415
|
-
|
|
434
|
+
subscriber: Subscriber<Store[Key] | undefined>,
|
|
416
435
|
copy?: boolean,
|
|
417
436
|
): Subscription;
|
|
418
437
|
|
|
@@ -420,11 +439,11 @@ export type ReactiveStore<Store> = {
|
|
|
420
439
|
* Subscribe to changes for a specific key
|
|
421
440
|
*
|
|
422
441
|
* @param key Key of the value to subscribe to
|
|
423
|
-
* @param
|
|
442
|
+
* @param subscriber Callback for changes
|
|
424
443
|
* @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
|
|
425
444
|
* @returns Subscription
|
|
426
445
|
*/
|
|
427
|
-
subscribe(key: Key,
|
|
446
|
+
subscribe(key: Key, subscriber: Subscriber<unknown>, copy?: boolean): Subscription;
|
|
428
447
|
|
|
429
448
|
/**
|
|
430
449
|
* Update the value _(based on the current value)_
|
|
@@ -434,7 +453,7 @@ export type ReactiveStore<Store> = {
|
|
|
434
453
|
update(callback: (value: Store) => Store): void;
|
|
435
454
|
} & Reactive<Store>;
|
|
436
455
|
|
|
437
|
-
|
|
456
|
+
type ReadonlyInstances<Value> = {
|
|
438
457
|
frozen?: ReadonlyFrozenSignal<Value>;
|
|
439
458
|
original?: ReadonlySignal<Value>;
|
|
440
459
|
};
|
|
@@ -490,6 +509,10 @@ export type Signal<Value> = {
|
|
|
490
509
|
} & Reactive<Value> &
|
|
491
510
|
SimpleReactive<Value>;
|
|
492
511
|
|
|
512
|
+
export type SignalState = {
|
|
513
|
+
readonlies?: ReadonlyInstances<unknown>;
|
|
514
|
+
} & ReactiveState;
|
|
515
|
+
|
|
493
516
|
type SimpleReactive<Value> = {
|
|
494
517
|
/**
|
|
495
518
|
* Get the value
|
|
@@ -509,20 +532,23 @@ type SimpleReactive<Value> = {
|
|
|
509
532
|
/**
|
|
510
533
|
* Subscribe to changes
|
|
511
534
|
*
|
|
512
|
-
* @param
|
|
535
|
+
* @param subscriber Callback for changes
|
|
513
536
|
* @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
|
|
514
537
|
* @returns Subscription
|
|
515
538
|
*/
|
|
516
|
-
subscribe(
|
|
539
|
+
subscribe(subscriber: Subscriber<Value>, copy?: boolean): Subscription;
|
|
517
540
|
};
|
|
518
541
|
|
|
519
542
|
export type StoredSubscription = {
|
|
520
543
|
callback: GenericCallback;
|
|
521
544
|
copy: boolean;
|
|
522
545
|
frozen: boolean;
|
|
523
|
-
|
|
546
|
+
instance: InternalSignal;
|
|
547
|
+
state: SignalState;
|
|
524
548
|
};
|
|
525
549
|
|
|
550
|
+
export type Subscriber<Value> = (value: Value, subscription: Subscription) => void;
|
|
551
|
+
|
|
526
552
|
export type SubscriptionType = 'copy' | 'frozen' | 'original' | 'readonly';
|
|
527
553
|
|
|
528
554
|
// #endregion
|
package/src/value/array.ts
CHANGED
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
NAME_ARRAY,
|
|
7
7
|
NAME_MORA,
|
|
8
8
|
PROPERTY_LENGTH,
|
|
9
|
+
SYMBOL_STATE,
|
|
9
10
|
} from '../constants';
|
|
11
|
+
import {getState} from '../helpers/misc';
|
|
10
12
|
import {
|
|
11
13
|
emitProxyValues,
|
|
12
14
|
getValueInProxy,
|
|
@@ -16,21 +18,92 @@ import {
|
|
|
16
18
|
updateProxyValue,
|
|
17
19
|
} from '../helpers/proxy';
|
|
18
20
|
import {subscribeToProxy} from '../helpers/subscription';
|
|
19
|
-
import {emitValue, equalArrays} from '../helpers/value';
|
|
20
|
-
import type {
|
|
21
|
-
Computed,
|
|
22
|
-
ComputedEffect,
|
|
23
|
-
ReactiveArray,
|
|
24
|
-
ReactiveOptions,
|
|
25
|
-
ReactiveState,
|
|
26
|
-
ReadonlyInstances,
|
|
27
|
-
Signal,
|
|
28
|
-
} from '../models';
|
|
21
|
+
import {emitValue, equalArrays, getStringValue} from '../helpers/value';
|
|
22
|
+
import type {InternalProxy, ReactiveArray, ReactiveOptions, ReactiveState} from '../models';
|
|
29
23
|
import {computed} from './computed';
|
|
30
|
-
import {reactive} from './reactive';
|
|
31
24
|
import {getReadonlyInstance} from './readonly';
|
|
32
25
|
import {signal} from './signal';
|
|
33
26
|
|
|
27
|
+
// #region Instance
|
|
28
|
+
|
|
29
|
+
function ReactiveArray(this: any, value: unknown, options?: ReactiveOptions<unknown>) {
|
|
30
|
+
this[SYMBOL_STATE] = {
|
|
31
|
+
...getState(undefined, options),
|
|
32
|
+
isArray: true,
|
|
33
|
+
length: signal(0),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
this[SYMBOL_STATE].value = new Proxy([], {
|
|
37
|
+
get: (target, property) =>
|
|
38
|
+
METHODS_UPDATE.has(property as string)
|
|
39
|
+
? updateArray(this, property as string, target)
|
|
40
|
+
: Reflect.get(target, property),
|
|
41
|
+
set: (target, property, value) => setValueInProxy(this, target, property, value),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
Object.defineProperty(this, PROPERTY_LENGTH, {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
get: () => this[SYMBOL_STATE].length.get(),
|
|
47
|
+
set: (value: number) => setArrayLength(this[SYMBOL_STATE], value),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
setProxyValue.call(this, value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ReactiveArray.prototype[NAME_MORA] = NAME_ARRAY;
|
|
54
|
+
|
|
55
|
+
ReactiveArray.prototype.asReadonly = getReadonlyInstance;
|
|
56
|
+
ReactiveArray.prototype.at = getArrayValues;
|
|
57
|
+
ReactiveArray.prototype.get = getArrayValues;
|
|
58
|
+
ReactiveArray.prototype.notify = emitProxyValues;
|
|
59
|
+
ReactiveArray.prototype.peek = peekArrayValue;
|
|
60
|
+
ReactiveArray.prototype.set = setProxyValue;
|
|
61
|
+
ReactiveArray.prototype.subscribe = subscribeToProxy;
|
|
62
|
+
ReactiveArray.prototype.toString = getStringValue;
|
|
63
|
+
ReactiveArray.prototype.update = updateProxyValue;
|
|
64
|
+
|
|
65
|
+
ReactiveArray.prototype.clear = function () {
|
|
66
|
+
this[SYMBOL_STATE].value.length = 0;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
ReactiveArray.prototype.filter = function (callback: never) {
|
|
70
|
+
return computed(() => filter(getArrayValues.call(this) as never, callback) as never);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
ReactiveArray.prototype.map = function (callback: never) {
|
|
74
|
+
return computed(() => (getArrayValues.call(this) as unknown[]).map(callback));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
ReactiveArray.prototype.pop = function () {
|
|
78
|
+
return this[SYMBOL_STATE].value.pop();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
ReactiveArray.prototype.push = function (...items: unknown[]) {
|
|
82
|
+
return this[SYMBOL_STATE].value.push(...items);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
ReactiveArray.prototype.select = function (filter: never, map: never) {
|
|
86
|
+
return computed(() => select(getArrayValues.call(this) as unknown[], filter, map));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
ReactiveArray.prototype.shift = function () {
|
|
90
|
+
return this[SYMBOL_STATE].value.shift();
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
ReactiveArray.prototype.splice = function (from: never, to?: never, ...items: unknown[]) {
|
|
94
|
+
return this[SYMBOL_STATE].value.splice(from, to ?? this[SYMBOL_STATE].value.length, ...items);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
ReactiveArray.prototype.toJSON = function () {
|
|
98
|
+
return this[SYMBOL_STATE].value;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
ReactiveArray.prototype.unshift = function (...items: unknown[]) {
|
|
102
|
+
return this[SYMBOL_STATE].value.unshift(...items);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// #endregion
|
|
106
|
+
|
|
34
107
|
// #region Functions
|
|
35
108
|
|
|
36
109
|
/**
|
|
@@ -66,131 +139,37 @@ export function array<Item>(
|
|
|
66
139
|
*/
|
|
67
140
|
export function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
|
|
68
141
|
|
|
69
|
-
export function array<
|
|
70
|
-
|
|
71
|
-
options
|
|
72
|
-
|
|
73
|
-
const [rx, state] = reactive<Item[], Item>([], options);
|
|
74
|
-
|
|
75
|
-
const isArray = true;
|
|
76
|
-
const length = signal(0);
|
|
77
|
-
const mapped = new Map<number, [Computed<unknown>, ComputedEffect]>();
|
|
78
|
-
const readonlies: ReadonlyInstances<Item[]> = {};
|
|
79
|
-
|
|
80
|
-
state.value = new Proxy([], {
|
|
81
|
-
get: (target: Item[], property: PropertyKey) =>
|
|
82
|
-
METHODS_UPDATE.has(property as string)
|
|
83
|
-
? updateArray(property as string, target, state, length)
|
|
84
|
-
: Reflect.get(target, property),
|
|
85
|
-
set: (target: Item[], property: PropertyKey, value: Item) =>
|
|
86
|
-
setValueInProxy(isArray, state, target, property, value, length),
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
function get(): Item[];
|
|
90
|
-
function get(index: number): Item | undefined;
|
|
91
|
-
function get(property: typeof PROPERTY_LENGTH): number;
|
|
92
|
-
function get(value?: unknown): number | Item | Item[] | undefined;
|
|
93
|
-
|
|
94
|
-
function get(value?: unknown): unknown {
|
|
95
|
-
return value === PROPERTY_LENGTH
|
|
96
|
-
? length.get()
|
|
97
|
-
: getValueInProxy(isArray, instance as never, state, mapped, value);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function set(first?: never, second?: never): void {
|
|
101
|
-
setProxyValue<Item[], Item>(true, state, setArrayValue, setAtIndex, first, second);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const instance = {
|
|
105
|
-
...rx,
|
|
106
|
-
asReadonly: (frozen?: never) => getReadonlyInstance(state, readonlies, frozen === true),
|
|
107
|
-
at: (index: never) => get(index),
|
|
108
|
-
clear: () => {
|
|
109
|
-
state.value.length = 0;
|
|
110
|
-
},
|
|
111
|
-
filter: (callback: never) => computed(() => filter(get(), callback)),
|
|
112
|
-
get: (value?: never) => get(value),
|
|
113
|
-
map: (callback: never) => computed(() => get().map(callback)),
|
|
114
|
-
notify: () => emitProxyValues(state, mapped),
|
|
115
|
-
peek: (first?: never, second?: never) => peekArrayValue(state, length, first, second),
|
|
116
|
-
pop: () => state.value.pop(),
|
|
117
|
-
push: (...items: Item[]) => state.value.push(...items),
|
|
118
|
-
select: (filter: never, map: never) => computed(() => select(get(), filter, map)),
|
|
119
|
-
set: (first?: never, second?: never) => set(first, second),
|
|
120
|
-
shift: () => state.value.shift(),
|
|
121
|
-
splice: (from: never, to?: never, ...items: Item[]) =>
|
|
122
|
-
state.value.splice(from, to ?? state.value.length, ...items),
|
|
123
|
-
subscribe: (first: never, second?: never, third?: never) =>
|
|
124
|
-
subscribeToProxy(isArray, instance as never, state, mapped, first, second, third),
|
|
125
|
-
unshift: (...items: Item[]) => state.value.unshift(...items),
|
|
126
|
-
update: (callback: never) => updateProxyValue(true, state, setArrayValue, setAtIndex, callback),
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
Object.defineProperties(instance, {
|
|
130
|
-
[NAME_MORA]: {
|
|
131
|
-
value: NAME_ARRAY,
|
|
132
|
-
},
|
|
133
|
-
length: {
|
|
134
|
-
enumerable: true,
|
|
135
|
-
get: () => length.get(),
|
|
136
|
-
set: (value: number) => setArrayLength(state, value),
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
set(value as never);
|
|
142
|
+
export function array(value: unknown, options?: ReactiveOptions<unknown>): ReactiveArray<unknown> {
|
|
143
|
+
// @ts-expect-error All good, no worries :-)
|
|
144
|
+
return new ReactiveArray(value, options);
|
|
145
|
+
}
|
|
141
146
|
|
|
142
|
-
|
|
147
|
+
function getArrayValues(this: InternalProxy, value?: unknown): unknown {
|
|
148
|
+
return value === PROPERTY_LENGTH
|
|
149
|
+
? this[SYMBOL_STATE].length!.get()
|
|
150
|
+
: getValueInProxy.call(this, value);
|
|
143
151
|
}
|
|
144
152
|
|
|
145
|
-
function peekArrayValue
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
second?: boolean,
|
|
150
|
-
): unknown {
|
|
151
|
-
return first === PROPERTY_LENGTH ? length.peek() : peekValueInProxy(true, state, first, second);
|
|
153
|
+
function peekArrayValue(this: InternalProxy, first?: unknown, second?: boolean): unknown {
|
|
154
|
+
return first === PROPERTY_LENGTH
|
|
155
|
+
? this[SYMBOL_STATE].length!.peek()
|
|
156
|
+
: peekValueInProxy.call(this, first, second);
|
|
152
157
|
}
|
|
153
158
|
|
|
154
|
-
function setArrayLength
|
|
159
|
+
function setArrayLength(state: ReactiveState, value: number): void {
|
|
155
160
|
if (
|
|
156
161
|
typeof value === 'number' &&
|
|
157
162
|
!Number.isNaN(value) &&
|
|
158
163
|
value >= 0 &&
|
|
159
|
-
value !== state.value.length
|
|
164
|
+
value !== (state.value as unknown[]).length
|
|
160
165
|
) {
|
|
161
|
-
state.value.length = value;
|
|
166
|
+
(state.value as unknown[]).length = value;
|
|
162
167
|
}
|
|
163
168
|
}
|
|
164
169
|
|
|
165
|
-
function
|
|
166
|
-
|
|
167
|
-
0,
|
|
168
|
-
(state.value as unknown[]).length,
|
|
169
|
-
...((value as unknown[]) ?? []),
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function setAtIndex<Value, Item = Value>(
|
|
174
|
-
state: ReactiveState<Value, Item>,
|
|
175
|
-
index: unknown,
|
|
176
|
-
value: Item,
|
|
177
|
-
): void {
|
|
178
|
-
const actual =
|
|
179
|
-
(index as number) < 0
|
|
180
|
-
? (state.value as unknown[]).length + (index as number)
|
|
181
|
-
: (index as number);
|
|
182
|
-
|
|
183
|
-
if (actual > -1) {
|
|
184
|
-
(state.value as unknown[])[actual] = value;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
170
|
+
function updateArray(instance: InternalProxy, type: string, array: unknown[]): unknown {
|
|
171
|
+
const state = instance[SYMBOL_STATE];
|
|
187
172
|
|
|
188
|
-
function updateArray<Item>(
|
|
189
|
-
type: string,
|
|
190
|
-
array: Item[],
|
|
191
|
-
state: ReactiveState<Item[], Item>,
|
|
192
|
-
length: Signal<number>,
|
|
193
|
-
): unknown {
|
|
194
173
|
const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
|
|
195
174
|
const previousArray = affectsLength ? [] : array.slice();
|
|
196
175
|
const previousLength = array.length;
|
|
@@ -201,9 +180,9 @@ function updateArray<Item>(
|
|
|
201
180
|
if (
|
|
202
181
|
affectsLength ? array.length !== previousLength : !equalArrays(state, previousArray, array)
|
|
203
182
|
) {
|
|
204
|
-
emitValue(
|
|
183
|
+
emitValue(instance);
|
|
205
184
|
|
|
206
|
-
length
|
|
185
|
+
state.length!.set(array.length);
|
|
207
186
|
}
|
|
208
187
|
|
|
209
188
|
return result;
|
package/src/value/computed.ts
CHANGED
|
@@ -1,18 +1,48 @@
|
|
|
1
|
+
import type {GenericAsyncCallback, GenericCallback} from '@oscarpalmer/atoms/models';
|
|
1
2
|
import {flushHandlers} from '../batch';
|
|
2
3
|
import {
|
|
3
4
|
ACTIVE,
|
|
4
5
|
BATCH,
|
|
5
6
|
NAME_COMPUTED,
|
|
6
7
|
NAME_MORA,
|
|
7
|
-
SUBSCRIPTION_TYPES_COPY,
|
|
8
8
|
SUBSCRIPTION_TYPE_FROZEN,
|
|
9
9
|
SUBSCRIPTION_TYPES,
|
|
10
|
+
SUBSCRIPTION_TYPES_COPY,
|
|
11
|
+
SYMBOL_EFFECT,
|
|
12
|
+
SYMBOL_STATE,
|
|
10
13
|
} from '../constants';
|
|
11
14
|
import {internalEffect, runEffect} from '../effect';
|
|
15
|
+
import {getState} from '../helpers/misc';
|
|
12
16
|
import {subscribeToSignal} from '../helpers/subscription';
|
|
13
|
-
import {
|
|
14
|
-
import type {
|
|
15
|
-
|
|
17
|
+
import {getStringValue, handleSignalValue, peekSignalValue} from '../helpers/value';
|
|
18
|
+
import type {
|
|
19
|
+
Computed,
|
|
20
|
+
ComputedEffect,
|
|
21
|
+
ReactiveOptions,
|
|
22
|
+
InternalComputed,
|
|
23
|
+
InternalSignal,
|
|
24
|
+
} from '../models';
|
|
25
|
+
|
|
26
|
+
// #region Instance
|
|
27
|
+
|
|
28
|
+
function Computed(this: any, callback: GenericCallback, options?: ReactiveOptions<unknown>) {
|
|
29
|
+
this[SYMBOL_STATE] = getState(undefined, options);
|
|
30
|
+
|
|
31
|
+
this[SYMBOL_EFFECT] = getComputedEffect(this, callback);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Computed.prototype[NAME_MORA] = NAME_COMPUTED;
|
|
35
|
+
|
|
36
|
+
Computed.prototype.get = getComputedValue;
|
|
37
|
+
Computed.prototype.peek = peekSignalValue;
|
|
38
|
+
Computed.prototype.subscribe = subscribeToSignal;
|
|
39
|
+
Computed.prototype.toString = getStringValue;
|
|
40
|
+
|
|
41
|
+
Computed.prototype.toJSON = function () {
|
|
42
|
+
return this[SYMBOL_STATE].value;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// #endregion
|
|
16
46
|
|
|
17
47
|
// #region Functions
|
|
18
48
|
|
|
@@ -31,36 +61,22 @@ export function computed<Value>(
|
|
|
31
61
|
}
|
|
32
62
|
|
|
33
63
|
function getComputed<Value>(
|
|
34
|
-
callback:
|
|
64
|
+
callback: GenericCallback,
|
|
35
65
|
options?: ReactiveOptions<Value>,
|
|
36
66
|
): [Computed<Value>, ComputedEffect] {
|
|
37
67
|
if (typeof callback !== 'function') {
|
|
38
68
|
throw new TypeError('Computed callback must be a function');
|
|
39
69
|
}
|
|
40
70
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
let fx: ComputedEffect;
|
|
44
|
-
|
|
45
|
-
const instance = {
|
|
46
|
-
...rx,
|
|
47
|
-
get: () => getValue(state, fx),
|
|
48
|
-
peek: (copy?: never) => peekSimpleValue(state.value, copy === true),
|
|
49
|
-
subscribe: (callback: never, copy?: never) => subscribeToSignal(state, callback, copy === true),
|
|
50
|
-
};
|
|
71
|
+
// @ts-expect-error All good, no worries :-)
|
|
72
|
+
const instance = new Computed(callback, options);
|
|
51
73
|
|
|
52
|
-
|
|
53
|
-
value: NAME_COMPUTED,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
fx = getComputedEffect(state, callback);
|
|
57
|
-
|
|
58
|
-
return [Object.freeze(instance) as never, fx];
|
|
74
|
+
return [instance as never, instance[SYMBOL_EFFECT]];
|
|
59
75
|
}
|
|
60
76
|
|
|
61
|
-
function getComputedEffect
|
|
62
|
-
|
|
63
|
-
callback:
|
|
77
|
+
function getComputedEffect(
|
|
78
|
+
instance: InternalSignal,
|
|
79
|
+
callback: GenericAsyncCallback,
|
|
64
80
|
): ComputedEffect {
|
|
65
81
|
const fx: ComputedEffect = {
|
|
66
82
|
dirty: true,
|
|
@@ -73,7 +89,7 @@ function getComputedEffect<Value>(
|
|
|
73
89
|
|
|
74
90
|
ACTIVE.computed = fx;
|
|
75
91
|
|
|
76
|
-
|
|
92
|
+
handleSignalValue(instance, callback, setAndEmit, () => {
|
|
77
93
|
ACTIVE.computed = previousComputed;
|
|
78
94
|
});
|
|
79
95
|
|
|
@@ -84,12 +100,19 @@ function getComputedEffect<Value>(
|
|
|
84
100
|
return fx;
|
|
85
101
|
}
|
|
86
102
|
|
|
87
|
-
function
|
|
103
|
+
function getComputedValue(this: InternalComputed): unknown {
|
|
104
|
+
const fx = this[SYMBOL_EFFECT];
|
|
105
|
+
const state = this[SYMBOL_STATE];
|
|
106
|
+
|
|
88
107
|
if (ACTIVE.computed != null && fx !== ACTIVE.computed) {
|
|
108
|
+
state.computeds ??= new Set();
|
|
109
|
+
|
|
89
110
|
state.computeds.add(ACTIVE.computed);
|
|
90
111
|
}
|
|
91
112
|
|
|
92
113
|
if (ACTIVE.effect != null && ACTIVE.effect !== fx.instance) {
|
|
114
|
+
state.effects ??= new Set();
|
|
115
|
+
|
|
93
116
|
state.effects.add(ACTIVE.effect);
|
|
94
117
|
}
|
|
95
118
|
|
|
@@ -100,26 +123,32 @@ function getValue<Value>(state: ReactiveState<Value, Value>, fx: ComputedEffect)
|
|
|
100
123
|
return state.value;
|
|
101
124
|
}
|
|
102
125
|
|
|
103
|
-
export function internalComputed
|
|
104
|
-
callback:
|
|
105
|
-
options?: ReactiveOptions<
|
|
106
|
-
): [Computed<
|
|
126
|
+
export function internalComputed(
|
|
127
|
+
callback: GenericCallback,
|
|
128
|
+
options?: ReactiveOptions<unknown>,
|
|
129
|
+
): [Computed<unknown>, ComputedEffect] {
|
|
107
130
|
return getComputed(callback, options);
|
|
108
131
|
}
|
|
109
132
|
|
|
110
|
-
function setAndEmit
|
|
111
|
-
|
|
133
|
+
function setAndEmit(instance: InternalSignal, value: unknown): void {
|
|
134
|
+
const state = instance[SYMBOL_STATE];
|
|
135
|
+
|
|
136
|
+
if ((state.equal ?? Object.is)(state.value as never, value as never)) {
|
|
112
137
|
return;
|
|
113
138
|
}
|
|
114
139
|
|
|
115
140
|
state.value = value;
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
computed.
|
|
142
|
+
if (state.computeds != null && state.computeds.size > 0) {
|
|
143
|
+
for (const computed of state.computeds) {
|
|
144
|
+
computed.dirty = true;
|
|
145
|
+
}
|
|
119
146
|
}
|
|
120
147
|
|
|
121
|
-
|
|
122
|
-
|
|
148
|
+
if (state.effects != null && state.effects.size > 0) {
|
|
149
|
+
for (const effect of state.effects) {
|
|
150
|
+
BATCH.handlers.set(effect, effect);
|
|
151
|
+
}
|
|
123
152
|
}
|
|
124
153
|
|
|
125
154
|
for (const type of SUBSCRIPTION_TYPES) {
|
|
@@ -127,12 +156,13 @@ function setAndEmit<Value>(state: ReactiveState<Value, Value>, value: Value): vo
|
|
|
127
156
|
continue;
|
|
128
157
|
}
|
|
129
158
|
|
|
130
|
-
const copy = SUBSCRIPTION_TYPES_COPY.has(type);
|
|
131
159
|
const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
|
|
132
160
|
|
|
133
|
-
if (subscriptions != null) {
|
|
161
|
+
if (subscriptions != null && subscriptions.size > 0) {
|
|
162
|
+
const copy = SUBSCRIPTION_TYPES_COPY.has(type);
|
|
163
|
+
|
|
134
164
|
for (const [, callback] of subscriptions) {
|
|
135
|
-
callback(
|
|
165
|
+
callback(peekSignalValue.call(instance, copy));
|
|
136
166
|
}
|
|
137
167
|
}
|
|
138
168
|
}
|