@oscarpalmer/mora 0.31.0 → 0.32.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 +29 -19
- package/dist/constants.mjs +17 -2
- package/dist/effect.d.mts +4 -5
- package/dist/effect.mjs +2 -2
- package/dist/helpers/is.d.mts +14 -9
- package/dist/helpers/is.mjs +6 -0
- package/dist/helpers/proxy.d.mts +12 -10
- package/dist/helpers/proxy.mjs +32 -14
- package/dist/helpers/subscription.d.mts +8 -0
- package/dist/helpers/subscription.mjs +27 -0
- package/dist/helpers/value.d.mts +8 -6
- package/dist/helpers/value.mjs +30 -4
- package/dist/index.d.mts +3 -2
- package/dist/models.d.mts +45 -87
- package/dist/mora.full.mjs +461 -285
- package/dist/value/array.d.mts +4 -5
- package/dist/value/array.mjs +22 -69
- package/dist/value/computed.d.mts +3 -4
- package/dist/value/computed.mjs +14 -14
- package/dist/value/reactive.d.mts +2 -3
- package/dist/value/reactive.mjs +0 -1
- package/dist/value/readonly.d.mts +3 -5
- package/dist/value/readonly.mjs +15 -19
- package/dist/value/signal.d.mts +4 -5
- package/dist/value/signal.mjs +9 -22
- package/dist/value/store.d.mts +4 -5
- package/dist/value/store.mjs +16 -56
- package/package.json +6 -6
- package/src/batch.ts +15 -5
- package/src/constants.ts +32 -2
- package/src/effect.ts +6 -2
- package/src/helpers/is.ts +10 -0
- package/src/helpers/proxy.ts +99 -49
- package/src/helpers/subscription.ts +78 -0
- package/src/helpers/value.ts +69 -4
- package/src/index.ts +1 -1
- package/src/models.ts +38 -81
- package/src/value/array.ts +61 -150
- package/src/value/computed.ts +36 -13
- package/src/value/reactive.ts +8 -5
- package/src/value/readonly.ts +26 -25
- package/src/value/signal.ts +19 -22
- package/src/value/store.ts +35 -114
- 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/src/subscription.ts +0 -45
package/src/value/array.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
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,
|
|
@@ -11,12 +8,15 @@ import {
|
|
|
11
8
|
PROPERTY_LENGTH,
|
|
12
9
|
} from '../constants';
|
|
13
10
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
emitProxyValues,
|
|
12
|
+
getValueInProxy,
|
|
13
|
+
peekValueInProxy,
|
|
16
14
|
setProxyValue,
|
|
17
15
|
setValueInProxy,
|
|
16
|
+
updateProxyValue,
|
|
18
17
|
} from '../helpers/proxy';
|
|
19
|
-
import {
|
|
18
|
+
import {subscribeToProxy} from '../helpers/subscription';
|
|
19
|
+
import {emitValue, equalArrays} from '../helpers/value';
|
|
20
20
|
import type {
|
|
21
21
|
Computed,
|
|
22
22
|
ComputedEffect,
|
|
@@ -26,12 +26,13 @@ import type {
|
|
|
26
26
|
ReadonlyInstances,
|
|
27
27
|
Signal,
|
|
28
28
|
} from '../models';
|
|
29
|
-
import {subscribe, unsubscribe} from '../subscription';
|
|
30
29
|
import {computed} from './computed';
|
|
31
30
|
import {reactive} from './reactive';
|
|
32
31
|
import {getReadonlyInstance} from './readonly';
|
|
33
32
|
import {signal} from './signal';
|
|
34
33
|
|
|
34
|
+
// #region Functions
|
|
35
|
+
|
|
35
36
|
/**
|
|
36
37
|
* Create a reactive array from a function result
|
|
37
38
|
*
|
|
@@ -71,26 +72,18 @@ export function array<Item>(
|
|
|
71
72
|
): ReactiveArray<Item> {
|
|
72
73
|
const [rx, state] = reactive<Item[], Item>([], options);
|
|
73
74
|
|
|
74
|
-
const
|
|
75
|
+
const isArray = true;
|
|
75
76
|
const length = signal(0);
|
|
77
|
+
const mapped = new Map<number, [Computed<unknown>, ComputedEffect]>();
|
|
76
78
|
const readonlies: ReadonlyInstances<Item[]> = {};
|
|
77
79
|
|
|
78
|
-
let instance: Record<string, unknown> = {};
|
|
79
|
-
|
|
80
80
|
state.value = new Proxy([], {
|
|
81
81
|
get: (target: Item[], property: PropertyKey) =>
|
|
82
82
|
METHODS_UPDATE.has(property as string)
|
|
83
83
|
? updateArray(property as string, target, state, length)
|
|
84
84
|
: Reflect.get(target, property),
|
|
85
85
|
set: (target: Item[], property: PropertyKey, value: Item) =>
|
|
86
|
-
setValueInProxy(
|
|
87
|
-
length,
|
|
88
|
-
property,
|
|
89
|
-
state,
|
|
90
|
-
target,
|
|
91
|
-
value,
|
|
92
|
-
isArray: true,
|
|
93
|
-
}),
|
|
86
|
+
setValueInProxy(isArray, state, target, property, value, length),
|
|
94
87
|
});
|
|
95
88
|
|
|
96
89
|
function get(): Item[];
|
|
@@ -98,85 +91,43 @@ export function array<Item>(
|
|
|
98
91
|
function get(property: typeof PROPERTY_LENGTH): number;
|
|
99
92
|
function get(value?: unknown): number | Item | Item[] | undefined;
|
|
100
93
|
|
|
101
|
-
function get(value?: unknown):
|
|
102
|
-
return
|
|
94
|
+
function get(value?: unknown): unknown {
|
|
95
|
+
return value === PROPERTY_LENGTH
|
|
96
|
+
? length.get()
|
|
97
|
+
: getValueInProxy(isArray, instance as never, state, mapped, value);
|
|
103
98
|
}
|
|
104
99
|
|
|
105
|
-
function set(first?:
|
|
106
|
-
setProxyValue<Item[], Item>(
|
|
107
|
-
true,
|
|
108
|
-
state,
|
|
109
|
-
isArrayValue,
|
|
110
|
-
isArrayIndex,
|
|
111
|
-
setArray,
|
|
112
|
-
setAtIndex,
|
|
113
|
-
first,
|
|
114
|
-
second,
|
|
115
|
-
);
|
|
100
|
+
function set(first?: never, second?: never): void {
|
|
101
|
+
setProxyValue<Item[], Item>(true, state, setArrayValue, setAtIndex, first, second);
|
|
116
102
|
}
|
|
117
103
|
|
|
118
|
-
const
|
|
104
|
+
const instance = {
|
|
119
105
|
...rx,
|
|
120
|
-
|
|
121
|
-
|
|
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),
|
|
106
|
+
asReadonly: (frozen?: never) => getReadonlyInstance(state, readonlies, frozen === true),
|
|
107
|
+
at: (index: never) => get(index),
|
|
150
108
|
clear: () => {
|
|
151
109
|
state.value.length = 0;
|
|
152
110
|
},
|
|
153
|
-
filter: (callback:
|
|
154
|
-
|
|
155
|
-
map:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
emityProxyValues(state, indiced);
|
|
159
|
-
},
|
|
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),
|
|
160
116
|
pop: () => state.value.pop(),
|
|
161
117
|
push: (...items: Item[]) => state.value.push(...items),
|
|
162
|
-
select:
|
|
163
|
-
|
|
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
|
-
},
|
|
118
|
+
select: (filter: never, map: never) => computed(() => select(get(), filter, map)),
|
|
119
|
+
set: (first?: never, second?: never) => set(first, second),
|
|
169
120
|
shift: () => state.value.shift(),
|
|
170
|
-
splice: (from:
|
|
121
|
+
splice: (from: never, to?: never, ...items: Item[]) =>
|
|
171
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),
|
|
172
125
|
unshift: (...items: Item[]) => state.value.unshift(...items),
|
|
173
|
-
update: (callback:
|
|
174
|
-
updateArrayValue(instance as ReactiveArray<Item>, state, callback),
|
|
126
|
+
update: (callback: never) => updateProxyValue(true, state, setArrayValue, setAtIndex, callback),
|
|
175
127
|
};
|
|
176
128
|
|
|
177
129
|
Object.defineProperties(instance, {
|
|
178
130
|
[NAME_MORA]: {
|
|
179
|
-
enumerable: false,
|
|
180
131
|
value: NAME_ARRAY,
|
|
181
132
|
},
|
|
182
133
|
length: {
|
|
@@ -186,31 +137,9 @@ export function array<Item>(
|
|
|
186
137
|
},
|
|
187
138
|
});
|
|
188
139
|
|
|
189
|
-
set(value);
|
|
140
|
+
set(value as never);
|
|
190
141
|
|
|
191
|
-
return Object.freeze(instance) as
|
|
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);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function isArrayIndex(value: unknown): boolean {
|
|
209
|
-
return typeof value === 'number';
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function isArrayValue<Item>(value: unknown): value is Item[] | undefined {
|
|
213
|
-
return value == null || Array.isArray(value);
|
|
142
|
+
return Object.freeze(instance) as never;
|
|
214
143
|
}
|
|
215
144
|
|
|
216
145
|
function peekArrayValue<Item>(
|
|
@@ -218,49 +147,41 @@ function peekArrayValue<Item>(
|
|
|
218
147
|
length: Signal<number>,
|
|
219
148
|
first?: unknown,
|
|
220
149
|
second?: boolean,
|
|
221
|
-
):
|
|
222
|
-
|
|
223
|
-
return length.peek();
|
|
224
|
-
}
|
|
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
|
-
}
|
|
248
|
-
|
|
249
|
-
function setArray<Item>(state: ReactiveState<Item[], Item>, value: Item[] | undefined): void {
|
|
250
|
-
state.value.splice(0, state.value.length, ...(value ?? []));
|
|
150
|
+
): unknown {
|
|
151
|
+
return first === PROPERTY_LENGTH ? length.peek() : peekValueInProxy(true, state, first, second);
|
|
251
152
|
}
|
|
252
153
|
|
|
253
154
|
function setArrayLength<Item>(state: ReactiveState<Item[], Item>, value: number): void {
|
|
254
|
-
if (
|
|
155
|
+
if (
|
|
156
|
+
typeof value === 'number' &&
|
|
157
|
+
!Number.isNaN(value) &&
|
|
158
|
+
value >= 0 &&
|
|
159
|
+
value !== state.value.length
|
|
160
|
+
) {
|
|
255
161
|
state.value.length = value;
|
|
256
162
|
}
|
|
257
163
|
}
|
|
258
164
|
|
|
259
|
-
function
|
|
260
|
-
|
|
165
|
+
function setArrayValue<Value, Item = Value>(state: ReactiveState<Value, Item>, value: Value): void {
|
|
166
|
+
(state.value as unknown[]).splice(
|
|
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);
|
|
261
182
|
|
|
262
183
|
if (actual > -1) {
|
|
263
|
-
state.value[actual] = value;
|
|
184
|
+
(state.value as unknown[])[actual] = value;
|
|
264
185
|
}
|
|
265
186
|
}
|
|
266
187
|
|
|
@@ -274,7 +195,7 @@ function updateArray<Item>(
|
|
|
274
195
|
const previousArray = affectsLength ? [] : array.slice();
|
|
275
196
|
const previousLength = array.length;
|
|
276
197
|
|
|
277
|
-
return (...args: unknown[])
|
|
198
|
+
return (...args: unknown[]) => {
|
|
278
199
|
const result = (array[type as never] as (...args: unknown[]) => unknown)(...args);
|
|
279
200
|
|
|
280
201
|
if (
|
|
@@ -289,14 +210,4 @@ function updateArray<Item>(
|
|
|
289
210
|
};
|
|
290
211
|
}
|
|
291
212
|
|
|
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
|
-
}
|
|
213
|
+
// #endregion
|
package/src/value/computed.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import {flushHandlers} from '../batch';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ACTIVE,
|
|
4
|
+
BATCH,
|
|
5
|
+
NAME_COMPUTED,
|
|
6
|
+
NAME_MORA,
|
|
7
|
+
SUBSCRIPTION_TYPES_COPY,
|
|
8
|
+
SUBSCRIPTION_TYPE_FROZEN,
|
|
9
|
+
SUBSCRIPTION_TYPES,
|
|
10
|
+
} from '../constants';
|
|
3
11
|
import {internalEffect, runEffect} from '../effect';
|
|
4
|
-
import {
|
|
12
|
+
import {subscribeToSignal} from '../helpers/subscription';
|
|
13
|
+
import {handleSimpleValue, peekSimpleValue} from '../helpers/value';
|
|
5
14
|
import type {Computed, ComputedEffect, ReactiveOptions, ReactiveState} from '../models';
|
|
6
|
-
import {subscribe} from '../subscription';
|
|
7
15
|
import {reactive} from './reactive';
|
|
8
16
|
|
|
17
|
+
// #region Functions
|
|
18
|
+
|
|
9
19
|
/**
|
|
10
20
|
* Create a computed value
|
|
11
21
|
*
|
|
@@ -24,6 +34,10 @@ function getComputed<Value>(
|
|
|
24
34
|
callback: () => Value | Promise<Value>,
|
|
25
35
|
options?: ReactiveOptions<Value>,
|
|
26
36
|
): [Computed<Value>, ComputedEffect] {
|
|
37
|
+
if (typeof callback !== 'function') {
|
|
38
|
+
throw new TypeError('Computed callback must be a function');
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
const [rx, state] = reactive<Value>(undefined as never, options);
|
|
28
42
|
|
|
29
43
|
let fx: ComputedEffect;
|
|
@@ -31,21 +45,17 @@ function getComputed<Value>(
|
|
|
31
45
|
const instance = {
|
|
32
46
|
...rx,
|
|
33
47
|
get: () => getValue(state, fx),
|
|
34
|
-
peek: () => state.value,
|
|
35
|
-
subscribe: (callback:
|
|
36
|
-
unsubscribe: (callback: (value: Value) => void) => {
|
|
37
|
-
state.subscriptions.delete(callback);
|
|
38
|
-
},
|
|
48
|
+
peek: (copy?: never) => peekSimpleValue(state.value, copy === true),
|
|
49
|
+
subscribe: (callback: never, copy?: never) => subscribeToSignal(state, callback, copy === true),
|
|
39
50
|
};
|
|
40
51
|
|
|
41
52
|
Object.defineProperty(instance, NAME_MORA, {
|
|
42
|
-
enumerable: false,
|
|
43
53
|
value: NAME_COMPUTED,
|
|
44
54
|
});
|
|
45
55
|
|
|
46
56
|
fx = getComputedEffect(state, callback);
|
|
47
57
|
|
|
48
|
-
return [Object.freeze(instance), fx];
|
|
58
|
+
return [Object.freeze(instance) as never, fx];
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
function getComputedEffect<Value>(
|
|
@@ -109,12 +119,25 @@ function setAndEmit<Value>(state: ReactiveState<Value, Value>, value: Value): vo
|
|
|
109
119
|
}
|
|
110
120
|
|
|
111
121
|
for (const effect of state.effects) {
|
|
112
|
-
BATCH.handlers.
|
|
122
|
+
BATCH.handlers.set(effect, effect);
|
|
113
123
|
}
|
|
114
124
|
|
|
115
|
-
for (const
|
|
116
|
-
|
|
125
|
+
for (const type of SUBSCRIPTION_TYPES) {
|
|
126
|
+
if (type === SUBSCRIPTION_TYPE_FROZEN) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const copy = SUBSCRIPTION_TYPES_COPY.has(type);
|
|
131
|
+
const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
|
|
132
|
+
|
|
133
|
+
if (subscriptions != null) {
|
|
134
|
+
for (const [, callback] of subscriptions) {
|
|
135
|
+
callback(peekSimpleValue(state.value, copy));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
117
138
|
}
|
|
118
139
|
|
|
119
140
|
flushHandlers();
|
|
120
141
|
}
|
|
142
|
+
|
|
143
|
+
// #endregion
|
package/src/value/reactive.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import type {Reactive, ReactiveOptions, ReactiveState} from '../models';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// #region Functions
|
|
4
|
+
|
|
5
|
+
export function reactive<Value, Item = Value>(
|
|
4
6
|
value: Value,
|
|
5
|
-
options?: ReactiveOptions<
|
|
6
|
-
): [Reactive<Value>, ReactiveState<Value,
|
|
7
|
-
const state: ReactiveState<Value,
|
|
7
|
+
options?: ReactiveOptions<Item>,
|
|
8
|
+
): [Reactive<Value>, ReactiveState<Value, Item>] {
|
|
9
|
+
const state: ReactiveState<Value, Item> = {
|
|
8
10
|
computeds: new Set(),
|
|
9
11
|
effects: new Set(),
|
|
10
12
|
equal:
|
|
11
13
|
typeof options === 'object' && typeof options?.equal === 'function'
|
|
12
14
|
? options.equal
|
|
13
15
|
: Object.is,
|
|
14
|
-
subscriptions: new Map(),
|
|
15
16
|
value: value as Value,
|
|
16
17
|
};
|
|
17
18
|
|
|
@@ -22,3 +23,5 @@ export function reactive<Value, Equal = Value>(
|
|
|
22
23
|
|
|
23
24
|
return [instance, state];
|
|
24
25
|
}
|
|
26
|
+
|
|
27
|
+
// #endregion
|
package/src/value/readonly.ts
CHANGED
|
@@ -1,42 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
NAME_MORA,
|
|
3
|
+
NAME_READONLY,
|
|
4
|
+
SUBSCRIPTION_TYPE_FROZEN,
|
|
5
|
+
SUBSCRIPTION_TYPE_READONLY,
|
|
6
|
+
} from '../constants';
|
|
7
|
+
import {subscribeToReactive} from '../helpers/subscription';
|
|
8
|
+
import {getFrozenValue, getSimpleValue, peekSimpleValue} from '../helpers/value';
|
|
5
9
|
import type {
|
|
6
10
|
ReactiveState,
|
|
7
11
|
ReadonlyFrozenSignal,
|
|
8
12
|
ReadonlyInstances,
|
|
9
13
|
ReadonlySignal,
|
|
10
|
-
ReadonlySignalValue,
|
|
11
14
|
} from '../models';
|
|
12
15
|
|
|
16
|
+
// #region Functions
|
|
17
|
+
|
|
13
18
|
export function getReadonlyInstance<Value>(
|
|
14
19
|
state: ReactiveState<Value, never>,
|
|
15
20
|
instances: ReadonlyInstances<Value>,
|
|
16
|
-
handlers: Record<string, GenericCallback>,
|
|
17
21
|
frozen: boolean,
|
|
18
22
|
): ReadonlySignal<Value> {
|
|
19
23
|
const key = frozen ? 'frozen' : 'original';
|
|
20
24
|
|
|
21
|
-
instances[key] ??= getReadonlySignal(state,
|
|
25
|
+
instances[key] ??= getReadonlySignal(state, frozen) as never;
|
|
22
26
|
|
|
23
|
-
return instances[key] as
|
|
27
|
+
return instances[key] as never;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export function getReadonlySignal<Value>(
|
|
27
31
|
state: ReactiveState<Value, never>,
|
|
28
|
-
handlers: Record<string, GenericCallback>,
|
|
29
32
|
frozen: boolean,
|
|
30
33
|
): ReadonlySignal<Value> | ReadonlyFrozenSignal<Value> {
|
|
34
|
+
const type = frozen ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_READONLY;
|
|
35
|
+
|
|
31
36
|
const instance = {
|
|
32
|
-
...handlers,
|
|
33
37
|
get: () => getReadonlyValue(state, false, frozen),
|
|
34
|
-
peek: () => getReadonlyValue(state, true, frozen),
|
|
38
|
+
peek: (copy?: boolean) => getReadonlyValue(state, true, frozen, copy),
|
|
39
|
+
subscribe: (callback: never) => subscribeToReactive(type, state, callback),
|
|
35
40
|
};
|
|
36
41
|
|
|
37
42
|
Object.defineProperties(instance, {
|
|
38
43
|
[NAME_MORA]: {
|
|
39
|
-
enumerable: false,
|
|
40
44
|
value: NAME_READONLY,
|
|
41
45
|
},
|
|
42
46
|
frozen: {
|
|
@@ -45,27 +49,24 @@ export function getReadonlySignal<Value>(
|
|
|
45
49
|
},
|
|
46
50
|
});
|
|
47
51
|
|
|
48
|
-
return Object.freeze(instance) as
|
|
52
|
+
return Object.freeze(instance) as never;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
function getReadonlyValue<Value>(
|
|
52
56
|
state: ReactiveState<Value, never>,
|
|
53
57
|
peek: boolean,
|
|
54
58
|
frozen: boolean,
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
copy?: boolean,
|
|
60
|
+
): unknown {
|
|
61
|
+
let value: unknown;
|
|
57
62
|
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (Array.isArray(state.value)) {
|
|
63
|
-
value = [...state.value] as Value;
|
|
64
|
-
} else if (isPlainObject(state.value)) {
|
|
65
|
-
value = {...state.value} as Value;
|
|
63
|
+
if (peek) {
|
|
64
|
+
value = peekSimpleValue(state.value, copy === true);
|
|
66
65
|
} else {
|
|
67
|
-
value = state
|
|
66
|
+
value = getSimpleValue(state);
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
return
|
|
69
|
+
return frozen ? getFrozenValue(value) : value;
|
|
71
70
|
}
|
|
71
|
+
|
|
72
|
+
// #endregion
|
package/src/value/signal.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import {NAME_MORA, NAME_SIGNAL} from '../constants';
|
|
2
|
-
import {
|
|
2
|
+
import {subscribeToSignal} from '../helpers/subscription';
|
|
3
|
+
import {
|
|
4
|
+
emitValue,
|
|
5
|
+
getSimpleValue,
|
|
6
|
+
handleSimpleValue,
|
|
7
|
+
peekSimpleValue,
|
|
8
|
+
updateSimpleValue,
|
|
9
|
+
} from '../helpers/value';
|
|
3
10
|
import type {ReactiveOptions, ReactiveState, ReadonlyInstances, Signal} from '../models';
|
|
4
|
-
import {subscribe} from '../subscription';
|
|
5
11
|
import {reactive} from './reactive';
|
|
6
12
|
import {getReadonlyInstance} from './readonly';
|
|
7
13
|
|
|
14
|
+
// #region Functions
|
|
15
|
+
|
|
8
16
|
function setAndEmit<Value>(state: ReactiveState<Value, Value>, value: Value): void {
|
|
9
17
|
if (!state.equal(state.value, value)) {
|
|
10
18
|
state.value = value;
|
|
@@ -54,34 +62,23 @@ export function signal<Value>(
|
|
|
54
62
|
|
|
55
63
|
const readonlies: ReadonlyInstances<Value> = {};
|
|
56
64
|
|
|
57
|
-
const handlers = {
|
|
58
|
-
...rx,
|
|
59
|
-
subscribe: (callback: (value: Value) => void) => subscribe(state, callback),
|
|
60
|
-
unsubscribe: (callback: (value: Value) => void) => {
|
|
61
|
-
state.subscriptions.delete(callback);
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
65
|
const instance = {
|
|
66
|
-
...
|
|
67
|
-
asReadonly: (frozen?:
|
|
68
|
-
getReadonlyInstance(state, readonlies, handlers, frozen === true),
|
|
66
|
+
...rx,
|
|
67
|
+
asReadonly: (frozen?: never) => getReadonlyInstance(state, readonlies, frozen === true),
|
|
69
68
|
get: () => getSimpleValue(state),
|
|
70
|
-
peek: () => state.value,
|
|
71
|
-
set: (value:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
update: (callback: (value: Value) => Value) => {
|
|
75
|
-
handleSimpleValue(state, callback(state.value), setAndEmit);
|
|
76
|
-
},
|
|
69
|
+
peek: (copy?: boolean) => peekSimpleValue(state.value, copy === true),
|
|
70
|
+
set: (value: never) => handleSimpleValue(state, value, setAndEmit),
|
|
71
|
+
subscribe: (callback: never, copy?: never) => subscribeToSignal(state, callback, copy === true),
|
|
72
|
+
update: (callback: never) => updateSimpleValue(state, callback, setAndEmit),
|
|
77
73
|
};
|
|
78
74
|
|
|
79
75
|
Object.defineProperty(instance, NAME_MORA, {
|
|
80
|
-
enumerable: false,
|
|
81
76
|
value: NAME_SIGNAL,
|
|
82
77
|
});
|
|
83
78
|
|
|
84
79
|
handleSimpleValue(state, value, setAndEmit);
|
|
85
80
|
|
|
86
|
-
return Object.freeze(instance) as
|
|
81
|
+
return Object.freeze(instance) as never;
|
|
87
82
|
}
|
|
83
|
+
|
|
84
|
+
// #endregion
|