@oscarpalmer/mora 0.15.0 → 0.17.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.cjs +3 -3
- package/dist/batch.js +3 -3
- package/dist/helpers/value.cjs +44 -8
- package/dist/helpers/value.js +45 -9
- package/dist/mora.full.js +75 -64
- package/dist/value/array.cjs +15 -9
- package/dist/value/array.js +14 -8
- package/dist/value/computed.cjs +6 -6
- package/dist/value/computed.js +6 -6
- package/dist/value/reactive.cjs +5 -1
- package/dist/value/reactive.js +5 -1
- package/dist/value/signal.cjs +8 -5
- package/dist/value/signal.js +9 -6
- package/package.json +3 -3
- package/src/batch.ts +2 -2
- package/src/helpers/is.ts +1 -1
- package/src/helpers/value.ts +66 -14
- package/src/subscription.ts +3 -3
- package/src/value/array.ts +22 -12
- package/src/value/computed.ts +12 -9
- package/src/value/reactive.ts +17 -4
- package/src/value/signal.ts +14 -7
- package/types/batch.d.cts +15 -8
- package/types/batch.d.ts +1 -1
- package/types/helpers/is.d.cts +21 -10
- package/types/helpers/is.d.ts +1 -1
- package/types/helpers/value.d.cts +18 -9
- package/types/helpers/value.d.ts +4 -2
- package/types/index.d.cts +24 -13
- package/types/subscription.d.cts +16 -9
- package/types/subscription.d.ts +4 -4
- package/types/value/array.d.cts +21 -10
- package/types/value/array.d.ts +8 -4
- package/types/value/computed.d.cts +16 -9
- package/types/value/computed.d.ts +5 -6
- package/types/value/reactive.d.cts +14 -7
- package/types/value/reactive.d.ts +11 -4
- package/types/value/signal.d.cts +16 -9
- package/types/value/signal.d.ts +3 -3
- package/dist/helpers/emit.cjs +0 -42
- package/dist/helpers/emit.js +0 -42
- package/src/helpers/emit.ts +0 -52
- package/types/helpers/emit.d.cts +0 -63
- package/types/helpers/emit.d.ts +0 -3
package/dist/value/signal.cjs
CHANGED
|
@@ -4,8 +4,8 @@ const helpers_is = require("../helpers/is.cjs");
|
|
|
4
4
|
const helpers_value = require("../helpers/value.cjs");
|
|
5
5
|
const value_reactive = require("./reactive.cjs");
|
|
6
6
|
class Signal extends value_reactive.Reactive {
|
|
7
|
-
constructor(value) {
|
|
8
|
-
super(helpers_is.signalName, value);
|
|
7
|
+
constructor(value, options) {
|
|
8
|
+
super(helpers_is.signalName, value, options);
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* @inheritdoc
|
|
@@ -17,7 +17,10 @@ class Signal extends value_reactive.Reactive {
|
|
|
17
17
|
* Set the value
|
|
18
18
|
*/
|
|
19
19
|
set(value) {
|
|
20
|
-
helpers_value.
|
|
20
|
+
if (helpers_value.differentValues(this.state, this.state.value, value)) {
|
|
21
|
+
this.state.value = value;
|
|
22
|
+
helpers_value.emitValue(this.state);
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
26
|
* Update the value
|
|
@@ -26,8 +29,8 @@ class Signal extends value_reactive.Reactive {
|
|
|
26
29
|
this.set(callback(this.state.value));
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
|
-
function signal(value) {
|
|
30
|
-
return new Signal(value);
|
|
32
|
+
function signal(value, options) {
|
|
33
|
+
return new Signal(value, options);
|
|
31
34
|
}
|
|
32
35
|
exports.Signal = Signal;
|
|
33
36
|
exports.signal = signal;
|
package/dist/value/signal.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { signalName } from "../helpers/is.js";
|
|
2
|
-
import { getValue,
|
|
2
|
+
import { getValue, differentValues, emitValue } from "../helpers/value.js";
|
|
3
3
|
import { Reactive } from "./reactive.js";
|
|
4
4
|
class Signal extends Reactive {
|
|
5
|
-
constructor(value) {
|
|
6
|
-
super(signalName, value);
|
|
5
|
+
constructor(value, options) {
|
|
6
|
+
super(signalName, value, options);
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* @inheritdoc
|
|
@@ -15,7 +15,10 @@ class Signal extends Reactive {
|
|
|
15
15
|
* Set the value
|
|
16
16
|
*/
|
|
17
17
|
set(value) {
|
|
18
|
-
|
|
18
|
+
if (differentValues(this.state, this.state.value, value)) {
|
|
19
|
+
this.state.value = value;
|
|
20
|
+
emitValue(this.state);
|
|
21
|
+
}
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* Update the value
|
|
@@ -24,8 +27,8 @@ class Signal extends Reactive {
|
|
|
24
27
|
this.set(callback(this.state.value));
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
|
-
function signal(value) {
|
|
28
|
-
return new Signal(value);
|
|
30
|
+
function signal(value, options) {
|
|
31
|
+
return new Signal(value, options);
|
|
29
32
|
}
|
|
30
33
|
export {
|
|
31
34
|
Signal,
|
package/package.json
CHANGED
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
"@rollup/plugin-node-resolve": "^16",
|
|
13
13
|
"@rollup/plugin-typescript": "^12.1",
|
|
14
14
|
"@types/node": "^22.15",
|
|
15
|
-
"@vitest/coverage-istanbul": "^3.
|
|
15
|
+
"@vitest/coverage-istanbul": "^3.2",
|
|
16
16
|
"dts-bundle-generator": "^9.5",
|
|
17
17
|
"glob": "^11",
|
|
18
18
|
"jsdom": "^26.1",
|
|
19
19
|
"tslib": "^2.8",
|
|
20
20
|
"typescript": "^5.8",
|
|
21
21
|
"vite": "^6.3",
|
|
22
|
-
"vitest": "^3.
|
|
22
|
+
"vitest": "^3.2"
|
|
23
23
|
},
|
|
24
24
|
"exports": {
|
|
25
25
|
".": {
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
},
|
|
55
55
|
"type": "module",
|
|
56
56
|
"types": "./types/index.d.ts",
|
|
57
|
-
"version": "0.
|
|
57
|
+
"version": "0.17.0"
|
|
58
58
|
}
|
package/src/batch.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {type Effect, runEffect} from './effect';
|
|
|
2
2
|
import {isEffect} from './helpers/is';
|
|
3
3
|
import type {Subscription} from './subscription';
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export function flushHandlers(): void {
|
|
6
6
|
while (batchDepth === 0 && batchedHandlers.size > 0) {
|
|
7
7
|
const handlers = [...batchedHandlers];
|
|
8
8
|
|
|
@@ -33,7 +33,7 @@ export function stopBatch(): void {
|
|
|
33
33
|
batchDepth -= 1;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
flushHandlers();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export const batchedHandlers = new Set<Effect | Subscription<unknown>>();
|
package/src/helpers/is.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type {Effect} from '../effect';
|
|
|
3
3
|
import type {ReactiveArray} from '../value/array';
|
|
4
4
|
import type {Computed} from '../value/computed';
|
|
5
5
|
import type {Reactive} from '../value/reactive';
|
|
6
|
-
import
|
|
6
|
+
import type {Signal} from '../value/signal';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Is the value a reactive array?
|
package/src/helpers/value.ts
CHANGED
|
@@ -1,9 +1,72 @@
|
|
|
1
|
+
import {batchDepth, batchedHandlers, flushHandlers} from '../batch';
|
|
1
2
|
import {activeEffect} from '../effect';
|
|
2
|
-
import {activeComputed} from '../value/computed';
|
|
3
|
+
import {type InternalComputed, activeComputed} from '../value/computed';
|
|
3
4
|
import type {ReactiveState} from '../value/reactive';
|
|
4
|
-
import {emitValue} from './emit';
|
|
5
5
|
|
|
6
|
-
export function
|
|
6
|
+
export function differentArrays<Value>(
|
|
7
|
+
state: ReactiveState<Value[], Value>,
|
|
8
|
+
first: Value[],
|
|
9
|
+
second: Value[],
|
|
10
|
+
): boolean {
|
|
11
|
+
let {length} = first;
|
|
12
|
+
|
|
13
|
+
if (length !== second.length) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let offset = 0;
|
|
18
|
+
|
|
19
|
+
if (length >= 100) {
|
|
20
|
+
offset = Math.round(length / 10);
|
|
21
|
+
offset = offset > 25 ? 25 : offset;
|
|
22
|
+
|
|
23
|
+
for (let index = 0; index < offset; index += 1) {
|
|
24
|
+
if (!state.equal(first[index], second[index])) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
length -= offset;
|
|
31
|
+
|
|
32
|
+
for (let index = offset; index < length; index += 1) {
|
|
33
|
+
if (!state.equal(first[index], second[index])) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function differentValues<Value>(
|
|
42
|
+
state: ReactiveState<Value, Value>,
|
|
43
|
+
first: Value,
|
|
44
|
+
second: Value,
|
|
45
|
+
): boolean {
|
|
46
|
+
return Array.isArray(first) && Array.isArray(second)
|
|
47
|
+
? differentArrays(state as never, first, second)
|
|
48
|
+
: !state.equal(first, second);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function emitValue<Value>(state: ReactiveState<Value, never>): void {
|
|
52
|
+
for (const computed of state.computeds) {
|
|
53
|
+
(computed as unknown as InternalComputed).effect.dirty = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const effect of state.effects) {
|
|
57
|
+
batchedHandlers.add(effect);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const [, subscription] of state.subscriptions) {
|
|
61
|
+
batchedHandlers.add(subscription as never);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (batchDepth === 0) {
|
|
65
|
+
flushHandlers();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getValue<Value>(state: ReactiveState<Value, never>): Value {
|
|
7
70
|
if (activeComputed != null) {
|
|
8
71
|
state.computeds.add(activeComputed);
|
|
9
72
|
}
|
|
@@ -14,14 +77,3 @@ export function getValue<Value>(state: ReactiveState<Value>): Value {
|
|
|
14
77
|
|
|
15
78
|
return state.value;
|
|
16
79
|
}
|
|
17
|
-
|
|
18
|
-
export function setValue<Value>(
|
|
19
|
-
state: ReactiveState<Value>,
|
|
20
|
-
value: Value,
|
|
21
|
-
): void {
|
|
22
|
-
if (!Object.is(state.value, value)) {
|
|
23
|
-
state.value = value;
|
|
24
|
-
|
|
25
|
-
emitValue(state);
|
|
26
|
-
}
|
|
27
|
-
}
|
package/src/subscription.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type {ReactiveState} from './value/reactive';
|
|
|
3
3
|
|
|
4
4
|
export class Subscription<Value> {
|
|
5
5
|
constructor(
|
|
6
|
-
public state: ReactiveState<Value>,
|
|
6
|
+
public state: ReactiveState<Value, never>,
|
|
7
7
|
public callback: GenericCallback,
|
|
8
8
|
) {
|
|
9
9
|
callback(state.value);
|
|
@@ -18,7 +18,7 @@ export type Unsubscribe = () => void;
|
|
|
18
18
|
export function noop(): void {}
|
|
19
19
|
|
|
20
20
|
export function subscribe<Value>(
|
|
21
|
-
state: ReactiveState<Value>,
|
|
21
|
+
state: ReactiveState<Value, never>,
|
|
22
22
|
callback: (value: Value) => void,
|
|
23
23
|
): Unsubscribe {
|
|
24
24
|
if (typeof callback !== 'function' || state.subscriptions.has(callback)) {
|
|
@@ -33,7 +33,7 @@ export function subscribe<Value>(
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function unsubscribe<Value>(
|
|
36
|
-
state: ReactiveState<Value>,
|
|
36
|
+
state: ReactiveState<Value, never>,
|
|
37
37
|
callback: (value: Value) => void,
|
|
38
38
|
): void {
|
|
39
39
|
const subscription = state.subscriptions.get(callback);
|
package/src/value/array.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {emitArrayChanges, emitValue} from '../helpers/emit';
|
|
2
1
|
import {arrayName} from '../helpers/is';
|
|
3
|
-
import {getValue} from '../helpers/value';
|
|
2
|
+
import {differentArrays, emitValue, getValue} from '../helpers/value';
|
|
4
3
|
import {type Computed, computed} from './computed';
|
|
5
|
-
import {Reactive, type ReactiveState} from './reactive';
|
|
4
|
+
import {Reactive, type ReactiveOptions, type ReactiveState} from './reactive';
|
|
6
5
|
import {type Signal, signal} from './signal';
|
|
7
6
|
|
|
8
|
-
export class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
7
|
+
export class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
9
8
|
#size = signal(0);
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -28,7 +27,7 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
constructor(value: Item[]) {
|
|
30
|
+
constructor(value: Item[], options?: ReactiveOptions<Item>) {
|
|
32
31
|
super(
|
|
33
32
|
arrayName,
|
|
34
33
|
new Proxy(value, {
|
|
@@ -39,6 +38,7 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
|
39
38
|
set: (target, property, value) =>
|
|
40
39
|
setValue(target, property, value, this.state, this.#size),
|
|
41
40
|
}),
|
|
41
|
+
options,
|
|
42
42
|
);
|
|
43
43
|
|
|
44
44
|
this.#size.set(value.length);
|
|
@@ -51,6 +51,13 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
|
51
51
|
return this.get().at(index);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Clear the array
|
|
56
|
+
*/
|
|
57
|
+
clear(): void {
|
|
58
|
+
this.length = 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
/**
|
|
55
62
|
* @inheritdoc
|
|
56
63
|
*/
|
|
@@ -158,15 +165,18 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
|
158
165
|
/**
|
|
159
166
|
* Create a reactive array
|
|
160
167
|
*/
|
|
161
|
-
export function array<Item>(
|
|
162
|
-
|
|
168
|
+
export function array<Item>(
|
|
169
|
+
value: Item[],
|
|
170
|
+
options?: ReactiveOptions<Item>,
|
|
171
|
+
): ReactiveArray<Item> {
|
|
172
|
+
return new ReactiveArray(Array.isArray(value) ? value : [], options);
|
|
163
173
|
}
|
|
164
174
|
|
|
165
175
|
function setValue<Item>(
|
|
166
176
|
target: Item[],
|
|
167
177
|
property: PropertyKey,
|
|
168
|
-
value:
|
|
169
|
-
state: ReactiveState<Item[]>,
|
|
178
|
+
value: Item,
|
|
179
|
+
state: ReactiveState<Item[], Item>,
|
|
170
180
|
length: Signal<number>,
|
|
171
181
|
): boolean {
|
|
172
182
|
const isIndex = !Number.isNaN(Number(property));
|
|
@@ -178,7 +188,7 @@ function setValue<Item>(
|
|
|
178
188
|
|
|
179
189
|
const previous = Reflect.get(target, property);
|
|
180
190
|
|
|
181
|
-
if (!
|
|
191
|
+
if (!state.equal(previous, value)) {
|
|
182
192
|
Reflect.set(target, property, value);
|
|
183
193
|
|
|
184
194
|
emitValue(state);
|
|
@@ -192,7 +202,7 @@ function setValue<Item>(
|
|
|
192
202
|
function updateArray<Item>(
|
|
193
203
|
type: string,
|
|
194
204
|
array: Item[],
|
|
195
|
-
state: ReactiveState<Item[]>,
|
|
205
|
+
state: ReactiveState<Item[], Item>,
|
|
196
206
|
length: Signal<number>,
|
|
197
207
|
): unknown {
|
|
198
208
|
const affectsLength = lengthAffectingMethods.has(type);
|
|
@@ -207,7 +217,7 @@ function updateArray<Item>(
|
|
|
207
217
|
if (
|
|
208
218
|
affectsLength
|
|
209
219
|
? array.length !== previousLength
|
|
210
|
-
:
|
|
220
|
+
: differentArrays(state, previousArray, array)
|
|
211
221
|
) {
|
|
212
222
|
emitValue(state);
|
|
213
223
|
|
package/src/value/computed.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {batchDepth, batchedHandlers} from '../batch';
|
|
2
2
|
import {type Effect, activeEffect, effect, runEffect} from '../effect';
|
|
3
3
|
import {computedName} from '../helpers/is';
|
|
4
|
-
import {Reactive, type ReactiveState} from './reactive';
|
|
4
|
+
import {Reactive, type ReactiveOptions, type ReactiveState} from './reactive';
|
|
5
5
|
|
|
6
|
-
type ComputedEffect = {
|
|
6
|
+
export type ComputedEffect = {
|
|
7
7
|
dirty: boolean;
|
|
8
8
|
instance: Effect;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export type InternalComputed = {
|
|
12
12
|
readonly effect: ComputedEffect;
|
|
13
|
-
readonly state: ReactiveState<unknown>;
|
|
13
|
+
readonly state: ReactiveState<unknown, unknown>;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export let activeComputed: Computed<unknown> | undefined;
|
|
@@ -21,8 +21,8 @@ export class Computed<Value> extends Reactive<Value> {
|
|
|
21
21
|
instance: undefined as never,
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
constructor(callback: () => Value) {
|
|
25
|
-
super(computedName, undefined as never);
|
|
24
|
+
constructor(callback: () => Value, options?: ReactiveOptions<Value>) {
|
|
25
|
+
super(computedName, undefined as never, options);
|
|
26
26
|
|
|
27
27
|
this.effect.instance = effect(() => {
|
|
28
28
|
if (this.effect.dirty) {
|
|
@@ -34,7 +34,7 @@ export class Computed<Value> extends Reactive<Value> {
|
|
|
34
34
|
|
|
35
35
|
activeComputed = previousComputed;
|
|
36
36
|
|
|
37
|
-
if (!
|
|
37
|
+
if (!this.state.equal(this.state.value, value)) {
|
|
38
38
|
this.state.value = value;
|
|
39
39
|
|
|
40
40
|
for (const computed of this.state.computeds) {
|
|
@@ -59,7 +59,7 @@ export class Computed<Value> extends Reactive<Value> {
|
|
|
59
59
|
* @inheritdoc
|
|
60
60
|
*/
|
|
61
61
|
get(): Value {
|
|
62
|
-
if (activeComputed != null &&
|
|
62
|
+
if (activeComputed != null && this !== activeComputed) {
|
|
63
63
|
this.state.computeds.add(activeComputed);
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -78,6 +78,9 @@ export class Computed<Value> extends Reactive<Value> {
|
|
|
78
78
|
/**
|
|
79
79
|
* Create a computed value
|
|
80
80
|
*/
|
|
81
|
-
export function computed<Value>(
|
|
82
|
-
|
|
81
|
+
export function computed<Value>(
|
|
82
|
+
callback: () => Value,
|
|
83
|
+
options?: ReactiveOptions<Value>,
|
|
84
|
+
): Computed<Value> {
|
|
85
|
+
return new Computed(callback, options);
|
|
83
86
|
}
|
package/src/value/reactive.ts
CHANGED
|
@@ -7,17 +7,22 @@ import {
|
|
|
7
7
|
} from '../subscription';
|
|
8
8
|
import type {Computed} from './computed';
|
|
9
9
|
|
|
10
|
-
export abstract class Reactive<Value> {
|
|
11
|
-
protected readonly state: ReactiveState<Value> = {
|
|
10
|
+
export abstract class Reactive<Value, Equal = Value> {
|
|
11
|
+
protected readonly state: ReactiveState<Value, Equal> = {
|
|
12
12
|
computeds: new Set(),
|
|
13
13
|
effects: new Set(),
|
|
14
|
+
equal: Object.is,
|
|
14
15
|
subscriptions: new Map(),
|
|
15
16
|
value: undefined as never,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
constructor(name: string, value: Value) {
|
|
19
|
+
constructor(name: string, value: Value, options?: ReactiveOptions<Equal>) {
|
|
19
20
|
this.state.value = value;
|
|
20
21
|
|
|
22
|
+
if (typeof options === 'object' && typeof options.equal === 'function') {
|
|
23
|
+
this.state.equal = options.equal;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
Object.defineProperty(this, '$mora', {
|
|
22
27
|
value: name,
|
|
23
28
|
});
|
|
@@ -64,9 +69,17 @@ export abstract class Reactive<Value> {
|
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
|
|
67
|
-
export type
|
|
72
|
+
export type ReactiveOptions<Value> = {
|
|
73
|
+
/**
|
|
74
|
+
* Method to compare values for equality
|
|
75
|
+
*/
|
|
76
|
+
equal?: (first: Value, second: Value) => boolean;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type ReactiveState<Value, Equal> = {
|
|
68
80
|
computeds: Set<Computed<unknown>>;
|
|
69
81
|
effects: Set<Effect>;
|
|
82
|
+
equal: (first: Equal, second: Equal) => boolean;
|
|
70
83
|
subscriptions: Map<(value: Value) => void, Subscription<Value>>;
|
|
71
84
|
value: Value;
|
|
72
85
|
};
|
package/src/value/signal.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {signalName} from '../helpers/is';
|
|
2
|
-
import {
|
|
3
|
-
import {Reactive} from './reactive';
|
|
2
|
+
import {differentValues, emitValue, getValue} from '../helpers/value';
|
|
3
|
+
import {Reactive, type ReactiveOptions} from './reactive';
|
|
4
4
|
|
|
5
5
|
export class Signal<Value> extends Reactive<Value> {
|
|
6
|
-
constructor(value: Value) {
|
|
7
|
-
super(signalName, value);
|
|
6
|
+
constructor(value: Value, options?: ReactiveOptions<Value>) {
|
|
7
|
+
super(signalName, value, options);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -18,7 +18,11 @@ export class Signal<Value> extends Reactive<Value> {
|
|
|
18
18
|
* Set the value
|
|
19
19
|
*/
|
|
20
20
|
set(value: Value): void {
|
|
21
|
-
|
|
21
|
+
if (differentValues(this.state, this.state.value, value)) {
|
|
22
|
+
this.state.value = value;
|
|
23
|
+
|
|
24
|
+
emitValue(this.state);
|
|
25
|
+
}
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
/**
|
|
@@ -32,6 +36,9 @@ export class Signal<Value> extends Reactive<Value> {
|
|
|
32
36
|
/**
|
|
33
37
|
* Create a reactive value
|
|
34
38
|
*/
|
|
35
|
-
export function signal<Value>(
|
|
36
|
-
|
|
39
|
+
export function signal<Value>(
|
|
40
|
+
value: Value,
|
|
41
|
+
options?: ReactiveOptions<Value>,
|
|
42
|
+
): Signal<Value> {
|
|
43
|
+
return new Signal<Value>(value, options);
|
|
37
44
|
}
|
package/types/batch.d.cts
CHANGED
|
@@ -8,15 +8,15 @@ declare class Effect {
|
|
|
8
8
|
}
|
|
9
9
|
declare class Computed<Value> extends Reactive<Value> {
|
|
10
10
|
private readonly effect;
|
|
11
|
-
constructor(callback: () => Value);
|
|
11
|
+
constructor(callback: () => Value, options?: ReactiveOptions<Value>);
|
|
12
12
|
/**
|
|
13
13
|
* @inheritdoc
|
|
14
14
|
*/
|
|
15
15
|
get(): Value;
|
|
16
16
|
}
|
|
17
|
-
declare abstract class Reactive<Value> {
|
|
18
|
-
protected readonly state: ReactiveState<Value>;
|
|
19
|
-
constructor(name: string, value: Value);
|
|
17
|
+
declare abstract class Reactive<Value, Equal = Value> {
|
|
18
|
+
protected readonly state: ReactiveState<Value, Equal>;
|
|
19
|
+
constructor(name: string, value: Value, options?: ReactiveOptions<Equal>);
|
|
20
20
|
/**
|
|
21
21
|
* Get the value
|
|
22
22
|
*/
|
|
@@ -42,22 +42,29 @@ declare abstract class Reactive<Value> {
|
|
|
42
42
|
*/
|
|
43
43
|
unsubscribe(callback: (value: Value) => void): void;
|
|
44
44
|
}
|
|
45
|
-
export type
|
|
45
|
+
export type ReactiveOptions<Value> = {
|
|
46
|
+
/**
|
|
47
|
+
* Method to compare values for equality
|
|
48
|
+
*/
|
|
49
|
+
equal?: (first: Value, second: Value) => boolean;
|
|
50
|
+
};
|
|
51
|
+
export type ReactiveState<Value, Equal> = {
|
|
46
52
|
computeds: Set<Computed<unknown>>;
|
|
47
53
|
effects: Set<Effect>;
|
|
54
|
+
equal: (first: Equal, second: Equal) => boolean;
|
|
48
55
|
subscriptions: Map<(value: Value) => void, Subscription<Value>>;
|
|
49
56
|
value: Value;
|
|
50
57
|
};
|
|
51
58
|
declare class Subscription<Value> {
|
|
52
|
-
state: ReactiveState<Value>;
|
|
59
|
+
state: ReactiveState<Value, never>;
|
|
53
60
|
callback: GenericCallback;
|
|
54
|
-
constructor(state: ReactiveState<Value>, callback: GenericCallback);
|
|
61
|
+
constructor(state: ReactiveState<Value, never>, callback: GenericCallback);
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
57
64
|
* Unsubscribe from changes
|
|
58
65
|
*/
|
|
59
66
|
export type Unsubscribe = () => void;
|
|
60
|
-
export declare function
|
|
67
|
+
export declare function flushHandlers(): void;
|
|
61
68
|
/**
|
|
62
69
|
* Start batching effects _(use `stopBatch` to flush and run batched effects)_
|
|
63
70
|
*/
|
package/types/batch.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Effect } from './effect';
|
|
2
2
|
import type { Subscription } from './subscription';
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function flushHandlers(): void;
|
|
4
4
|
/**
|
|
5
5
|
* Start batching effects _(use `stopBatch` to flush and run batched effects)_
|
|
6
6
|
*/
|
package/types/helpers/is.d.cts
CHANGED
|
@@ -8,15 +8,15 @@ declare class Effect {
|
|
|
8
8
|
}
|
|
9
9
|
declare class Computed<Value> extends Reactive<Value> {
|
|
10
10
|
private readonly effect;
|
|
11
|
-
constructor(callback: () => Value);
|
|
11
|
+
constructor(callback: () => Value, options?: ReactiveOptions<Value>);
|
|
12
12
|
/**
|
|
13
13
|
* @inheritdoc
|
|
14
14
|
*/
|
|
15
15
|
get(): Value;
|
|
16
16
|
}
|
|
17
|
-
declare abstract class Reactive<Value> {
|
|
18
|
-
protected readonly state: ReactiveState<Value>;
|
|
19
|
-
constructor(name: string, value: Value);
|
|
17
|
+
declare abstract class Reactive<Value, Equal = Value> {
|
|
18
|
+
protected readonly state: ReactiveState<Value, Equal>;
|
|
19
|
+
constructor(name: string, value: Value, options?: ReactiveOptions<Equal>);
|
|
20
20
|
/**
|
|
21
21
|
* Get the value
|
|
22
22
|
*/
|
|
@@ -42,22 +42,29 @@ declare abstract class Reactive<Value> {
|
|
|
42
42
|
*/
|
|
43
43
|
unsubscribe(callback: (value: Value) => void): void;
|
|
44
44
|
}
|
|
45
|
-
export type
|
|
45
|
+
export type ReactiveOptions<Value> = {
|
|
46
|
+
/**
|
|
47
|
+
* Method to compare values for equality
|
|
48
|
+
*/
|
|
49
|
+
equal?: (first: Value, second: Value) => boolean;
|
|
50
|
+
};
|
|
51
|
+
export type ReactiveState<Value, Equal> = {
|
|
46
52
|
computeds: Set<Computed<unknown>>;
|
|
47
53
|
effects: Set<Effect>;
|
|
54
|
+
equal: (first: Equal, second: Equal) => boolean;
|
|
48
55
|
subscriptions: Map<(value: Value) => void, Subscription<Value>>;
|
|
49
56
|
value: Value;
|
|
50
57
|
};
|
|
51
58
|
declare class Subscription<Value> {
|
|
52
|
-
state: ReactiveState<Value>;
|
|
59
|
+
state: ReactiveState<Value, never>;
|
|
53
60
|
callback: GenericCallback;
|
|
54
|
-
constructor(state: ReactiveState<Value>, callback: GenericCallback);
|
|
61
|
+
constructor(state: ReactiveState<Value, never>, callback: GenericCallback);
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
57
64
|
* Unsubscribe from changes
|
|
58
65
|
*/
|
|
59
66
|
export type Unsubscribe = () => void;
|
|
60
|
-
declare class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
67
|
+
declare class ReactiveArray<Item> extends Reactive<Item[], Item> {
|
|
61
68
|
#private;
|
|
62
69
|
/**
|
|
63
70
|
* The length of the array
|
|
@@ -67,11 +74,15 @@ declare class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
|
67
74
|
* Set the length of the array
|
|
68
75
|
*/
|
|
69
76
|
set length(value: number);
|
|
70
|
-
constructor(value: Item[]);
|
|
77
|
+
constructor(value: Item[], options?: ReactiveOptions<Item>);
|
|
71
78
|
/**
|
|
72
79
|
* Get an indexed item
|
|
73
80
|
*/
|
|
74
81
|
at(index: number): Item | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Clear the array
|
|
84
|
+
*/
|
|
85
|
+
clear(): void;
|
|
75
86
|
/**
|
|
76
87
|
* @inheritdoc
|
|
77
88
|
*/
|
|
@@ -126,7 +137,7 @@ declare class ReactiveArray<Item> extends Reactive<Item[]> {
|
|
|
126
137
|
unshift(...items: Item[]): number;
|
|
127
138
|
}
|
|
128
139
|
declare class Signal<Value> extends Reactive<Value> {
|
|
129
|
-
constructor(value: Value);
|
|
140
|
+
constructor(value: Value, options?: ReactiveOptions<Value>);
|
|
130
141
|
/**
|
|
131
142
|
* @inheritdoc
|
|
132
143
|
*/
|
package/types/helpers/is.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Effect } from '../effect';
|
|
|
2
2
|
import type { ReactiveArray } from '../value/array';
|
|
3
3
|
import type { Computed } from '../value/computed';
|
|
4
4
|
import type { Reactive } from '../value/reactive';
|
|
5
|
-
import {
|
|
5
|
+
import type { Signal } from '../value/signal';
|
|
6
6
|
/**
|
|
7
7
|
* Is the value a reactive array?
|
|
8
8
|
*/
|