@esportsplus/reactivity 0.0.11 → 0.0.12
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/methods/reactive.js +32 -11
- package/build/reactive.d.ts +6 -8
- package/build/reactive.js +30 -25
- package/build/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/methods/reactive.ts +43 -18
- package/src/reactive.ts +43 -39
- package/src/types.ts +3 -1
|
@@ -1,29 +1,50 @@
|
|
|
1
1
|
import Reactive from '../reactive';
|
|
2
|
+
function factory(value) {
|
|
3
|
+
if (typeof value === 'function') {
|
|
4
|
+
return fn(value);
|
|
5
|
+
}
|
|
6
|
+
if (typeof value === 'object' && value !== null && (value.constructor === Object)) {
|
|
7
|
+
return obj(value);
|
|
8
|
+
}
|
|
9
|
+
return new Reactive(value);
|
|
10
|
+
}
|
|
11
|
+
function fn(value) {
|
|
12
|
+
let fn = new Reactive(value);
|
|
13
|
+
return (...args) => {
|
|
14
|
+
let value = fn.get();
|
|
15
|
+
if (args.length && typeof value === 'function') {
|
|
16
|
+
value = value(...args);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
2
21
|
function obj(values) {
|
|
3
22
|
let lazy = {}, properties = {};
|
|
4
23
|
for (let key in values) {
|
|
5
24
|
properties[key] = {
|
|
6
25
|
get() {
|
|
7
26
|
if (!lazy[key]) {
|
|
8
|
-
lazy[key] =
|
|
27
|
+
lazy[key] = factory(values[key]);
|
|
28
|
+
}
|
|
29
|
+
if (lazy[key] instanceof Reactive) {
|
|
30
|
+
return lazy[key].get();
|
|
9
31
|
}
|
|
10
|
-
return lazy[key]
|
|
32
|
+
return lazy[key];
|
|
11
33
|
},
|
|
12
34
|
set(value) {
|
|
13
35
|
if (!lazy[key]) {
|
|
14
|
-
lazy[key] =
|
|
36
|
+
lazy[key] = factory(values[key]);
|
|
37
|
+
}
|
|
38
|
+
if (lazy[key] instanceof Reactive) {
|
|
39
|
+
lazy[key].set(value);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
lazy[key] = factory(value);
|
|
15
43
|
}
|
|
16
|
-
lazy[key].set(value);
|
|
17
44
|
}
|
|
18
45
|
};
|
|
19
46
|
}
|
|
20
47
|
return Object.defineProperties({}, properties);
|
|
21
48
|
}
|
|
22
49
|
;
|
|
23
|
-
|
|
24
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
25
|
-
return obj(value);
|
|
26
|
-
}
|
|
27
|
-
return new Reactive(value);
|
|
28
|
-
}
|
|
29
|
-
export default (value) => reactive(value);
|
|
50
|
+
export default (value) => factory(value);
|
package/build/reactive.d.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { Scheduler } from './types';
|
|
2
|
-
type Fn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
|
|
1
|
+
import { ReactiveFn, Scheduler, State } from './types';
|
|
3
2
|
declare class Reactive<T> {
|
|
4
|
-
private effect;
|
|
5
3
|
private fn?;
|
|
6
|
-
private observers;
|
|
7
|
-
private sources;
|
|
8
|
-
private state;
|
|
9
4
|
private value;
|
|
5
|
+
effect: boolean;
|
|
10
6
|
cleanup: ((old: T) => void)[] | null;
|
|
11
|
-
|
|
7
|
+
observers: Reactive<any>[] | null;
|
|
8
|
+
sources: Reactive<any>[] | null;
|
|
9
|
+
state: State;
|
|
10
|
+
constructor(data: ReactiveFn<T> | T, effect?: boolean);
|
|
12
11
|
get(): T extends (...args: any[]) => any ? ReturnType<T> : T;
|
|
13
12
|
set(value: T): void;
|
|
14
|
-
private mark;
|
|
15
13
|
private removeParentObservers;
|
|
16
14
|
private sync;
|
|
17
15
|
private update;
|
package/build/reactive.js
CHANGED
|
@@ -1,19 +1,43 @@
|
|
|
1
1
|
import { CLEAN, CHECK, DIRTY } from './symbols';
|
|
2
|
-
let index = 0, reaction = null, queue = [], schedulers = new Set, stack = null;
|
|
2
|
+
let index = 0, reaction = null, queue = [], scheduled = false, schedulers = new Set, stack = null;
|
|
3
|
+
function mark(instances, state) {
|
|
4
|
+
if (!instances) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
for (let i = 0, n = instances.length; i < n; i++) {
|
|
8
|
+
let instance = instances[i];
|
|
9
|
+
if (instance.state < state) {
|
|
10
|
+
if (instance.effect && instance.state === CLEAN) {
|
|
11
|
+
queue.push(instance);
|
|
12
|
+
if (!scheduled) {
|
|
13
|
+
for (let scheduler of schedulers) {
|
|
14
|
+
scheduler.schedule();
|
|
15
|
+
}
|
|
16
|
+
scheduled = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
instance.state = state;
|
|
20
|
+
if (instance.observers) {
|
|
21
|
+
mark(instance.observers, CHECK);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
3
26
|
async function task() {
|
|
4
27
|
for (let i = 0, n = queue.length; i < n; i++) {
|
|
5
28
|
await queue[i].get();
|
|
6
29
|
}
|
|
7
30
|
queue.length = 0;
|
|
31
|
+
scheduled = false;
|
|
8
32
|
}
|
|
9
33
|
class Reactive {
|
|
10
|
-
effect;
|
|
11
34
|
fn;
|
|
35
|
+
value;
|
|
36
|
+
effect;
|
|
37
|
+
cleanup = null;
|
|
12
38
|
observers = null;
|
|
13
39
|
sources = null;
|
|
14
40
|
state;
|
|
15
|
-
value;
|
|
16
|
-
cleanup = null;
|
|
17
41
|
constructor(data, effect = false) {
|
|
18
42
|
this.effect = effect;
|
|
19
43
|
if (typeof data === 'function') {
|
|
@@ -49,30 +73,11 @@ class Reactive {
|
|
|
49
73
|
return this.value;
|
|
50
74
|
}
|
|
51
75
|
set(value) {
|
|
52
|
-
if (this.
|
|
53
|
-
|
|
54
|
-
this.observers[i].mark(DIRTY);
|
|
55
|
-
}
|
|
76
|
+
if (this.value !== value) {
|
|
77
|
+
mark(this.observers, DIRTY);
|
|
56
78
|
}
|
|
57
79
|
this.value = value;
|
|
58
80
|
}
|
|
59
|
-
mark(state) {
|
|
60
|
-
if (this.state < state) {
|
|
61
|
-
if (this.effect && this.state === CLEAN) {
|
|
62
|
-
queue.push(this);
|
|
63
|
-
for (let scheduler of schedulers) {
|
|
64
|
-
scheduler.schedule();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
this.state = state;
|
|
68
|
-
if (!this.observers) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
for (let i = 0; i < this.observers.length; i++) {
|
|
72
|
-
this.observers[i].mark(CHECK);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
81
|
removeParentObservers() {
|
|
77
82
|
if (!this.sources) {
|
|
78
83
|
return;
|
package/build/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ type Primitives = any[] | boolean | number | string | null | undefined | ((...ar
|
|
|
6
6
|
type InferNested<T> = T extends (...args: any[]) => any ? ReturnType<T> : T extends Record<string, Primitives> ? {
|
|
7
7
|
[K in keyof T]: InferNested<T[K]>;
|
|
8
8
|
} : T;
|
|
9
|
+
type ReactiveFn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
|
|
9
10
|
type Scheduler = {
|
|
10
11
|
schedule(): void;
|
|
11
12
|
tasks: {
|
|
@@ -14,4 +15,4 @@ type Scheduler = {
|
|
|
14
15
|
};
|
|
15
16
|
};
|
|
16
17
|
type State = typeof CHECK | typeof CLEAN | typeof DIRTY;
|
|
17
|
-
export { Infer, Scheduler, State };
|
|
18
|
+
export { Infer, ReactiveFn, Scheduler, State };
|
package/package.json
CHANGED
package/src/methods/reactive.ts
CHANGED
|
@@ -2,7 +2,35 @@ import { Infer } from '~/types';
|
|
|
2
2
|
import Reactive from '~/reactive';
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function factory<T>(value: T) {
|
|
6
|
+
if (typeof value === 'function') {
|
|
7
|
+
return fn(value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (typeof value === 'object' && value !== null && (value.constructor === Object)) {
|
|
11
|
+
return obj(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return new Reactive(value) as T;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function fn<T>(value: T) {
|
|
18
|
+
let fn = new Reactive(value);
|
|
19
|
+
|
|
20
|
+
return (...args: any[]) => {
|
|
21
|
+
let value = fn.get();
|
|
22
|
+
|
|
23
|
+
if (args.length && typeof value === 'function') {
|
|
24
|
+
value = value(...args);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return value as typeof value extends (...args: any[]) => any
|
|
28
|
+
? ReturnType<typeof value>
|
|
29
|
+
: typeof value;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// TODO: Typecheck on `values` to get rid of lazy var?
|
|
6
34
|
function obj<T>(values: T) {
|
|
7
35
|
let lazy: Record<string, any> = {},
|
|
8
36
|
properties: PropertyDescriptorMap = {};
|
|
@@ -11,17 +39,26 @@ function obj<T>(values: T) {
|
|
|
11
39
|
properties[key] = {
|
|
12
40
|
get() {
|
|
13
41
|
if (!lazy[key]) {
|
|
14
|
-
lazy[key] =
|
|
42
|
+
lazy[key] = factory(values[key]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (lazy[key] instanceof Reactive) {
|
|
46
|
+
return lazy[key].get();
|
|
15
47
|
}
|
|
16
48
|
|
|
17
|
-
return lazy[key]
|
|
49
|
+
return lazy[key];
|
|
18
50
|
},
|
|
19
51
|
set(value: unknown) {
|
|
20
52
|
if (!lazy[key]) {
|
|
21
|
-
lazy[key] =
|
|
53
|
+
lazy[key] = factory(values[key]);
|
|
22
54
|
}
|
|
23
55
|
|
|
24
|
-
lazy[key]
|
|
56
|
+
if (lazy[key] instanceof Reactive) {
|
|
57
|
+
lazy[key].set(value);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
lazy[key] = factory(value);
|
|
61
|
+
}
|
|
25
62
|
}
|
|
26
63
|
};
|
|
27
64
|
}
|
|
@@ -29,17 +66,5 @@ function obj<T>(values: T) {
|
|
|
29
66
|
return Object.defineProperties({}, properties) as T;
|
|
30
67
|
};
|
|
31
68
|
|
|
32
|
-
function reactive<T>(value: T) {
|
|
33
|
-
// if (Array.isArray(value)) {
|
|
34
|
-
// TODO
|
|
35
|
-
// }
|
|
36
|
-
// TODO: Can remove isArray implementation is created
|
|
37
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
38
|
-
return obj(value);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return new Reactive(value) as T;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
69
|
|
|
45
|
-
export default <T>(value: T) =>
|
|
70
|
+
export default <T>(value: T) => factory(value) as Infer<T>;
|
package/src/reactive.ts
CHANGED
|
@@ -1,43 +1,73 @@
|
|
|
1
1
|
import { CLEAN, CHECK, DIRTY } from './symbols';
|
|
2
|
-
import { Scheduler, State } from './types';
|
|
2
|
+
import { ReactiveFn, Scheduler, State } from './types';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
let index = 0,
|
|
6
6
|
reaction: Reactive<any> | null = null,
|
|
7
7
|
queue: Reactive<any>[] = [],
|
|
8
|
+
scheduled = false,
|
|
8
9
|
schedulers = new Set<Scheduler>,
|
|
9
10
|
stack: Reactive<any>[] | null = null;
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
function mark(instances: Reactive<any>[] | null, state: typeof CHECK | typeof DIRTY) {
|
|
14
|
+
if (!instances) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (let i = 0, n = instances.length; i < n; i++) {
|
|
19
|
+
let instance = instances[i];
|
|
20
|
+
|
|
21
|
+
if (instance.state < state) {
|
|
22
|
+
// If previous state was clean we need to update effects
|
|
23
|
+
if (instance.effect && instance.state === CLEAN) {
|
|
24
|
+
queue.push(instance);
|
|
25
|
+
|
|
26
|
+
if (!scheduled) {
|
|
27
|
+
for (let scheduler of schedulers) {
|
|
28
|
+
scheduler.schedule();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
scheduled = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
instance.state = state;
|
|
36
|
+
|
|
37
|
+
if (instance.observers) {
|
|
38
|
+
mark(instance.observers, CHECK);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
async function task() {
|
|
13
45
|
for (let i = 0, n = queue.length; i < n; i++) {
|
|
14
46
|
await queue[i].get();
|
|
15
47
|
}
|
|
16
48
|
|
|
17
49
|
queue.length = 0;
|
|
50
|
+
scheduled = false;
|
|
18
51
|
}
|
|
19
52
|
|
|
20
53
|
|
|
21
|
-
type Fn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
|
|
22
|
-
|
|
23
|
-
|
|
24
54
|
class Reactive<T> {
|
|
25
|
-
private
|
|
26
|
-
private fn?: Fn<T>;
|
|
27
|
-
private observers: Reactive<any>[] | null = null;
|
|
28
|
-
private sources: Reactive<any>[] | null = null;
|
|
29
|
-
private state: State;
|
|
55
|
+
private fn?: ReactiveFn<T>;
|
|
30
56
|
private value: T;
|
|
31
57
|
|
|
32
58
|
|
|
59
|
+
effect: boolean;
|
|
33
60
|
cleanup: ((old: T) => void)[] | null = null;
|
|
61
|
+
observers: Reactive<any>[] | null = null;
|
|
62
|
+
sources: Reactive<any>[] | null = null;
|
|
63
|
+
state: State;
|
|
34
64
|
|
|
35
65
|
|
|
36
|
-
constructor(data:
|
|
66
|
+
constructor(data: ReactiveFn<T> | T, effect: boolean = false) {
|
|
37
67
|
this.effect = effect;
|
|
38
68
|
|
|
39
69
|
if (typeof data === 'function') {
|
|
40
|
-
this.fn = data as
|
|
70
|
+
this.fn = data as ReactiveFn<T>;
|
|
41
71
|
this.state = DIRTY;
|
|
42
72
|
this.value = undefined as any;
|
|
43
73
|
|
|
@@ -75,39 +105,13 @@ class Reactive<T> {
|
|
|
75
105
|
}
|
|
76
106
|
|
|
77
107
|
set(value: T) {
|
|
78
|
-
if (this.
|
|
79
|
-
|
|
80
|
-
this.observers[i].mark(DIRTY);
|
|
81
|
-
}
|
|
108
|
+
if (this.value !== value) {
|
|
109
|
+
mark(this.observers, DIRTY);
|
|
82
110
|
}
|
|
83
111
|
|
|
84
112
|
this.value = value;
|
|
85
113
|
}
|
|
86
114
|
|
|
87
|
-
|
|
88
|
-
private mark(state: typeof CHECK | typeof DIRTY) {
|
|
89
|
-
if (this.state < state) {
|
|
90
|
-
// If previous state was clean we need to update effects
|
|
91
|
-
if (this.effect && this.state === CLEAN) {
|
|
92
|
-
queue.push(this);
|
|
93
|
-
|
|
94
|
-
for (let scheduler of schedulers) {
|
|
95
|
-
scheduler.schedule();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
this.state = state;
|
|
100
|
-
|
|
101
|
-
if (!this.observers) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
for (let i = 0; i < this.observers.length; i++) {
|
|
106
|
-
this.observers[i].mark(CHECK);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
115
|
// We don't actually delete sources here because we're replacing the entire array soon
|
|
112
116
|
private removeParentObservers() {
|
|
113
117
|
if (!this.sources) {
|
package/src/types.ts
CHANGED
|
@@ -20,6 +20,8 @@ type InferNested<T> =
|
|
|
20
20
|
? { [K in keyof T]: InferNested<T[K]> }
|
|
21
21
|
: T;
|
|
22
22
|
|
|
23
|
+
type ReactiveFn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
|
|
24
|
+
|
|
23
25
|
type Scheduler = {
|
|
24
26
|
schedule(): void;
|
|
25
27
|
tasks: {
|
|
@@ -31,4 +33,4 @@ type Scheduler = {
|
|
|
31
33
|
type State = typeof CHECK | typeof CLEAN | typeof DIRTY;
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
export { Infer, Scheduler, State };
|
|
36
|
+
export { Infer, ReactiveFn, Scheduler, State };
|