@oscarpalmer/mora 0.31.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.d.mts +4 -5
- package/dist/batch.mjs +4 -2
- package/dist/constants.d.mts +31 -19
- package/dist/constants.mjs +19 -2
- package/dist/effect.d.mts +5 -5
- package/dist/effect.mjs +8 -5
- package/dist/helpers/is.d.mts +14 -9
- package/dist/helpers/is.mjs +6 -0
- package/dist/helpers/misc.d.mts +4 -0
- package/dist/helpers/misc.mjs +9 -0
- package/dist/helpers/proxy.d.mts +10 -10
- package/dist/helpers/proxy.mjs +83 -27
- package/dist/helpers/subscription.d.mts +8 -0
- package/dist/helpers/subscription.mjs +30 -0
- package/dist/helpers/value.d.mts +10 -7
- package/dist/helpers/value.mjs +56 -17
- package/dist/index.d.mts +3 -2
- package/dist/models.d.mts +82 -101
- package/dist/models.mjs +1 -0
- package/dist/mora.full.mjs +659 -412
- package/dist/value/array.d.mts +5 -5
- package/dist/value/array.mjs +70 -109
- package/dist/value/computed.d.mts +5 -4
- package/dist/value/computed.mjs +45 -31
- package/dist/value/readonly.d.mts +4 -6
- package/dist/value/readonly.mjs +34 -31
- package/dist/value/signal.d.mts +5 -5
- package/dist/value/signal.mjs +28 -34
- package/dist/value/store.d.mts +5 -5
- package/dist/value/store.mjs +25 -83
- package/package.json +6 -6
- package/src/batch.ts +15 -5
- package/src/constants.ts +36 -2
- package/src/effect.ts +20 -11
- package/src/helpers/is.ts +16 -6
- package/src/helpers/misc.ts +15 -0
- package/src/helpers/proxy.ts +151 -74
- package/src/helpers/subscription.ts +89 -0
- package/src/helpers/value.ts +121 -30
- package/src/index.ts +1 -1
- package/src/models.ts +81 -98
- package/src/value/array.ts +116 -226
- package/src/value/computed.ts +96 -43
- package/src/value/readonly.ts +68 -54
- package/src/value/signal.ts +54 -46
- package/src/value/store.ts +42 -168
- package/types/constants.d.ts +1 -1
- package/types/index.d.ts +1 -1
- package/types/value/array.d.ts +1 -1
- package/types/value/computed.d.ts +0 -3
- package/types/value/signal.d.ts +1 -1
- package/types/value/store.d.ts +1 -1
- package/dist/subscription.d.mts +0 -7
- package/dist/subscription.mjs +0 -27
- package/dist/value/reactive.d.mts +0 -5
- package/dist/value/reactive.mjs +0 -16
- package/src/subscription.ts +0 -45
- package/src/value/reactive.ts +0 -24
package/src/value/array.ts
CHANGED
|
@@ -1,37 +1,111 @@
|
|
|
1
1
|
import {select} from '@oscarpalmer/atoms/array';
|
|
2
2
|
import {filter} from '@oscarpalmer/atoms/array/filter';
|
|
3
|
-
import {noop} from '@oscarpalmer/atoms/function';
|
|
4
|
-
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
5
|
-
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
6
3
|
import {
|
|
7
4
|
METHODS_AFFECTING_LENGTH,
|
|
8
5
|
METHODS_UPDATE,
|
|
9
6
|
NAME_ARRAY,
|
|
10
7
|
NAME_MORA,
|
|
11
8
|
PROPERTY_LENGTH,
|
|
9
|
+
SYMBOL_STATE,
|
|
12
10
|
} from '../constants';
|
|
11
|
+
import {getState} from '../helpers/misc';
|
|
13
12
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
emitProxyValues,
|
|
14
|
+
getValueInProxy,
|
|
15
|
+
peekValueInProxy,
|
|
16
16
|
setProxyValue,
|
|
17
17
|
setValueInProxy,
|
|
18
|
+
updateProxyValue,
|
|
18
19
|
} from '../helpers/proxy';
|
|
19
|
-
import {
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
ComputedEffect,
|
|
23
|
-
ReactiveArray,
|
|
24
|
-
ReactiveOptions,
|
|
25
|
-
ReactiveState,
|
|
26
|
-
ReadonlyInstances,
|
|
27
|
-
Signal,
|
|
28
|
-
} from '../models';
|
|
29
|
-
import {subscribe, unsubscribe} from '../subscription';
|
|
20
|
+
import {subscribeToProxy} from '../helpers/subscription';
|
|
21
|
+
import {emitValue, equalArrays, getStringValue} from '../helpers/value';
|
|
22
|
+
import type {InternalProxy, ReactiveArray, ReactiveOptions, ReactiveState} from '../models';
|
|
30
23
|
import {computed} from './computed';
|
|
31
|
-
import {reactive} from './reactive';
|
|
32
24
|
import {getReadonlyInstance} from './readonly';
|
|
33
25
|
import {signal} from './signal';
|
|
34
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
|
+
|
|
107
|
+
// #region Functions
|
|
108
|
+
|
|
35
109
|
/**
|
|
36
110
|
* Create a reactive array from a function result
|
|
37
111
|
*
|
|
@@ -65,238 +139,54 @@ export function array<Item>(
|
|
|
65
139
|
*/
|
|
66
140
|
export function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
|
|
67
141
|
|
|
68
|
-
export function array<
|
|
69
|
-
|
|
70
|
-
options
|
|
71
|
-
): ReactiveArray<Item> {
|
|
72
|
-
const [rx, state] = reactive<Item[], Item>([], options);
|
|
73
|
-
|
|
74
|
-
const indiced = new Map<number, [Computed<unknown>, ComputedEffect]>();
|
|
75
|
-
const length = signal(0);
|
|
76
|
-
const readonlies: ReadonlyInstances<Item[]> = {};
|
|
77
|
-
|
|
78
|
-
let instance: Record<string, unknown> = {};
|
|
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({
|
|
87
|
-
length,
|
|
88
|
-
property,
|
|
89
|
-
state,
|
|
90
|
-
target,
|
|
91
|
-
value,
|
|
92
|
-
isArray: true,
|
|
93
|
-
}),
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
function get(): Item[];
|
|
97
|
-
function get(index: number): Item | undefined;
|
|
98
|
-
function get(property: typeof PROPERTY_LENGTH): number;
|
|
99
|
-
function get(value?: unknown): number | Item | Item[] | undefined;
|
|
100
|
-
|
|
101
|
-
function get(value?: unknown): number | Item | Item[] | undefined {
|
|
102
|
-
return getArrayValue(instance as ReactiveArray<Item>, indiced, state, length, value);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function set(first?: unknown, second?: unknown): void {
|
|
106
|
-
setProxyValue<Item[], Item>(
|
|
107
|
-
true,
|
|
108
|
-
state,
|
|
109
|
-
isArrayValue,
|
|
110
|
-
isArrayIndex,
|
|
111
|
-
setArray,
|
|
112
|
-
setAtIndex,
|
|
113
|
-
first,
|
|
114
|
-
second,
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const handlers = {
|
|
119
|
-
...rx,
|
|
120
|
-
get: (value?: number | typeof PROPERTY_LENGTH) => get(value),
|
|
121
|
-
peek: (first?: unknown, second?: boolean) => peekArrayValue(state, length, first, second),
|
|
122
|
-
subscribe: (first: number | GenericCallback, second?: GenericCallback) => {
|
|
123
|
-
if (typeof first === 'number' && typeof second === 'function') {
|
|
124
|
-
return getReactiveValueInProxy(
|
|
125
|
-
instance as ReactiveArray<Item>,
|
|
126
|
-
indiced,
|
|
127
|
-
first,
|
|
128
|
-
true,
|
|
129
|
-
).subscribe(second);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return typeof first === 'function' ? subscribe(state, first) : noop;
|
|
133
|
-
},
|
|
134
|
-
unsubscribe: (first: number | GenericCallback, second?: GenericCallback) => {
|
|
135
|
-
if (typeof first === 'number' && typeof second === 'function') {
|
|
136
|
-
getReactiveValueInProxy(instance as ReactiveArray<Item>, indiced, first, true)?.unsubscribe(
|
|
137
|
-
second,
|
|
138
|
-
);
|
|
139
|
-
} else if (typeof first === 'function') {
|
|
140
|
-
unsubscribe(state, first);
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
instance = {
|
|
146
|
-
...handlers,
|
|
147
|
-
asReadonly: (frozen?: unknown) =>
|
|
148
|
-
getReadonlyInstance(state, readonlies, handlers, frozen === true),
|
|
149
|
-
at: (index: number): Item | undefined => get(index),
|
|
150
|
-
clear: () => {
|
|
151
|
-
state.value.length = 0;
|
|
152
|
-
},
|
|
153
|
-
filter: (callback: (item: Item, index: number, array: Item[]) => boolean) =>
|
|
154
|
-
computed(() => filter(get(), callback)),
|
|
155
|
-
map: <Mapped>(callback: (item: Item, index: number, array: Item[]) => Mapped) =>
|
|
156
|
-
computed(() => get().map(callback)),
|
|
157
|
-
notify: () => {
|
|
158
|
-
emityProxyValues(state, indiced);
|
|
159
|
-
},
|
|
160
|
-
pop: () => state.value.pop(),
|
|
161
|
-
push: (...items: Item[]) => state.value.push(...items),
|
|
162
|
-
select: <Mapped>(
|
|
163
|
-
filter: (item: Item, index: number, array: Item[]) => boolean,
|
|
164
|
-
map: (item: Item, index: number, array: Item[]) => Mapped,
|
|
165
|
-
) => computed(() => select(get() as Item[], filter, map)),
|
|
166
|
-
set: (first?: unknown, second?: unknown) => {
|
|
167
|
-
set(first, second);
|
|
168
|
-
},
|
|
169
|
-
shift: () => state.value.shift(),
|
|
170
|
-
splice: (from: number, to?: number, ...items: Item[]) =>
|
|
171
|
-
state.value.splice(from, to ?? state.value.length, ...items),
|
|
172
|
-
unshift: (...items: Item[]) => state.value.unshift(...items),
|
|
173
|
-
update: (callback: (value: Item[]) => Item[]) =>
|
|
174
|
-
updateArrayValue(instance as ReactiveArray<Item>, state, callback),
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
Object.defineProperties(instance, {
|
|
178
|
-
[NAME_MORA]: {
|
|
179
|
-
enumerable: false,
|
|
180
|
-
value: NAME_ARRAY,
|
|
181
|
-
},
|
|
182
|
-
length: {
|
|
183
|
-
enumerable: true,
|
|
184
|
-
get: () => length.get(),
|
|
185
|
-
set: (value: number) => setArrayLength(state, value),
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
set(value);
|
|
190
|
-
|
|
191
|
-
return Object.freeze(instance) as ReactiveArray<Item>;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function getArrayValue<Item>(
|
|
195
|
-
instance: ReactiveArray<Item>,
|
|
196
|
-
indiced: Map<number, [Computed<unknown>, ComputedEffect]>,
|
|
197
|
-
state: ReactiveState<Item[], Item>,
|
|
198
|
-
length: Signal<number>,
|
|
199
|
-
first?: unknown,
|
|
200
|
-
): number | Item | Item[] | undefined {
|
|
201
|
-
if (typeof first === 'number') {
|
|
202
|
-
return getReactiveValueInProxy(instance, indiced, first, true).get();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return first === PROPERTY_LENGTH ? length.get() : getSimpleValue(state);
|
|
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);
|
|
206
145
|
}
|
|
207
146
|
|
|
208
|
-
function
|
|
209
|
-
return
|
|
147
|
+
function getArrayValues(this: InternalProxy, value?: unknown): unknown {
|
|
148
|
+
return value === PROPERTY_LENGTH
|
|
149
|
+
? this[SYMBOL_STATE].length!.get()
|
|
150
|
+
: getValueInProxy.call(this, value);
|
|
210
151
|
}
|
|
211
152
|
|
|
212
|
-
function
|
|
213
|
-
return
|
|
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);
|
|
214
157
|
}
|
|
215
158
|
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
159
|
+
function setArrayLength(state: ReactiveState, value: number): void {
|
|
160
|
+
if (
|
|
161
|
+
typeof value === 'number' &&
|
|
162
|
+
!Number.isNaN(value) &&
|
|
163
|
+
value >= 0 &&
|
|
164
|
+
value !== (state.value as unknown[]).length
|
|
165
|
+
) {
|
|
166
|
+
(state.value as unknown[]).length = value;
|
|
224
167
|
}
|
|
225
|
-
|
|
226
|
-
let value: Item | Item[] | undefined;
|
|
227
|
-
|
|
228
|
-
if (typeof first === 'number') {
|
|
229
|
-
value = state.value.at(first);
|
|
230
|
-
} else {
|
|
231
|
-
value = state.value;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (!(first === true || second === true)) {
|
|
235
|
-
return value;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (Array.isArray(value)) {
|
|
239
|
-
return value.slice();
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (isPlainObject(value)) {
|
|
243
|
-
return {...value};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return value;
|
|
247
168
|
}
|
|
248
169
|
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
}
|
|
170
|
+
function updateArray(instance: InternalProxy, type: string, array: unknown[]): unknown {
|
|
171
|
+
const state = instance[SYMBOL_STATE];
|
|
252
172
|
|
|
253
|
-
function setArrayLength<Item>(state: ReactiveState<Item[], Item>, value: number): void {
|
|
254
|
-
if (typeof value === 'number' && value >= 0 && value !== state.value.length) {
|
|
255
|
-
state.value.length = value;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function setAtIndex<Item>(state: ReactiveState<Item[], Item>, index: unknown, value: Item): void {
|
|
260
|
-
const actual = (index as number) < 0 ? state.value.length + (index as number) : (index as number);
|
|
261
|
-
|
|
262
|
-
if (actual > -1) {
|
|
263
|
-
state.value[actual] = value;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function updateArray<Item>(
|
|
268
|
-
type: string,
|
|
269
|
-
array: Item[],
|
|
270
|
-
state: ReactiveState<Item[], Item>,
|
|
271
|
-
length: Signal<number>,
|
|
272
|
-
): unknown {
|
|
273
173
|
const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
|
|
274
174
|
const previousArray = affectsLength ? [] : array.slice();
|
|
275
175
|
const previousLength = array.length;
|
|
276
176
|
|
|
277
|
-
return (...args: unknown[])
|
|
177
|
+
return (...args: unknown[]) => {
|
|
278
178
|
const result = (array[type as never] as (...args: unknown[]) => unknown)(...args);
|
|
279
179
|
|
|
280
180
|
if (
|
|
281
181
|
affectsLength ? array.length !== previousLength : !equalArrays(state, previousArray, array)
|
|
282
182
|
) {
|
|
283
|
-
emitValue(
|
|
183
|
+
emitValue(instance);
|
|
284
184
|
|
|
285
|
-
length
|
|
185
|
+
state.length!.set(array.length);
|
|
286
186
|
}
|
|
287
187
|
|
|
288
188
|
return result;
|
|
289
189
|
};
|
|
290
190
|
}
|
|
291
191
|
|
|
292
|
-
|
|
293
|
-
instance: ReactiveArray<Item>,
|
|
294
|
-
state: ReactiveState<Item[], Item>,
|
|
295
|
-
callback: (value: Item[]) => Item[],
|
|
296
|
-
): void {
|
|
297
|
-
const updated = callback(state.value);
|
|
298
|
-
|
|
299
|
-
if (updated == null || Array.isArray(updated)) {
|
|
300
|
-
instance.set(updated);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
192
|
+
// #endregion
|
package/src/value/computed.ts
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
|
+
import type {GenericAsyncCallback, GenericCallback} from '@oscarpalmer/atoms/models';
|
|
1
2
|
import {flushHandlers} from '../batch';
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ACTIVE,
|
|
5
|
+
BATCH,
|
|
6
|
+
NAME_COMPUTED,
|
|
7
|
+
NAME_MORA,
|
|
8
|
+
SUBSCRIPTION_TYPE_FROZEN,
|
|
9
|
+
SUBSCRIPTION_TYPES,
|
|
10
|
+
SUBSCRIPTION_TYPES_COPY,
|
|
11
|
+
SYMBOL_EFFECT,
|
|
12
|
+
SYMBOL_STATE,
|
|
13
|
+
} from '../constants';
|
|
3
14
|
import {internalEffect, runEffect} from '../effect';
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
15
|
+
import {getState} from '../helpers/misc';
|
|
16
|
+
import {subscribeToSignal} from '../helpers/subscription';
|
|
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
|
|
46
|
+
|
|
47
|
+
// #region Functions
|
|
8
48
|
|
|
9
49
|
/**
|
|
10
50
|
* Create a computed value
|
|
@@ -21,36 +61,22 @@ export function computed<Value>(
|
|
|
21
61
|
}
|
|
22
62
|
|
|
23
63
|
function getComputed<Value>(
|
|
24
|
-
callback:
|
|
64
|
+
callback: GenericCallback,
|
|
25
65
|
options?: ReactiveOptions<Value>,
|
|
26
66
|
): [Computed<Value>, ComputedEffect] {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const instance = {
|
|
32
|
-
...rx,
|
|
33
|
-
get: () => getValue(state, fx),
|
|
34
|
-
peek: () => state.value,
|
|
35
|
-
subscribe: (callback: (value: Value) => void) => subscribe(state, callback),
|
|
36
|
-
unsubscribe: (callback: (value: Value) => void) => {
|
|
37
|
-
state.subscriptions.delete(callback);
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
Object.defineProperty(instance, NAME_MORA, {
|
|
42
|
-
enumerable: false,
|
|
43
|
-
value: NAME_COMPUTED,
|
|
44
|
-
});
|
|
67
|
+
if (typeof callback !== 'function') {
|
|
68
|
+
throw new TypeError('Computed callback must be a function');
|
|
69
|
+
}
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
// @ts-expect-error All good, no worries :-)
|
|
72
|
+
const instance = new Computed(callback, options);
|
|
47
73
|
|
|
48
|
-
return [
|
|
74
|
+
return [instance as never, instance[SYMBOL_EFFECT]];
|
|
49
75
|
}
|
|
50
76
|
|
|
51
|
-
function getComputedEffect
|
|
52
|
-
|
|
53
|
-
callback:
|
|
77
|
+
function getComputedEffect(
|
|
78
|
+
instance: InternalSignal,
|
|
79
|
+
callback: GenericAsyncCallback,
|
|
54
80
|
): ComputedEffect {
|
|
55
81
|
const fx: ComputedEffect = {
|
|
56
82
|
dirty: true,
|
|
@@ -63,7 +89,7 @@ function getComputedEffect<Value>(
|
|
|
63
89
|
|
|
64
90
|
ACTIVE.computed = fx;
|
|
65
91
|
|
|
66
|
-
|
|
92
|
+
handleSignalValue(instance, callback, setAndEmit, () => {
|
|
67
93
|
ACTIVE.computed = previousComputed;
|
|
68
94
|
});
|
|
69
95
|
|
|
@@ -74,12 +100,19 @@ function getComputedEffect<Value>(
|
|
|
74
100
|
return fx;
|
|
75
101
|
}
|
|
76
102
|
|
|
77
|
-
function
|
|
103
|
+
function getComputedValue(this: InternalComputed): unknown {
|
|
104
|
+
const fx = this[SYMBOL_EFFECT];
|
|
105
|
+
const state = this[SYMBOL_STATE];
|
|
106
|
+
|
|
78
107
|
if (ACTIVE.computed != null && fx !== ACTIVE.computed) {
|
|
108
|
+
state.computeds ??= new Set();
|
|
109
|
+
|
|
79
110
|
state.computeds.add(ACTIVE.computed);
|
|
80
111
|
}
|
|
81
112
|
|
|
82
113
|
if (ACTIVE.effect != null && ACTIVE.effect !== fx.instance) {
|
|
114
|
+
state.effects ??= new Set();
|
|
115
|
+
|
|
83
116
|
state.effects.add(ACTIVE.effect);
|
|
84
117
|
}
|
|
85
118
|
|
|
@@ -90,31 +123,51 @@ function getValue<Value>(state: ReactiveState<Value, Value>, fx: ComputedEffect)
|
|
|
90
123
|
return state.value;
|
|
91
124
|
}
|
|
92
125
|
|
|
93
|
-
export function internalComputed
|
|
94
|
-
callback:
|
|
95
|
-
options?: ReactiveOptions<
|
|
96
|
-
): [Computed<
|
|
126
|
+
export function internalComputed(
|
|
127
|
+
callback: GenericCallback,
|
|
128
|
+
options?: ReactiveOptions<unknown>,
|
|
129
|
+
): [Computed<unknown>, ComputedEffect] {
|
|
97
130
|
return getComputed(callback, options);
|
|
98
131
|
}
|
|
99
132
|
|
|
100
|
-
function setAndEmit
|
|
101
|
-
|
|
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)) {
|
|
102
137
|
return;
|
|
103
138
|
}
|
|
104
139
|
|
|
105
140
|
state.value = value;
|
|
106
141
|
|
|
107
|
-
|
|
108
|
-
computed.
|
|
142
|
+
if (state.computeds != null && state.computeds.size > 0) {
|
|
143
|
+
for (const computed of state.computeds) {
|
|
144
|
+
computed.dirty = true;
|
|
145
|
+
}
|
|
109
146
|
}
|
|
110
147
|
|
|
111
|
-
|
|
112
|
-
|
|
148
|
+
if (state.effects != null && state.effects.size > 0) {
|
|
149
|
+
for (const effect of state.effects) {
|
|
150
|
+
BATCH.handlers.set(effect, effect);
|
|
151
|
+
}
|
|
113
152
|
}
|
|
114
153
|
|
|
115
|
-
for (const
|
|
116
|
-
|
|
154
|
+
for (const type of SUBSCRIPTION_TYPES) {
|
|
155
|
+
if (type === SUBSCRIPTION_TYPE_FROZEN) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
|
|
160
|
+
|
|
161
|
+
if (subscriptions != null && subscriptions.size > 0) {
|
|
162
|
+
const copy = SUBSCRIPTION_TYPES_COPY.has(type);
|
|
163
|
+
|
|
164
|
+
for (const [, callback] of subscriptions) {
|
|
165
|
+
callback(peekSignalValue.call(instance, copy));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
117
168
|
}
|
|
118
169
|
|
|
119
170
|
flushHandlers();
|
|
120
171
|
}
|
|
172
|
+
|
|
173
|
+
// #endregion
|