@esportsplus/reactivity 0.1.12 → 0.1.14
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/build/signal.d.ts +14 -34
- package/build/signal.js +64 -110
- package/build/types.d.ts +21 -2
- package/package.json +1 -1
- package/src/signal.ts +87 -152
- package/src/types.ts +27 -2
package/build/signal.d.ts
CHANGED
|
@@ -1,43 +1,23 @@
|
|
|
1
|
-
import { Changed, Event, Function, Listener, NeverAsync, Options, State, Type } from './types';
|
|
2
|
-
declare class
|
|
1
|
+
import { Computed, Changed, Effect, Event, Function, Listener, NeverAsync, Options, Root, Scheduler, Signal, State, Type } from './types';
|
|
2
|
+
declare class Reactive<T> {
|
|
3
|
+
changed: Changed | null;
|
|
4
|
+
fn: Computed<T>['fn'] | Effect['fn'] | null;
|
|
3
5
|
listeners: Record<Event, (Listener<any> | null)[]> | null;
|
|
4
|
-
observers:
|
|
6
|
+
observers: Reactive<any>[] | null;
|
|
5
7
|
root: Root | null;
|
|
6
|
-
|
|
8
|
+
scheduler: Scheduler | null;
|
|
9
|
+
sources: Reactive<any>[] | null;
|
|
7
10
|
state: State;
|
|
8
|
-
|
|
11
|
+
task: Function | null;
|
|
12
|
+
tracking: boolean | null;
|
|
13
|
+
type: Type;
|
|
9
14
|
value: T;
|
|
10
|
-
constructor(state: State, value: T);
|
|
11
|
-
get type(): Type | never;
|
|
15
|
+
constructor(state: State, type: Type, value: T);
|
|
12
16
|
dispatch<D>(event: Event, data?: D): void;
|
|
13
17
|
dispose(): void;
|
|
18
|
+
get(): T;
|
|
14
19
|
on<T>(event: Event, listener: Listener<T>): void;
|
|
15
20
|
once<T>(event: Event, listener: Listener<T>): void;
|
|
16
|
-
}
|
|
17
|
-
declare class Computed<T> extends Core<T> {
|
|
18
|
-
changed: Changed;
|
|
19
|
-
fn: NeverAsync<() => T>;
|
|
20
|
-
constructor(fn: Computed<T>['fn'], options?: Options);
|
|
21
|
-
get type(): Type;
|
|
22
|
-
get(): T;
|
|
23
|
-
}
|
|
24
|
-
declare class Effect extends Core<null> {
|
|
25
|
-
fn: NeverAsync<(node: Effect) => void>;
|
|
26
|
-
task: Function;
|
|
27
|
-
constructor(fn: Effect['fn']);
|
|
28
|
-
get type(): Type;
|
|
29
|
-
}
|
|
30
|
-
declare class Root extends Core<null> {
|
|
31
|
-
scheduler: (fn: Function) => unknown;
|
|
32
|
-
tracking: boolean;
|
|
33
|
-
constructor(scheduler: Root['scheduler'], tracking: boolean);
|
|
34
|
-
get type(): Type;
|
|
35
|
-
}
|
|
36
|
-
declare class Signal<T> extends Core<T> {
|
|
37
|
-
changed: Changed;
|
|
38
|
-
constructor(data: T, options?: Options);
|
|
39
|
-
get type(): Type;
|
|
40
|
-
get(): T;
|
|
41
21
|
set(value: T): T;
|
|
42
22
|
}
|
|
43
23
|
declare const computed: <T>(fn: () => T extends unknown ? T : NeverAsync<T>, options?: Options) => Computed<T>;
|
|
@@ -45,7 +25,7 @@ declare const dispose: <T extends {
|
|
|
45
25
|
dispose: VoidFunction;
|
|
46
26
|
}>(dispose?: T | T[] | null | undefined) => T | T[] | null | undefined;
|
|
47
27
|
declare const effect: (fn: Effect['fn']) => Effect;
|
|
48
|
-
declare const root: <T>(fn: (root: Root) => T, scheduler?:
|
|
28
|
+
declare const root: <T>(fn: (root: Root) => T, scheduler?: Scheduler | null) => T;
|
|
49
29
|
declare const signal: <T>(value: T, options?: Options) => Signal<T>;
|
|
50
30
|
export { computed, dispose, effect, root, signal };
|
|
51
|
-
export {
|
|
31
|
+
export { Reactive };
|
package/build/signal.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
|
|
2
2
|
import { isArray } from './utilities';
|
|
3
3
|
let index = 0, observer = null, observers = null, scope = null;
|
|
4
|
-
class
|
|
4
|
+
class Reactive {
|
|
5
|
+
changed = null;
|
|
6
|
+
fn = null;
|
|
5
7
|
listeners = null;
|
|
6
8
|
observers = null;
|
|
7
9
|
root;
|
|
10
|
+
scheduler = null;
|
|
8
11
|
sources = null;
|
|
9
12
|
state;
|
|
10
|
-
|
|
13
|
+
task = null;
|
|
14
|
+
tracking = null;
|
|
15
|
+
type;
|
|
11
16
|
value;
|
|
12
|
-
constructor(state, value) {
|
|
17
|
+
constructor(state, type, value) {
|
|
13
18
|
let root = null;
|
|
14
|
-
if (
|
|
19
|
+
if (type !== ROOT) {
|
|
15
20
|
if (scope !== null) {
|
|
16
21
|
root = scope;
|
|
17
22
|
}
|
|
@@ -19,7 +24,7 @@ class Core {
|
|
|
19
24
|
root = observer.root;
|
|
20
25
|
}
|
|
21
26
|
if (root == null) {
|
|
22
|
-
if (
|
|
27
|
+
if (type === EFFECT) {
|
|
23
28
|
throw new Error(`Reactivity: 'effect' cannot be created without a reactive root`);
|
|
24
29
|
}
|
|
25
30
|
}
|
|
@@ -29,16 +34,14 @@ class Core {
|
|
|
29
34
|
}
|
|
30
35
|
this.root = root;
|
|
31
36
|
this.state = state;
|
|
37
|
+
this.type = type;
|
|
32
38
|
this.value = value;
|
|
33
39
|
}
|
|
34
|
-
get type() {
|
|
35
|
-
throw new Error(`Reactivity: reactive primitives require 'type' getters`);
|
|
36
|
-
}
|
|
37
40
|
dispatch(event, data) {
|
|
38
41
|
if (this.listeners === null || this.listeners[event] === undefined) {
|
|
39
42
|
return;
|
|
40
43
|
}
|
|
41
|
-
let listeners = this.listeners[event],
|
|
44
|
+
let listeners = this.listeners[event], values = {
|
|
42
45
|
data,
|
|
43
46
|
value: this.value
|
|
44
47
|
};
|
|
@@ -48,7 +51,7 @@ class Core {
|
|
|
48
51
|
continue;
|
|
49
52
|
}
|
|
50
53
|
try {
|
|
51
|
-
listener(
|
|
54
|
+
listener(values);
|
|
52
55
|
if (listener.once !== undefined) {
|
|
53
56
|
listeners[i] = null;
|
|
54
57
|
}
|
|
@@ -69,9 +72,31 @@ class Core {
|
|
|
69
72
|
this.sources = null;
|
|
70
73
|
this.state = DISPOSED;
|
|
71
74
|
}
|
|
75
|
+
get() {
|
|
76
|
+
if (this.state === DISPOSED) {
|
|
77
|
+
return this.value;
|
|
78
|
+
}
|
|
79
|
+
if (observer !== null) {
|
|
80
|
+
if (observers === null) {
|
|
81
|
+
if (observer.sources !== null && observer.sources[index] == this) {
|
|
82
|
+
index++;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
observers = [this];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
observers.push(this);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (this.type === COMPUTED || this.type === EFFECT) {
|
|
93
|
+
sync(this);
|
|
94
|
+
}
|
|
95
|
+
return this.value;
|
|
96
|
+
}
|
|
72
97
|
on(event, listener) {
|
|
73
|
-
if (this.
|
|
74
|
-
|
|
98
|
+
if (this.state === DIRTY) {
|
|
99
|
+
return;
|
|
75
100
|
}
|
|
76
101
|
if (this.listeners === null) {
|
|
77
102
|
this.listeners = { [event]: [listener] };
|
|
@@ -96,61 +121,15 @@ class Core {
|
|
|
96
121
|
listener.once = true;
|
|
97
122
|
this.on(event, listener);
|
|
98
123
|
}
|
|
99
|
-
}
|
|
100
|
-
class Computed extends Core {
|
|
101
|
-
changed;
|
|
102
|
-
fn;
|
|
103
|
-
constructor(fn, options) {
|
|
104
|
-
super(DIRTY, undefined);
|
|
105
|
-
this.changed = options?.changed || changed;
|
|
106
|
-
this.fn = fn;
|
|
107
|
-
}
|
|
108
|
-
get type() {
|
|
109
|
-
return COMPUTED;
|
|
110
|
-
}
|
|
111
|
-
get() {
|
|
112
|
-
return read(this);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
class Effect extends Core {
|
|
116
|
-
fn;
|
|
117
|
-
task;
|
|
118
|
-
constructor(fn) {
|
|
119
|
-
super(DIRTY, null);
|
|
120
|
-
this.fn = fn;
|
|
121
|
-
this.task = () => read(this);
|
|
122
|
-
update(this);
|
|
123
|
-
}
|
|
124
|
-
get type() {
|
|
125
|
-
return EFFECT;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
class Root extends Core {
|
|
129
|
-
scheduler;
|
|
130
|
-
tracking;
|
|
131
|
-
constructor(scheduler, tracking) {
|
|
132
|
-
super(CLEAN, null);
|
|
133
|
-
this.scheduler = scheduler;
|
|
134
|
-
this.tracking = tracking;
|
|
135
|
-
}
|
|
136
|
-
get type() {
|
|
137
|
-
return ROOT;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
class Signal extends Core {
|
|
141
|
-
changed;
|
|
142
|
-
constructor(data, options) {
|
|
143
|
-
super(CLEAN, data);
|
|
144
|
-
this.changed = options?.changed || changed;
|
|
145
|
-
}
|
|
146
|
-
get type() {
|
|
147
|
-
return SIGNAL;
|
|
148
|
-
}
|
|
149
|
-
get() {
|
|
150
|
-
return read(this);
|
|
151
|
-
}
|
|
152
124
|
set(value) {
|
|
153
|
-
|
|
125
|
+
if (this.type !== SIGNAL && observer !== this) {
|
|
126
|
+
throw new Error(`Reactivity: 'set' method is only available on signals`);
|
|
127
|
+
}
|
|
128
|
+
if (this.changed(this.value, value)) {
|
|
129
|
+
this.value = value;
|
|
130
|
+
notify(this.observers, DIRTY);
|
|
131
|
+
}
|
|
132
|
+
return this.value;
|
|
154
133
|
}
|
|
155
134
|
}
|
|
156
135
|
function changed(a, b) {
|
|
@@ -171,28 +150,6 @@ function notify(nodes, state) {
|
|
|
171
150
|
}
|
|
172
151
|
}
|
|
173
152
|
}
|
|
174
|
-
function read(node) {
|
|
175
|
-
if (node.state === DISPOSED) {
|
|
176
|
-
return node.value;
|
|
177
|
-
}
|
|
178
|
-
if (observer !== null) {
|
|
179
|
-
if (observers === null) {
|
|
180
|
-
if (observer.sources !== null && observer.sources[index] == node) {
|
|
181
|
-
index++;
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
observers = [node];
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
observers.push(node);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (node.type === COMPUTED || node.type === EFFECT) {
|
|
192
|
-
sync(node);
|
|
193
|
-
}
|
|
194
|
-
return node.value;
|
|
195
|
-
}
|
|
196
153
|
function removeSourceObservers(node, start) {
|
|
197
154
|
if (node.sources === null) {
|
|
198
155
|
return;
|
|
@@ -229,9 +186,7 @@ function update(node) {
|
|
|
229
186
|
observers = null;
|
|
230
187
|
try {
|
|
231
188
|
node.dispatch('update');
|
|
232
|
-
node.updating = true;
|
|
233
189
|
let value = node.fn.call(node);
|
|
234
|
-
node.updating = false;
|
|
235
190
|
if (observers) {
|
|
236
191
|
removeSourceObservers(node, index);
|
|
237
192
|
if (node.sources !== null && index > 0) {
|
|
@@ -258,12 +213,7 @@ function update(node) {
|
|
|
258
213
|
node.sources.length = index;
|
|
259
214
|
}
|
|
260
215
|
if (node.type === COMPUTED) {
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch {
|
|
265
|
-
if (node.state === DIRTY) {
|
|
266
|
-
removeSourceObservers(node, 0);
|
|
216
|
+
node.set(value);
|
|
267
217
|
}
|
|
268
218
|
}
|
|
269
219
|
finally {
|
|
@@ -273,15 +223,11 @@ function update(node) {
|
|
|
273
223
|
}
|
|
274
224
|
node.state = CLEAN;
|
|
275
225
|
}
|
|
276
|
-
function write(node, value) {
|
|
277
|
-
if (node.changed(node.value, value)) {
|
|
278
|
-
node.value = value;
|
|
279
|
-
notify(node.observers, DIRTY);
|
|
280
|
-
}
|
|
281
|
-
return value;
|
|
282
|
-
}
|
|
283
226
|
const computed = (fn, options) => {
|
|
284
|
-
|
|
227
|
+
let instance = new Reactive(DIRTY, COMPUTED, undefined);
|
|
228
|
+
instance.changed = options?.changed || changed;
|
|
229
|
+
instance.fn = fn;
|
|
230
|
+
return instance;
|
|
285
231
|
};
|
|
286
232
|
const dispose = (dispose) => {
|
|
287
233
|
if (dispose == null) {
|
|
@@ -297,25 +243,33 @@ const dispose = (dispose) => {
|
|
|
297
243
|
return dispose;
|
|
298
244
|
};
|
|
299
245
|
const effect = (fn) => {
|
|
300
|
-
|
|
246
|
+
let instance = new Reactive(DIRTY, EFFECT, null);
|
|
247
|
+
instance.fn = fn;
|
|
248
|
+
instance.task = () => instance.get();
|
|
249
|
+
update(instance);
|
|
250
|
+
return instance;
|
|
301
251
|
};
|
|
302
|
-
const root = (fn, scheduler) => {
|
|
252
|
+
const root = (fn, scheduler = null) => {
|
|
303
253
|
let o = observer, s = scope;
|
|
304
|
-
if (scheduler ===
|
|
254
|
+
if (scheduler === null) {
|
|
305
255
|
if (scope === null) {
|
|
306
256
|
throw new Error('Reactivity: `root` cannot be created without a task scheduler');
|
|
307
257
|
}
|
|
308
258
|
scheduler = scope.scheduler;
|
|
309
259
|
}
|
|
310
260
|
observer = null;
|
|
311
|
-
scope = new
|
|
261
|
+
scope = new Reactive(CLEAN, ROOT, null);
|
|
262
|
+
scope.scheduler = scheduler;
|
|
263
|
+
scope.tracking = fn.length > 0;
|
|
312
264
|
let result = fn(scope);
|
|
313
265
|
observer = o;
|
|
314
266
|
scope = s;
|
|
315
267
|
return result;
|
|
316
268
|
};
|
|
317
269
|
const signal = (value, options) => {
|
|
318
|
-
|
|
270
|
+
let instance = new Reactive(CLEAN, SIGNAL, value);
|
|
271
|
+
instance.changed = options?.changed || changed;
|
|
272
|
+
return instance;
|
|
319
273
|
};
|
|
320
274
|
export { computed, dispose, effect, root, signal };
|
|
321
|
-
export {
|
|
275
|
+
export { Reactive };
|
package/build/types.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { Function, NeverAsync, Prettify } from '@esportsplus/typescript';
|
|
2
2
|
import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
|
|
3
|
-
import {
|
|
3
|
+
import { Reactive } from './signal';
|
|
4
|
+
type Base<T> = Omit<Reactive<T>, 'changed' | 'fn' | 'scheduler' | 'task' | 'tracking'>;
|
|
4
5
|
type Changed = (a: unknown, b: unknown) => boolean;
|
|
6
|
+
type Computed<T> = {
|
|
7
|
+
changed: Changed;
|
|
8
|
+
fn: NeverAsync<() => T>;
|
|
9
|
+
} & Base<T>;
|
|
10
|
+
type Effect = {
|
|
11
|
+
fn: NeverAsync<(node: Effect) => void>;
|
|
12
|
+
root: Root;
|
|
13
|
+
task: Function;
|
|
14
|
+
} & Omit<Base<void>, 'value'>;
|
|
5
15
|
type Event = string;
|
|
6
16
|
type Listener<D> = {
|
|
7
17
|
once?: boolean;
|
|
@@ -14,6 +24,15 @@ type Object = Record<PropertyKey, unknown>;
|
|
|
14
24
|
type Options = {
|
|
15
25
|
changed?: Changed;
|
|
16
26
|
};
|
|
27
|
+
type Root = {
|
|
28
|
+
scheduler: Scheduler;
|
|
29
|
+
tracking: boolean;
|
|
30
|
+
value: void;
|
|
31
|
+
} & Omit<Reactive<void>, 'changed' | 'fn' | 'root' | 'task'>;
|
|
32
|
+
type Scheduler = (fn: Function) => unknown;
|
|
33
|
+
type Signal<T> = {
|
|
34
|
+
changed: Changed;
|
|
35
|
+
} & Base<T>;
|
|
17
36
|
type State = typeof CHECK | typeof CLEAN | typeof DIRTY | typeof DISPOSED;
|
|
18
37
|
type Type = typeof COMPUTED | typeof EFFECT | typeof ROOT | typeof SIGNAL;
|
|
19
|
-
export type { Changed, Computed, Effect, Event, Function, Listener, Object, Options, NeverAsync, Prettify, Root, Signal, State, Type };
|
|
38
|
+
export type { Changed, Computed, Effect, Event, Function, Listener, Object, Options, NeverAsync, Prettify, Root, Scheduler, Signal, State, Type };
|
package/package.json
CHANGED
package/src/signal.ts
CHANGED
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
|
|
2
|
-
import { Changed, Event, Function, Listener, NeverAsync, Options, State, Type } from './types';
|
|
2
|
+
import { Computed, Changed, Effect, Event, Function, Listener, NeverAsync, Options, Root, Scheduler, Signal, State, Type } from './types';
|
|
3
3
|
import { isArray } from './utilities';
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
let index = 0,
|
|
7
|
-
observer:
|
|
8
|
-
observers:
|
|
7
|
+
observer: Reactive<any> | null = null,
|
|
8
|
+
observers: Reactive<any>[] | null = null,
|
|
9
9
|
scope: Root | null = null;
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class Reactive<T> {
|
|
13
|
+
changed: Changed | null = null;
|
|
14
|
+
fn: Computed<T>['fn'] | Effect['fn'] | null = null;
|
|
13
15
|
listeners: Record<Event, (Listener<any> | null)[]> | null = null;
|
|
14
|
-
observers:
|
|
16
|
+
observers: Reactive<any>[] | null = null;
|
|
15
17
|
root: Root | null;
|
|
16
|
-
|
|
18
|
+
scheduler: Scheduler | null = null;
|
|
19
|
+
sources: Reactive<any>[] | null = null;
|
|
17
20
|
state: State;
|
|
18
|
-
|
|
21
|
+
task: Function | null = null;
|
|
22
|
+
tracking: boolean | null = null;
|
|
23
|
+
type: Type;
|
|
19
24
|
value: T;
|
|
20
25
|
|
|
21
26
|
|
|
22
|
-
constructor(state: State, value: T) {
|
|
27
|
+
constructor(state: State, type: Type, value: T) {
|
|
23
28
|
let root = null;
|
|
24
29
|
|
|
25
|
-
if (
|
|
30
|
+
if (type !== ROOT) {
|
|
26
31
|
if (scope !== null) {
|
|
27
32
|
root = scope;
|
|
28
33
|
}
|
|
@@ -31,7 +36,7 @@ class Core<T> {
|
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
if (root == null) {
|
|
34
|
-
if (
|
|
39
|
+
if (type === EFFECT) {
|
|
35
40
|
throw new Error(`Reactivity: 'effect' cannot be created without a reactive root`);
|
|
36
41
|
}
|
|
37
42
|
}
|
|
@@ -42,22 +47,18 @@ class Core<T> {
|
|
|
42
47
|
|
|
43
48
|
this.root = root;
|
|
44
49
|
this.state = state;
|
|
50
|
+
this.type = type;
|
|
45
51
|
this.value = value;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
|
|
49
|
-
get type(): Type | never {
|
|
50
|
-
throw new Error(`Reactivity: reactive primitives require 'type' getters`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
55
|
dispatch<D>(event: Event, data?: D) {
|
|
55
56
|
if (this.listeners === null || this.listeners[event] === undefined) {
|
|
56
57
|
return;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
let listeners = this.listeners[event],
|
|
60
|
-
|
|
61
|
+
values = {
|
|
61
62
|
data,
|
|
62
63
|
value: this.value
|
|
63
64
|
};
|
|
@@ -70,7 +71,7 @@ class Core<T> {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
try {
|
|
73
|
-
listener(
|
|
74
|
+
listener(values);
|
|
74
75
|
|
|
75
76
|
if (listener.once !== undefined) {
|
|
76
77
|
listeners[i] = null;
|
|
@@ -97,9 +98,36 @@ class Core<T> {
|
|
|
97
98
|
this.state = DISPOSED;
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
get() {
|
|
102
|
+
if (this.state === DISPOSED) {
|
|
103
|
+
return this.value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (observer !== null) {
|
|
107
|
+
if (observers === null) {
|
|
108
|
+
if (observer.sources !== null && observer.sources[index] == this) {
|
|
109
|
+
index++;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
observers = [this];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
observers.push(this);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (this.type === COMPUTED || this.type === EFFECT) {
|
|
121
|
+
sync(this);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return this.value;
|
|
125
|
+
}
|
|
126
|
+
|
|
100
127
|
on<T>(event: Event, listener: Listener<T>) {
|
|
101
|
-
|
|
102
|
-
|
|
128
|
+
// Events cannot be set within effects or computed's
|
|
129
|
+
if (this.state === DIRTY) {
|
|
130
|
+
return;
|
|
103
131
|
}
|
|
104
132
|
|
|
105
133
|
if (this.listeners === null) {
|
|
@@ -128,87 +156,18 @@ class Core<T> {
|
|
|
128
156
|
listener.once = true;
|
|
129
157
|
this.on(event, listener);
|
|
130
158
|
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
class Computed<T> extends Core<T> {
|
|
134
|
-
changed: Changed;
|
|
135
|
-
fn: NeverAsync<() => T>;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
constructor(fn: Computed<T>['fn'], options?: Options) {
|
|
139
|
-
super(DIRTY, undefined as T);
|
|
140
|
-
this.changed = options?.changed || changed;
|
|
141
|
-
this.fn = fn;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
get type(): Type {
|
|
146
|
-
return COMPUTED;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
get() {
|
|
151
|
-
return read(this);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
class Effect extends Core<null> {
|
|
156
|
-
fn: NeverAsync<(node: Effect) => void>;
|
|
157
|
-
task: Function;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
constructor(fn: Effect['fn']) {
|
|
161
|
-
super(DIRTY, null);
|
|
162
|
-
this.fn = fn;
|
|
163
|
-
this.task = () => read(this);
|
|
164
|
-
|
|
165
|
-
update(this);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
get type(): Type {
|
|
170
|
-
return EFFECT;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
class Root extends Core<null> {
|
|
175
|
-
scheduler: (fn: Function) => unknown;
|
|
176
|
-
tracking: boolean;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
constructor(scheduler: Root['scheduler'], tracking: boolean) {
|
|
180
|
-
super(CLEAN, null);
|
|
181
|
-
this.scheduler = scheduler;
|
|
182
|
-
this.tracking = tracking;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
get type(): Type {
|
|
187
|
-
return ROOT;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
class Signal<T> extends Core<T> {
|
|
192
|
-
changed: Changed;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
constructor(data: T, options?: Options) {
|
|
196
|
-
super(CLEAN, data);
|
|
197
|
-
this.changed = options?.changed || changed;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
get type(): Type {
|
|
202
|
-
return SIGNAL;
|
|
203
|
-
}
|
|
204
159
|
|
|
160
|
+
set(value: T): T {
|
|
161
|
+
if (this.type !== SIGNAL && observer !== this) {
|
|
162
|
+
throw new Error(`Reactivity: 'set' method is only available on signals`);
|
|
163
|
+
}
|
|
205
164
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
165
|
+
if (this.changed!(this.value, value)) {
|
|
166
|
+
this.value = value;
|
|
167
|
+
notify(this.observers, DIRTY);
|
|
168
|
+
}
|
|
209
169
|
|
|
210
|
-
|
|
211
|
-
return write(this, value);
|
|
170
|
+
return this.value;
|
|
212
171
|
}
|
|
213
172
|
}
|
|
214
173
|
|
|
@@ -217,7 +176,7 @@ function changed(a: unknown, b: unknown) {
|
|
|
217
176
|
return a !== b;
|
|
218
177
|
}
|
|
219
178
|
|
|
220
|
-
function notify<T>(nodes:
|
|
179
|
+
function notify<T>(nodes: Reactive<T>[] | null, state: typeof CHECK | typeof DIRTY) {
|
|
221
180
|
if (nodes === null) {
|
|
222
181
|
return;
|
|
223
182
|
}
|
|
@@ -236,33 +195,7 @@ function notify<T>(nodes: Core<T>[] | null, state: typeof CHECK | typeof DIRTY)
|
|
|
236
195
|
}
|
|
237
196
|
}
|
|
238
197
|
|
|
239
|
-
function
|
|
240
|
-
if (node.state === DISPOSED) {
|
|
241
|
-
return node.value;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (observer !== null) {
|
|
245
|
-
if (observers === null) {
|
|
246
|
-
if (observer.sources !== null && observer.sources[index] == node) {
|
|
247
|
-
index++;
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
observers = [node];
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
observers.push(node);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (node.type === COMPUTED || node.type === EFFECT) {
|
|
259
|
-
sync(node);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return node.value;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function removeSourceObservers<T>(node: Core<T>, start: number) {
|
|
198
|
+
function removeSourceObservers<T>(node: Reactive<T>, start: number) {
|
|
266
199
|
if (node.sources === null) {
|
|
267
200
|
return;
|
|
268
201
|
}
|
|
@@ -279,7 +212,7 @@ function removeSourceObservers<T>(node: Core<T>, start: number) {
|
|
|
279
212
|
}
|
|
280
213
|
}
|
|
281
214
|
|
|
282
|
-
function sync<T>(node:
|
|
215
|
+
function sync<T>(node: Reactive<T>) {
|
|
283
216
|
if (node.state === CHECK && node.sources !== null) {
|
|
284
217
|
for (let i = 0, n = node.sources.length; i < n; i++) {
|
|
285
218
|
sync(node.sources[i]);
|
|
@@ -294,14 +227,14 @@ function sync<T>(node: Core<T>) {
|
|
|
294
227
|
}
|
|
295
228
|
|
|
296
229
|
if (node.state === DIRTY) {
|
|
297
|
-
update(node
|
|
230
|
+
update(node);
|
|
298
231
|
}
|
|
299
232
|
else {
|
|
300
233
|
node.state = CLEAN;
|
|
301
234
|
}
|
|
302
235
|
}
|
|
303
236
|
|
|
304
|
-
function update<T>(node:
|
|
237
|
+
function update<T>(node: Reactive<T>) {
|
|
305
238
|
let i = index,
|
|
306
239
|
o = observer,
|
|
307
240
|
os = observers;
|
|
@@ -312,13 +245,10 @@ function update<T>(node: Computed<T> | Effect) {
|
|
|
312
245
|
|
|
313
246
|
try {
|
|
314
247
|
node.dispatch('update');
|
|
315
|
-
node.updating = true;
|
|
316
248
|
|
|
317
249
|
// @ts-ignore
|
|
318
250
|
let value = node.fn.call(node);
|
|
319
251
|
|
|
320
|
-
node.updating = false;
|
|
321
|
-
|
|
322
252
|
if (observers) {
|
|
323
253
|
removeSourceObservers(node, index);
|
|
324
254
|
|
|
@@ -350,12 +280,7 @@ function update<T>(node: Computed<T> | Effect) {
|
|
|
350
280
|
}
|
|
351
281
|
|
|
352
282
|
if (node.type === COMPUTED) {
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
if (node.state === DIRTY) {
|
|
358
|
-
removeSourceObservers(node, 0);
|
|
283
|
+
node.set(value as T);
|
|
359
284
|
}
|
|
360
285
|
}
|
|
361
286
|
finally {
|
|
@@ -367,18 +292,14 @@ function update<T>(node: Computed<T> | Effect) {
|
|
|
367
292
|
node.state = CLEAN;
|
|
368
293
|
}
|
|
369
294
|
|
|
370
|
-
function write<T>(node: Computed<T> | Signal<T>, value: T) {
|
|
371
|
-
if (node.changed(node.value, value)) {
|
|
372
|
-
node.value = value;
|
|
373
|
-
notify(node.observers, DIRTY);
|
|
374
|
-
}
|
|
375
295
|
|
|
376
|
-
|
|
377
|
-
|
|
296
|
+
const computed = <T>(fn: Computed<T>['fn'], options?: Options) => {
|
|
297
|
+
let instance = new Reactive(DIRTY, COMPUTED, undefined as T) as any as Computed<T>;
|
|
378
298
|
|
|
299
|
+
instance.changed = options?.changed || changed;
|
|
300
|
+
instance.fn = fn;
|
|
379
301
|
|
|
380
|
-
|
|
381
|
-
return new Computed(fn, options);
|
|
302
|
+
return instance;
|
|
382
303
|
};
|
|
383
304
|
|
|
384
305
|
const dispose = <T extends { dispose: VoidFunction }>(dispose?: T[] | T | null) => {
|
|
@@ -397,14 +318,21 @@ const dispose = <T extends { dispose: VoidFunction }>(dispose?: T[] | T | null)
|
|
|
397
318
|
};
|
|
398
319
|
|
|
399
320
|
const effect = (fn: Effect['fn']) => {
|
|
400
|
-
|
|
321
|
+
let instance = new Reactive(DIRTY, EFFECT, null) as any as Effect;
|
|
322
|
+
|
|
323
|
+
instance.fn = fn;
|
|
324
|
+
instance.task = () => instance.get();
|
|
325
|
+
|
|
326
|
+
update(instance as any as Reactive<void>);
|
|
327
|
+
|
|
328
|
+
return instance;
|
|
401
329
|
};
|
|
402
330
|
|
|
403
|
-
const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler
|
|
331
|
+
const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler: Scheduler | null = null) => {
|
|
404
332
|
let o = observer,
|
|
405
333
|
s = scope;
|
|
406
334
|
|
|
407
|
-
if (scheduler ===
|
|
335
|
+
if (scheduler === null) {
|
|
408
336
|
if (scope === null) {
|
|
409
337
|
throw new Error('Reactivity: `root` cannot be created without a task scheduler');
|
|
410
338
|
}
|
|
@@ -413,7 +341,10 @@ const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler?: Root['scheduler'
|
|
|
413
341
|
}
|
|
414
342
|
|
|
415
343
|
observer = null;
|
|
416
|
-
|
|
344
|
+
|
|
345
|
+
scope = new Reactive(CLEAN, ROOT, null) as any as Root;
|
|
346
|
+
scope.scheduler = scheduler as Scheduler;
|
|
347
|
+
scope.tracking = fn.length > 0;
|
|
417
348
|
|
|
418
349
|
let result = fn(scope);
|
|
419
350
|
|
|
@@ -424,9 +355,13 @@ const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler?: Root['scheduler'
|
|
|
424
355
|
};
|
|
425
356
|
|
|
426
357
|
const signal = <T>(value: T, options?: Options) => {
|
|
427
|
-
|
|
358
|
+
let instance = new Reactive(CLEAN, SIGNAL, value) as any as Signal<T>;
|
|
359
|
+
|
|
360
|
+
instance.changed = options?.changed || changed;
|
|
361
|
+
|
|
362
|
+
return instance;
|
|
428
363
|
};
|
|
429
364
|
|
|
430
365
|
|
|
431
366
|
export { computed, dispose, effect, root, signal };
|
|
432
|
-
export {
|
|
367
|
+
export { Reactive };
|
package/src/types.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { Function, NeverAsync, Prettify } from '@esportsplus/typescript'
|
|
2
2
|
import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
|
|
3
|
-
import {
|
|
3
|
+
import { Reactive } from './signal';
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
type Base<T> = Omit<Reactive<T>, 'changed' | 'fn' | 'scheduler' | 'task' | 'tracking'>;
|
|
7
|
+
|
|
6
8
|
type Changed = (a: unknown, b: unknown) => boolean;
|
|
7
9
|
|
|
10
|
+
type Computed<T> = {
|
|
11
|
+
changed: Changed;
|
|
12
|
+
fn: NeverAsync<() => T>;
|
|
13
|
+
} & Base<T>;
|
|
14
|
+
|
|
15
|
+
type Effect = {
|
|
16
|
+
fn: NeverAsync<(node: Effect) => void>;
|
|
17
|
+
root: Root;
|
|
18
|
+
task: Function;
|
|
19
|
+
} & Omit<Base<void>, 'value'>;
|
|
20
|
+
|
|
8
21
|
type Event = string;
|
|
9
22
|
|
|
10
23
|
type Listener<D> = {
|
|
@@ -19,9 +32,21 @@ type Options = {
|
|
|
19
32
|
changed?: Changed;
|
|
20
33
|
};
|
|
21
34
|
|
|
35
|
+
type Root = {
|
|
36
|
+
scheduler: Scheduler;
|
|
37
|
+
tracking: boolean;
|
|
38
|
+
value: void;
|
|
39
|
+
} & Omit<Reactive<void>, 'changed' | 'fn' | 'root' | 'task'>;
|
|
40
|
+
|
|
41
|
+
type Scheduler = (fn: Function) => unknown;
|
|
42
|
+
|
|
43
|
+
type Signal<T> = {
|
|
44
|
+
changed: Changed;
|
|
45
|
+
} & Base<T>;
|
|
46
|
+
|
|
22
47
|
type State = typeof CHECK | typeof CLEAN | typeof DIRTY | typeof DISPOSED;
|
|
23
48
|
|
|
24
49
|
type Type = typeof COMPUTED | typeof EFFECT | typeof ROOT | typeof SIGNAL;
|
|
25
50
|
|
|
26
51
|
|
|
27
|
-
export type { Changed, Computed, Effect, Event, Function, Listener, Object, Options, NeverAsync, Prettify, Root, Signal, State, Type };
|
|
52
|
+
export type { Changed, Computed, Effect, Event, Function, Listener, Object, Options, NeverAsync, Prettify, Root, Scheduler, Signal, State, Type };
|