@oscarpalmer/mora 0.21.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 +11 -25
- package/dist/constants.js +34 -0
- package/dist/effect.js +18 -28
- package/dist/helpers/is.js +9 -26
- package/dist/helpers/proxy.js +36 -48
- package/dist/helpers/value.js +21 -48
- package/dist/index.js +3 -16
- package/dist/models.js +0 -0
- package/dist/mora.full.js +218 -130
- package/dist/subscription.js +22 -28
- package/dist/value/array.js +97 -182
- package/dist/value/computed.js +33 -58
- package/dist/value/reactive.js +29 -53
- package/dist/value/signal.js +21 -33
- package/dist/value/store.js +37 -60
- package/package.json +17 -24
- 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 -1
- package/src/models.ts +66 -0
- package/src/subscription.ts +14 -16
- package/src/value/array.ts +87 -45
- 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/batch.cjs +0 -32
- package/dist/effect.cjs +0 -30
- package/dist/helpers/is.cjs +0 -40
- package/dist/helpers/proxy.cjs +0 -56
- package/dist/helpers/value.cjs +0 -54
- package/dist/index.cjs +0 -21
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/is.cjs +0 -17
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/is.js +0 -17
- package/dist/subscription.cjs +0 -32
- package/dist/value/array.cjs +0 -187
- package/dist/value/computed.cjs +0 -60
- package/dist/value/reactive.cjs +0 -55
- package/dist/value/signal.cjs +0 -36
- package/dist/value/store.cjs +0 -63
- package/types/batch.d.cts +0 -79
- package/types/effect.d.cts +0 -16
- package/types/helpers/is.d.cts +0 -259
- package/types/helpers/proxy.d.cts +0 -244
- package/types/helpers/value.d.cts +0 -71
- package/types/index.d.cts +0 -281
- package/types/subscription.d.cts +0 -71
- package/types/value/array.d.cts +0 -161
- package/types/value/computed.d.cts +0 -81
- package/types/value/reactive.d.cts +0 -68
- package/types/value/signal.d.cts +0 -87
- package/types/value/store.d.cts +0 -136
package/src/value/array.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import type {GenericCallback} from '@oscarpalmer/atoms';
|
|
2
|
-
import {
|
|
1
|
+
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {
|
|
3
|
+
METHODS_AFFECTING_LENGTH,
|
|
4
|
+
METHODS_UPDATE,
|
|
5
|
+
NAME_ARRAY,
|
|
6
|
+
} from '../constants';
|
|
3
7
|
import {getReactiveValueInProxy, setValueInProxy} from '../helpers/proxy';
|
|
4
8
|
import {emitValue, equalArrays, getValue} from '../helpers/value';
|
|
5
|
-
import
|
|
9
|
+
import type {ReactiveOptions, ReactiveState, Unsubscribe} from '../models';
|
|
10
|
+
import {noop, subscribe} from '../subscription';
|
|
6
11
|
import {type Computed, computed} from './computed';
|
|
7
|
-
import {Reactive
|
|
12
|
+
import {Reactive} from './reactive';
|
|
8
13
|
import {type Signal, signal} from './signal';
|
|
9
14
|
|
|
10
15
|
export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
11
|
-
#indiced = new Map<number, Computed<unknown>>();
|
|
12
|
-
|
|
16
|
+
readonly #indiced = new Map<number, Computed<unknown>>();
|
|
17
|
+
|
|
18
|
+
readonly #size = signal(0);
|
|
13
19
|
|
|
14
20
|
/**
|
|
15
21
|
* The length of the array
|
|
@@ -33,21 +39,21 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
33
39
|
|
|
34
40
|
constructor(value: Item[], options?: ReactiveOptions<Item>) {
|
|
35
41
|
super(
|
|
36
|
-
|
|
42
|
+
NAME_ARRAY,
|
|
37
43
|
new Proxy(value, {
|
|
38
|
-
get: (target, property) =>
|
|
39
|
-
|
|
44
|
+
get: (target: Item[], property: PropertyKey) =>
|
|
45
|
+
METHODS_UPDATE.has(property as string)
|
|
40
46
|
? updateArray(property as string, target, this.state, this.#size)
|
|
41
47
|
: Reflect.get(target, property),
|
|
42
|
-
set: (target, property, value) =>
|
|
43
|
-
setValueInProxy(
|
|
44
|
-
target,
|
|
48
|
+
set: (target: Item[], property: PropertyKey, value: Item) =>
|
|
49
|
+
setValueInProxy({
|
|
45
50
|
property,
|
|
51
|
+
target,
|
|
46
52
|
value,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.#size,
|
|
50
|
-
),
|
|
53
|
+
isArray: true,
|
|
54
|
+
state: this.state,
|
|
55
|
+
length: this.#size,
|
|
56
|
+
}),
|
|
51
57
|
}),
|
|
52
58
|
options,
|
|
53
59
|
);
|
|
@@ -62,6 +68,17 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
62
68
|
this.length = 0;
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Create a computed, filtered array
|
|
73
|
+
* @param callback Callback to evaluate each item
|
|
74
|
+
* @return Computed array of filtered items
|
|
75
|
+
*/
|
|
76
|
+
filter(
|
|
77
|
+
callback: (item: Item, index: number, array: Item[]) => boolean,
|
|
78
|
+
): Computed<Item[]> {
|
|
79
|
+
return computed(() => this.get().filter(callback));
|
|
80
|
+
}
|
|
81
|
+
|
|
65
82
|
/**
|
|
66
83
|
* @inheritdoc
|
|
67
84
|
*/
|
|
@@ -69,11 +86,14 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
69
86
|
|
|
70
87
|
/**
|
|
71
88
|
* Get the value at an index
|
|
89
|
+
* @param index Index of item to get _(if negative, starts from the end)_
|
|
90
|
+
* @returns Item at index, or `undefined` if it doesn't exist
|
|
72
91
|
*/
|
|
73
92
|
get(index: number): Item | undefined;
|
|
74
93
|
|
|
75
94
|
/**
|
|
76
95
|
* Get the length of the array
|
|
96
|
+
* @returns Length of the array
|
|
77
97
|
*/
|
|
78
98
|
get(property: 'length'): number;
|
|
79
99
|
|
|
@@ -89,17 +109,10 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
89
109
|
return getValue(this.state);
|
|
90
110
|
}
|
|
91
111
|
|
|
92
|
-
/**
|
|
93
|
-
* Create a computed, filtered array
|
|
94
|
-
*/
|
|
95
|
-
filter(
|
|
96
|
-
callback: (item: Item, index: number, array: Item[]) => boolean,
|
|
97
|
-
): Computed<Item[]> {
|
|
98
|
-
return computed(() => this.get().filter(callback));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
112
|
/**
|
|
102
113
|
* Create a computed, mapped array
|
|
114
|
+
* @param callback Callback to transform each item
|
|
115
|
+
* @return Computed array of mapped items
|
|
103
116
|
*/
|
|
104
117
|
map<Mapped>(
|
|
105
118
|
callback: (item: Item, index: number, array: Item[]) => Mapped,
|
|
@@ -107,20 +120,36 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
107
120
|
return computed(() => this.get().map(callback));
|
|
108
121
|
}
|
|
109
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Notify dependents of changes
|
|
125
|
+
*
|
|
126
|
+
* _This bypasses equality checks and will immediately notify dependents.
|
|
127
|
+
* Use this only if you're modifying nested data that would be ignored by equality checks._
|
|
128
|
+
*/
|
|
129
|
+
notify(): void {
|
|
130
|
+
emitValue(this.state);
|
|
131
|
+
}
|
|
132
|
+
|
|
110
133
|
/**
|
|
111
134
|
* @inheritdoc
|
|
112
135
|
*/
|
|
113
136
|
peek(): Item[];
|
|
114
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Get the value at an index _(without reactivity)_
|
|
140
|
+
* @param index Index of item to get _(if negative, starts from the end)_
|
|
141
|
+
* @returns Item at index, or `undefined` if it doesn't exist
|
|
142
|
+
*/
|
|
115
143
|
peek(index: number): Item | undefined;
|
|
116
144
|
|
|
117
145
|
/**
|
|
118
146
|
* Get the length of the array _(without reactivity)_
|
|
147
|
+
* @returns Length of the array
|
|
119
148
|
*/
|
|
120
|
-
peek(
|
|
149
|
+
peek(property: 'length'): number;
|
|
121
150
|
|
|
122
151
|
peek(value?: unknown): unknown {
|
|
123
|
-
if (value ===
|
|
152
|
+
if (value === 'length') {
|
|
124
153
|
return this.#size.peek();
|
|
125
154
|
}
|
|
126
155
|
|
|
@@ -133,6 +162,7 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
133
162
|
|
|
134
163
|
/**
|
|
135
164
|
* Remove and return the last item of the array
|
|
165
|
+
* @returns Removed item, or `undefined` if the array is empty
|
|
136
166
|
*/
|
|
137
167
|
pop(): Item | undefined {
|
|
138
168
|
return this.state.value.pop();
|
|
@@ -140,6 +170,8 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
140
170
|
|
|
141
171
|
/**
|
|
142
172
|
* Add items to the end of the array
|
|
173
|
+
* @param items Items to add
|
|
174
|
+
* @returns New array length
|
|
143
175
|
*/
|
|
144
176
|
push(...items: Item[]): number {
|
|
145
177
|
return this.state.value.push(...items);
|
|
@@ -147,16 +179,20 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
147
179
|
|
|
148
180
|
/**
|
|
149
181
|
* Set the value
|
|
182
|
+
* @param value New array of items _(defaults to an empty array)_
|
|
150
183
|
*/
|
|
151
184
|
set(value?: Item[]): void;
|
|
152
185
|
|
|
153
186
|
/**
|
|
154
187
|
* Set the value at an index
|
|
188
|
+
* @param index Index of item to set __(if negative, starts from the end)_
|
|
189
|
+
* @param value New item
|
|
155
190
|
*/
|
|
156
191
|
set(index: number, value: Item): void;
|
|
157
192
|
|
|
158
193
|
/**
|
|
159
194
|
* Set the length of the array
|
|
195
|
+
* @param value New array length
|
|
160
196
|
*/
|
|
161
197
|
set(property: 'length', value: number): void;
|
|
162
198
|
|
|
@@ -165,13 +201,14 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
165
201
|
this.state.value.splice(0, this.state.value.length, ...(first ?? []));
|
|
166
202
|
} else if (first === 'length') {
|
|
167
203
|
this.length = second as number;
|
|
168
|
-
} else if (typeof first === 'number') {
|
|
169
|
-
this.state.value
|
|
204
|
+
} else if (typeof first === 'number' && !Number.isNaN(first)) {
|
|
205
|
+
setAtIndex(this.state.value, first, second as Item);
|
|
170
206
|
}
|
|
171
207
|
}
|
|
172
208
|
|
|
173
209
|
/**
|
|
174
210
|
* Remove and return the first item of the array
|
|
211
|
+
* @returns Removed item, or `undefined` if the array is empty
|
|
175
212
|
*/
|
|
176
213
|
shift(): Item | undefined {
|
|
177
214
|
return this.state.value.shift();
|
|
@@ -179,6 +216,10 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
179
216
|
|
|
180
217
|
/**
|
|
181
218
|
* Remove and return items from the array _(and optionally add new items)_
|
|
219
|
+
* @param from Index to start removing items from
|
|
220
|
+
* @param to Index to stop removing items at _(defaults to the end of the array)_
|
|
221
|
+
* @param items Optional items to add
|
|
222
|
+
* @returns Removed items
|
|
182
223
|
*/
|
|
183
224
|
splice(from: number, to?: number, ...items: Item[]): Item[] {
|
|
184
225
|
return this.state.value.splice(
|
|
@@ -195,6 +236,9 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
195
236
|
|
|
196
237
|
/**
|
|
197
238
|
* Subscribe to changes at a specific index
|
|
239
|
+
* @param index Index of item to subscribe to
|
|
240
|
+
* @param callback Callback for changes
|
|
241
|
+
* @returns Unsubscribe callback
|
|
198
242
|
*/
|
|
199
243
|
subscribe(
|
|
200
244
|
index: number,
|
|
@@ -219,6 +263,8 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
219
263
|
|
|
220
264
|
/**
|
|
221
265
|
* Add items to the beginning of the array
|
|
266
|
+
* @param items Items to add
|
|
267
|
+
* @returns New array length
|
|
222
268
|
*/
|
|
223
269
|
unshift(...items: Item[]): number {
|
|
224
270
|
return this.state.value.unshift(...items);
|
|
@@ -226,6 +272,7 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
226
272
|
|
|
227
273
|
/**
|
|
228
274
|
* Update the value _(based on the current value)_
|
|
275
|
+
* @param callback Callback to update the value
|
|
229
276
|
*/
|
|
230
277
|
update(callback: (value: Item[]) => Item[]): void {
|
|
231
278
|
const updated = callback(this.state.value);
|
|
@@ -238,6 +285,9 @@ export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
|
238
285
|
|
|
239
286
|
/**
|
|
240
287
|
* Create a reactive array
|
|
288
|
+
* @param value Initial array of items
|
|
289
|
+
* @param options Optional reactivity options
|
|
290
|
+
* @returns Reactive array
|
|
241
291
|
*/
|
|
242
292
|
export function array<Item>(
|
|
243
293
|
value: Item[],
|
|
@@ -252,7 +302,7 @@ function updateArray<Item>(
|
|
|
252
302
|
state: ReactiveState<Item[], Item>,
|
|
253
303
|
length: Signal<number>,
|
|
254
304
|
): unknown {
|
|
255
|
-
const affectsLength =
|
|
305
|
+
const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
|
|
256
306
|
const previousArray = affectsLength ? [] : [...array];
|
|
257
307
|
const previousLength = array.length;
|
|
258
308
|
|
|
@@ -275,18 +325,10 @@ function updateArray<Item>(
|
|
|
275
325
|
};
|
|
276
326
|
}
|
|
277
327
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const updateMethods = new Set<string>([
|
|
286
|
-
...lengthAffectingMethods,
|
|
287
|
-
'copyWithin',
|
|
288
|
-
'fill',
|
|
289
|
-
'reverse',
|
|
290
|
-
'sort',
|
|
291
|
-
'splice',
|
|
292
|
-
]);
|
|
328
|
+
function setAtIndex<Item>(array: Item[], index: number, value: Item): void {
|
|
329
|
+
const actual = index < 0 ? array.length + index : index;
|
|
330
|
+
|
|
331
|
+
if (actual > -1) {
|
|
332
|
+
array[actual] = value;
|
|
333
|
+
}
|
|
334
|
+
}
|
package/src/value/computed.ts
CHANGED
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {Reactive
|
|
5
|
-
|
|
6
|
-
export type ComputedEffect = {
|
|
7
|
-
dirty: boolean;
|
|
8
|
-
instance: Effect;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export type InternalComputed = {
|
|
12
|
-
readonly effect: ComputedEffect;
|
|
13
|
-
readonly state: ReactiveState<unknown, unknown>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export let activeComputed: Computed<unknown> | undefined;
|
|
1
|
+
import {ACTIVE, BATCH, NAME_COMPUTED} from '../constants';
|
|
2
|
+
import {effect, runEffect} from '../effect';
|
|
3
|
+
import type {ComputedEffect, ReactiveOptions} from '../models';
|
|
4
|
+
import {Reactive} from './reactive';
|
|
17
5
|
|
|
18
6
|
export class Computed<Value> extends Reactive<Value> {
|
|
19
7
|
private readonly effect: ComputedEffect = {
|
|
@@ -22,36 +10,38 @@ export class Computed<Value> extends Reactive<Value> {
|
|
|
22
10
|
};
|
|
23
11
|
|
|
24
12
|
constructor(callback: () => Value, options?: ReactiveOptions<Value>) {
|
|
25
|
-
super(
|
|
13
|
+
super(NAME_COMPUTED, undefined as never, options);
|
|
26
14
|
|
|
27
15
|
this.effect.instance = effect(() => {
|
|
28
|
-
if (this.effect.dirty) {
|
|
29
|
-
|
|
16
|
+
if (!this.effect.dirty) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
30
19
|
|
|
31
|
-
|
|
20
|
+
const previousComputed = ACTIVE.computed;
|
|
32
21
|
|
|
33
|
-
|
|
22
|
+
ACTIVE.computed = this as never;
|
|
34
23
|
|
|
35
|
-
|
|
24
|
+
const value = callback();
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
this.state.value = value;
|
|
26
|
+
ACTIVE.computed = previousComputed;
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
28
|
+
if (!this.state.equal(this.state.value, value)) {
|
|
29
|
+
this.state.value = value;
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
31
|
+
for (const computed of this.state.computeds) {
|
|
32
|
+
computed.effect.dirty = true;
|
|
33
|
+
}
|
|
47
34
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
35
|
+
for (const effect of this.state.effects) {
|
|
36
|
+
BATCH.handlers.add(effect);
|
|
51
37
|
}
|
|
52
38
|
|
|
53
|
-
this.
|
|
39
|
+
for (const [, subscription] of this.state.subscriptions) {
|
|
40
|
+
subscription.callback(value);
|
|
41
|
+
}
|
|
54
42
|
}
|
|
43
|
+
|
|
44
|
+
this.effect.dirty = false;
|
|
55
45
|
});
|
|
56
46
|
}
|
|
57
47
|
|
|
@@ -59,15 +49,15 @@ export class Computed<Value> extends Reactive<Value> {
|
|
|
59
49
|
* @inheritdoc
|
|
60
50
|
*/
|
|
61
51
|
get(): Value {
|
|
62
|
-
if (
|
|
63
|
-
this.state.computeds.add(
|
|
52
|
+
if (ACTIVE.computed != null && this !== ACTIVE.computed) {
|
|
53
|
+
this.state.computeds.add(ACTIVE.computed);
|
|
64
54
|
}
|
|
65
55
|
|
|
66
|
-
if (
|
|
67
|
-
this.state.effects.add(
|
|
56
|
+
if (ACTIVE.effect != null && ACTIVE.effect !== this.effect.instance) {
|
|
57
|
+
this.state.effects.add(ACTIVE.effect);
|
|
68
58
|
}
|
|
69
59
|
|
|
70
|
-
if (this.effect.dirty &&
|
|
60
|
+
if (this.effect.dirty && BATCH.depth === 0) {
|
|
71
61
|
runEffect(this.effect.instance);
|
|
72
62
|
}
|
|
73
63
|
|
package/src/value/reactive.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
type Subscription,
|
|
5
|
-
type Unsubscribe,
|
|
6
|
-
subscribe,
|
|
7
|
-
unsubscribe,
|
|
8
|
-
} from '../subscription';
|
|
9
|
-
import type {Computed} from './computed';
|
|
1
|
+
import type {ReactiveOptions, ReactiveState, Unsubscribe} from '../models';
|
|
2
|
+
import {subscribe, unsubscribe} from '../subscription';
|
|
10
3
|
|
|
11
4
|
export abstract class Reactive<Value, Equal = Value> {
|
|
12
5
|
protected readonly state: ReactiveState<Value, Equal> = {
|
|
@@ -31,11 +24,13 @@ export abstract class Reactive<Value, Equal = Value> {
|
|
|
31
24
|
|
|
32
25
|
/**
|
|
33
26
|
* Get the value
|
|
27
|
+
* @return Current value
|
|
34
28
|
*/
|
|
35
29
|
abstract get(): Value;
|
|
36
30
|
|
|
37
31
|
/**
|
|
38
32
|
* Get the value _(without reactivity)_
|
|
33
|
+
* @return Current value
|
|
39
34
|
*/
|
|
40
35
|
peek(): Value {
|
|
41
36
|
return this.state.value;
|
|
@@ -43,6 +38,8 @@ export abstract class Reactive<Value, Equal = Value> {
|
|
|
43
38
|
|
|
44
39
|
/**
|
|
45
40
|
* Subscribe to changes
|
|
41
|
+
* @param callback Callback for changes
|
|
42
|
+
* @return Unsubscribe callback
|
|
46
43
|
*/
|
|
47
44
|
subscribe(callback: (value: Value) => void): Unsubscribe {
|
|
48
45
|
return subscribe(this.state, callback);
|
|
@@ -50,6 +47,7 @@ export abstract class Reactive<Value, Equal = Value> {
|
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
49
|
* JSON representation of the value
|
|
50
|
+
* @return JSON value
|
|
53
51
|
*/
|
|
54
52
|
toJSON(): Value {
|
|
55
53
|
return this.get();
|
|
@@ -57,6 +55,7 @@ export abstract class Reactive<Value, Equal = Value> {
|
|
|
57
55
|
|
|
58
56
|
/**
|
|
59
57
|
* String representation of the value
|
|
58
|
+
* @return Value as string
|
|
60
59
|
*/
|
|
61
60
|
toString(): string {
|
|
62
61
|
return String(this.get());
|
|
@@ -64,23 +63,9 @@ export abstract class Reactive<Value, Equal = Value> {
|
|
|
64
63
|
|
|
65
64
|
/**
|
|
66
65
|
* Unsubscribe from changes
|
|
66
|
+
* @param callback Callback to unsubscribe
|
|
67
67
|
*/
|
|
68
68
|
unsubscribe(callback: (value: Value) => void): void {
|
|
69
69
|
unsubscribe(this.state, callback);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
export type ReactiveOptions<Value> = {
|
|
74
|
-
/**
|
|
75
|
-
* Method to compare values for equality
|
|
76
|
-
*/
|
|
77
|
-
equal?: (first: Value, second: Value) => boolean;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export type ReactiveState<Value, Equal> = {
|
|
81
|
-
computeds: Set<Computed<unknown>>;
|
|
82
|
-
effects: Set<Effect>;
|
|
83
|
-
equal: (first: Equal, second: Equal) => boolean;
|
|
84
|
-
subscriptions: Map<GenericCallback, Subscription>;
|
|
85
|
-
value: Value;
|
|
86
|
-
};
|
package/src/value/signal.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {NAME_SIGNAL} from '../constants';
|
|
2
2
|
import {emitValue, getValue} from '../helpers/value';
|
|
3
|
-
import
|
|
3
|
+
import type {ReactiveOptions} from '../models';
|
|
4
|
+
import {Reactive} from './reactive';
|
|
4
5
|
|
|
5
6
|
export class Signal<Value> extends Reactive<Value> {
|
|
6
7
|
constructor(value: Value, options?: ReactiveOptions<Value>) {
|
|
7
|
-
super(
|
|
8
|
+
super(NAME_SIGNAL, value, options);
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -16,6 +17,7 @@ export class Signal<Value> extends Reactive<Value> {
|
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Set the value
|
|
20
|
+
* @param value New value
|
|
19
21
|
*/
|
|
20
22
|
set(value: Value): void {
|
|
21
23
|
if (!this.state.equal(this.state.value, value)) {
|
|
@@ -27,6 +29,7 @@ export class Signal<Value> extends Reactive<Value> {
|
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Update the value _(based on the current value)_
|
|
32
|
+
* @param callback Callback to update the value
|
|
30
33
|
*/
|
|
31
34
|
update(callback: (value: Value) => Value): void {
|
|
32
35
|
this.set(callback(this.state.value));
|
|
@@ -35,6 +38,9 @@ export class Signal<Value> extends Reactive<Value> {
|
|
|
35
38
|
|
|
36
39
|
/**
|
|
37
40
|
* Create a reactive value
|
|
41
|
+
* @param value Initial value
|
|
42
|
+
* @param options Optional reactivity options
|
|
43
|
+
* @returns Reactive value
|
|
38
44
|
*/
|
|
39
45
|
export function signal<Value>(
|
|
40
46
|
value: Value,
|
package/src/value/store.ts
CHANGED
|
@@ -4,26 +4,33 @@ import type {
|
|
|
4
4
|
Key,
|
|
5
5
|
PlainObject,
|
|
6
6
|
} from '@oscarpalmer/atoms/models';
|
|
7
|
-
import {
|
|
7
|
+
import {NAME_STORE} from '../constants';
|
|
8
8
|
import {
|
|
9
9
|
getReactiveValueInProxy,
|
|
10
10
|
setProxyValue,
|
|
11
11
|
setValueInProxy,
|
|
12
12
|
} from '../helpers/proxy';
|
|
13
13
|
import {getValue} from '../helpers/value';
|
|
14
|
-
import
|
|
14
|
+
import type {ReactiveOptions, Unsubscribe} from '../models';
|
|
15
|
+
import {noop, subscribe} from '../subscription';
|
|
15
16
|
import type {Computed} from './computed';
|
|
16
|
-
import {Reactive
|
|
17
|
+
import {Reactive} from './reactive';
|
|
17
18
|
|
|
18
19
|
export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
19
|
-
#keyed = new Map<Key, Computed<unknown>>();
|
|
20
|
+
readonly #keyed = new Map<Key, Computed<unknown>>();
|
|
20
21
|
|
|
21
22
|
constructor(value: Value, options?: ReactiveOptions<Value>) {
|
|
22
23
|
super(
|
|
23
|
-
|
|
24
|
+
NAME_STORE,
|
|
24
25
|
new Proxy(value, {
|
|
25
|
-
set: (target, property, value) =>
|
|
26
|
-
setValueInProxy(
|
|
26
|
+
set: (target: Value, property: PropertyKey, value: unknown) =>
|
|
27
|
+
setValueInProxy({
|
|
28
|
+
target,
|
|
29
|
+
property,
|
|
30
|
+
value,
|
|
31
|
+
isArray: false,
|
|
32
|
+
state: this.state,
|
|
33
|
+
}),
|
|
27
34
|
}),
|
|
28
35
|
options,
|
|
29
36
|
);
|
|
@@ -35,12 +42,16 @@ export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
|
35
42
|
get(): Value;
|
|
36
43
|
|
|
37
44
|
/**
|
|
38
|
-
* Get a value by key
|
|
45
|
+
* Get a value by key
|
|
46
|
+
* @param key Key of the value to get
|
|
47
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
39
48
|
*/
|
|
40
49
|
get(key: keyof Value): Value[keyof Value];
|
|
41
50
|
|
|
42
51
|
/**
|
|
43
|
-
* Get a value by key
|
|
52
|
+
* Get a value by key
|
|
53
|
+
* @param key Key of the value to get
|
|
54
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
44
55
|
*/
|
|
45
56
|
get(key: Key): unknown;
|
|
46
57
|
|
|
@@ -52,16 +63,21 @@ export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
|
52
63
|
|
|
53
64
|
/**
|
|
54
65
|
* Get the value _(without reactivity)_
|
|
66
|
+
* @returns The current value
|
|
55
67
|
*/
|
|
56
68
|
peek(): Value;
|
|
57
69
|
|
|
58
70
|
/**
|
|
59
71
|
* Get a value by key _(without reactivity)_
|
|
72
|
+
* @param key Key of the value to get
|
|
73
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
60
74
|
*/
|
|
61
75
|
peek(key: keyof Value): Value[keyof Value];
|
|
62
76
|
|
|
63
77
|
/**
|
|
64
78
|
* Get a value by key _(without reactivity)_
|
|
79
|
+
* @param key Key of the value to get
|
|
80
|
+
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
65
81
|
*/
|
|
66
82
|
peek(key: Key): unknown;
|
|
67
83
|
|
|
@@ -71,16 +87,21 @@ export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
|
71
87
|
|
|
72
88
|
/**
|
|
73
89
|
* Set the value
|
|
90
|
+
* @param value New value _(defaults to an empty object)_
|
|
74
91
|
*/
|
|
75
92
|
set(value?: Value): void;
|
|
76
93
|
|
|
77
94
|
/**
|
|
78
95
|
* Set a value by key
|
|
96
|
+
* @param key Key of the value to set
|
|
97
|
+
* @param value New value
|
|
79
98
|
*/
|
|
80
99
|
set(key: keyof Value, value: Value[keyof Value]): void;
|
|
81
100
|
|
|
82
101
|
/**
|
|
83
102
|
* Set a value by key
|
|
103
|
+
* @param key Key of the value to set
|
|
104
|
+
* @param value New value
|
|
84
105
|
*/
|
|
85
106
|
set(key: Key, value: unknown): void;
|
|
86
107
|
|
|
@@ -99,6 +120,9 @@ export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
|
99
120
|
|
|
100
121
|
/**
|
|
101
122
|
* Subscribe to changes for a specific key
|
|
123
|
+
* @param key Key of the value to subscribe to
|
|
124
|
+
* @param callback Callback for changes
|
|
125
|
+
* @returns Unsubscribe callback
|
|
102
126
|
*/
|
|
103
127
|
subscribe<Key extends keyof Value>(
|
|
104
128
|
key: Key,
|
|
@@ -107,6 +131,9 @@ export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
|
107
131
|
|
|
108
132
|
/**
|
|
109
133
|
* Subscribe to changes for a specific key
|
|
134
|
+
* @param key Key of the value to subscribe to
|
|
135
|
+
* @param callback Callback for changes
|
|
136
|
+
* @returns Unsubscribe callback
|
|
110
137
|
*/
|
|
111
138
|
subscribe(key: Key, callback: (value: unknown) => void): Unsubscribe;
|
|
112
139
|
|
|
@@ -125,6 +152,7 @@ export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
|
125
152
|
|
|
126
153
|
/**
|
|
127
154
|
* Update the value _(based on the current value)_
|
|
155
|
+
* @param callback Callback to update the value
|
|
128
156
|
*/
|
|
129
157
|
update(callback: (value: Value) => Value): void {
|
|
130
158
|
const updated = callback({...this.state.value});
|
|
@@ -137,6 +165,9 @@ export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
|
137
165
|
|
|
138
166
|
/**
|
|
139
167
|
* Create a reactive store
|
|
168
|
+
* @param value Initial object value
|
|
169
|
+
* @param options Optional reactivity options
|
|
170
|
+
* @returns Reactive store
|
|
140
171
|
*/
|
|
141
172
|
export function store<Value extends PlainObject>(
|
|
142
173
|
value: Value,
|
package/types/batch.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { type Effect } from './effect';
|
|
2
|
-
import type { Subscription } from './subscription';
|
|
3
1
|
export declare function flushHandlers(): void;
|
|
4
2
|
/**
|
|
5
|
-
* Start batching effects
|
|
3
|
+
* Start batching effects
|
|
4
|
+
*
|
|
5
|
+
* _(Use {@link stopBatch} to flush and run batched effects)_
|
|
6
6
|
*/
|
|
7
7
|
export declare function startBatch(): void;
|
|
8
8
|
/**
|
|
9
9
|
* Stop batching effects and flush _(run)_ them
|
|
10
10
|
*/
|
|
11
11
|
export declare function stopBatch(): void;
|
|
12
|
-
export declare const batchedHandlers: Set<Subscription | Effect>;
|
|
13
|
-
export declare let batchDepth: number;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Active, Batch } from './models';
|
|
2
|
+
export declare const ACTIVE: Active;
|
|
3
|
+
export declare const ARRAY_THRESHOLD = 100;
|
|
4
|
+
export declare const ARRAY_OFFSET = 25;
|
|
5
|
+
export declare const ARRAY_PEEK = 10;
|
|
6
|
+
export declare const BATCH: Batch;
|
|
7
|
+
export declare const METHODS_AFFECTING_LENGTH: Set<string>;
|
|
8
|
+
export declare const METHODS_UPDATE: Set<string>;
|
|
9
|
+
export declare const NAME_ARRAY = "array";
|
|
10
|
+
export declare const NAME_COMPUTED = "computed";
|
|
11
|
+
export declare const NAME_EFFECT = "effect";
|
|
12
|
+
export declare const NAME_SIGNAL = "signal";
|
|
13
|
+
export declare const NAME_STORE = "store";
|
|
14
|
+
export declare const NAMES: Set<string>;
|
package/types/effect.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare class Effect {
|
|
|
7
7
|
export declare function runEffect(effect: Effect): void;
|
|
8
8
|
/**
|
|
9
9
|
* Create an effect
|
|
10
|
+
* @param callback Callback for handling signal effects
|
|
11
|
+
* @returns Effect
|
|
10
12
|
*/
|
|
11
13
|
export declare function effect(callback: GenericCallback): Effect;
|
|
12
|
-
export declare let activeEffect: Effect | undefined;
|