@oscarpalmer/atoms 0.21.0 → 0.22.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/js/index.js +87 -6
- package/dist/js/proxy.js +181 -3
- package/dist/js/proxy.mjs +82 -4
- package/dist/js/value.js +7 -4
- package/dist/js/value.mjs +7 -4
- package/package.json +1 -1
- package/src/js/proxy.ts +146 -10
- package/src/js/value.ts +9 -4
- package/types/proxy.d.ts +17 -10
package/dist/js/index.js
CHANGED
|
@@ -310,14 +310,17 @@ var _getDiffs = function(first, second, prefix) {
|
|
|
310
310
|
const to = second?.[key];
|
|
311
311
|
if (!Object.is(from, to)) {
|
|
312
312
|
const prefixed = _getKey(prefix, key);
|
|
313
|
-
|
|
313
|
+
const change = {
|
|
314
314
|
from,
|
|
315
315
|
to,
|
|
316
316
|
key: prefixed
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
};
|
|
318
|
+
const nested = isArrayOrPlainObject(from) || isArrayOrPlainObject(to);
|
|
319
|
+
const diffs = nested ? _getDiffs(from, to, prefixed) : [];
|
|
320
|
+
if (!nested || nested && diffs.length > 0) {
|
|
321
|
+
changes.push(change);
|
|
320
322
|
}
|
|
323
|
+
changes.push(...diffs);
|
|
321
324
|
}
|
|
322
325
|
checked.add(key);
|
|
323
326
|
}
|
|
@@ -449,7 +452,17 @@ var _createProxy = function(existing, value2) {
|
|
|
449
452
|
return property === "$" ? manager : Reflect.get(target, property);
|
|
450
453
|
},
|
|
451
454
|
set(target, property, value3) {
|
|
452
|
-
|
|
455
|
+
if (property === "$") {
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
const isSubscribed = manager.subscribed;
|
|
459
|
+
const original = isSubscribed && !cloned.has(manager) ? clone(merge(manager.owner)) : undefined;
|
|
460
|
+
const actual = _createProxy(manager, value3);
|
|
461
|
+
const result = Reflect.set(target, property, actual);
|
|
462
|
+
if (result && isSubscribed) {
|
|
463
|
+
_onChange(manager, original);
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
453
466
|
}
|
|
454
467
|
});
|
|
455
468
|
const manager = existing ?? new Manager(proxy);
|
|
@@ -465,25 +478,90 @@ var _createProxy = function(existing, value2) {
|
|
|
465
478
|
}
|
|
466
479
|
return proxy;
|
|
467
480
|
};
|
|
481
|
+
var _emit = function(manager) {
|
|
482
|
+
const difference = diff(cloned.get(manager) ?? {}, clone(merge(manager.owner)));
|
|
483
|
+
const keys = Object.keys(difference.values);
|
|
484
|
+
const { length } = keys;
|
|
485
|
+
let index = 0;
|
|
486
|
+
for (;index < length; index += 1) {
|
|
487
|
+
const key = keys[index];
|
|
488
|
+
const subscribers = manager.subscribers.get(key);
|
|
489
|
+
if (subscribers === undefined || subscribers.size === 0) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
const { from } = difference.values[key];
|
|
493
|
+
const to = get(manager.owner, key);
|
|
494
|
+
for (const subscriber of subscribers) {
|
|
495
|
+
subscriber(to, from);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
cloned.delete(manager);
|
|
499
|
+
};
|
|
468
500
|
var _isProxy = function(value2) {
|
|
469
501
|
return value2?.$ instanceof Manager;
|
|
470
502
|
};
|
|
503
|
+
var _onChange = function(manager, value2) {
|
|
504
|
+
cancelAnimationFrame(frames.get(manager));
|
|
505
|
+
if (!cloned.has(manager)) {
|
|
506
|
+
cloned.set(manager, value2);
|
|
507
|
+
}
|
|
508
|
+
frames.set(manager, requestAnimationFrame(() => {
|
|
509
|
+
_emit(manager);
|
|
510
|
+
}));
|
|
511
|
+
};
|
|
512
|
+
function cloneProxy(proxy) {
|
|
513
|
+
if (!_isProxy(proxy)) {
|
|
514
|
+
throw new Error("Value must be a proxy");
|
|
515
|
+
}
|
|
516
|
+
return proxy.$.clone();
|
|
517
|
+
}
|
|
471
518
|
function proxy(value2) {
|
|
472
519
|
if (!isArrayOrPlainObject(value2)) {
|
|
473
520
|
throw new Error("Proxy value must be an array or object");
|
|
474
521
|
}
|
|
475
522
|
return _createProxy(undefined, value2);
|
|
476
523
|
}
|
|
524
|
+
function subscribe(proxy2, key, subscriber) {
|
|
525
|
+
if (_isProxy(proxy2)) {
|
|
526
|
+
proxy2.$.on(key, subscriber);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function unsubscribe(proxy2, key, subscriber) {
|
|
530
|
+
if (_isProxy(proxy2)) {
|
|
531
|
+
proxy2.$.off(key, subscriber);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
477
534
|
|
|
478
535
|
class Manager {
|
|
479
536
|
owner;
|
|
537
|
+
count = 0;
|
|
538
|
+
subscribers = new Map;
|
|
539
|
+
get subscribed() {
|
|
540
|
+
return this.count > 0;
|
|
541
|
+
}
|
|
480
542
|
constructor(owner) {
|
|
481
543
|
this.owner = owner;
|
|
482
544
|
}
|
|
483
545
|
clone() {
|
|
484
|
-
return clone(merge(this.owner));
|
|
546
|
+
return _createProxy(undefined, clone(merge(this.owner)));
|
|
547
|
+
}
|
|
548
|
+
off(key, subscriber) {
|
|
549
|
+
if (this.subscribers.get(key)?.delete(subscriber) ?? false) {
|
|
550
|
+
this.count -= 1;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
on(key, subscriber) {
|
|
554
|
+
let subscribers = this.subscribers.get(key);
|
|
555
|
+
if (subscribers === undefined) {
|
|
556
|
+
subscribers = new Set;
|
|
557
|
+
this.subscribers.set(key, subscribers);
|
|
558
|
+
}
|
|
559
|
+
subscribers.add(subscriber);
|
|
560
|
+
this.count += 1;
|
|
485
561
|
}
|
|
486
562
|
}
|
|
563
|
+
var cloned = new Map;
|
|
564
|
+
var frames = new Map;
|
|
487
565
|
// src/js/timer.ts
|
|
488
566
|
function repeat(callback, options) {
|
|
489
567
|
const count = typeof options?.count === "number" ? options.count : Infinity;
|
|
@@ -569,7 +647,9 @@ class Timer {
|
|
|
569
647
|
}
|
|
570
648
|
export {
|
|
571
649
|
wait,
|
|
650
|
+
unsubscribe,
|
|
572
651
|
unique,
|
|
652
|
+
subscribe,
|
|
573
653
|
splice,
|
|
574
654
|
set,
|
|
575
655
|
repeat,
|
|
@@ -593,6 +673,7 @@ export {
|
|
|
593
673
|
exists,
|
|
594
674
|
diff,
|
|
595
675
|
createUuid,
|
|
676
|
+
cloneProxy,
|
|
596
677
|
clone,
|
|
597
678
|
clamp,
|
|
598
679
|
chunk,
|
package/dist/js/proxy.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// src/js/string.ts
|
|
2
|
+
function getString(value) {
|
|
3
|
+
if (typeof value === "string") {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
const result = value?.toString?.() ?? value;
|
|
7
|
+
return result?.toString?.() ?? String(result);
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
// src/js/is.ts
|
|
2
11
|
function isArrayOrPlainObject(value) {
|
|
3
12
|
return Array.isArray(value) || isPlainObject(value);
|
|
@@ -11,9 +20,100 @@ function isPlainObject(value) {
|
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
// src/js/value.ts
|
|
23
|
+
var _getDiffs = function(first, second, prefix) {
|
|
24
|
+
const changes = [];
|
|
25
|
+
const checked = new Set;
|
|
26
|
+
let outer = 0;
|
|
27
|
+
for (;outer < 2; outer += 1) {
|
|
28
|
+
const value = outer === 0 ? first : second;
|
|
29
|
+
if (!value) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const keys = Object.keys(value);
|
|
33
|
+
const { length } = keys;
|
|
34
|
+
let inner = 0;
|
|
35
|
+
for (;inner < length; inner += 1) {
|
|
36
|
+
const key = keys[inner];
|
|
37
|
+
if (checked.has(key)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const from = first?.[key];
|
|
41
|
+
const to = second?.[key];
|
|
42
|
+
if (!Object.is(from, to)) {
|
|
43
|
+
const prefixed = _getKey(prefix, key);
|
|
44
|
+
const change = {
|
|
45
|
+
from,
|
|
46
|
+
to,
|
|
47
|
+
key: prefixed
|
|
48
|
+
};
|
|
49
|
+
const nested = isArrayOrPlainObject(from) || isArrayOrPlainObject(to);
|
|
50
|
+
const diffs = nested ? _getDiffs(from, to, prefixed) : [];
|
|
51
|
+
if (!nested || nested && diffs.length > 0) {
|
|
52
|
+
changes.push(change);
|
|
53
|
+
}
|
|
54
|
+
changes.push(...diffs);
|
|
55
|
+
}
|
|
56
|
+
checked.add(key);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return changes;
|
|
60
|
+
};
|
|
61
|
+
var _getKey = function(...parts) {
|
|
62
|
+
return parts.filter((part) => part !== undefined).join(".");
|
|
63
|
+
};
|
|
64
|
+
var _getValue = function(data, key) {
|
|
65
|
+
if (typeof data !== "object" || data === null || /^(__proto__|constructor|prototype)$/i.test(key)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
return data instanceof Map ? data.get(key) : data[key];
|
|
69
|
+
};
|
|
14
70
|
function clone(value) {
|
|
15
71
|
return structuredClone(value);
|
|
16
72
|
}
|
|
73
|
+
function diff(first, second) {
|
|
74
|
+
const result = {
|
|
75
|
+
original: {
|
|
76
|
+
from: first,
|
|
77
|
+
to: second
|
|
78
|
+
},
|
|
79
|
+
type: "partial",
|
|
80
|
+
values: {}
|
|
81
|
+
};
|
|
82
|
+
const same = Object.is(first, second);
|
|
83
|
+
const firstIsArrayOrObject = isArrayOrPlainObject(first);
|
|
84
|
+
const secondIsArrayOrObject = isArrayOrPlainObject(second);
|
|
85
|
+
if (same || !firstIsArrayOrObject && !secondIsArrayOrObject) {
|
|
86
|
+
result.type = same ? "none" : "full";
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
if (firstIsArrayOrObject !== secondIsArrayOrObject) {
|
|
90
|
+
result.type = "full";
|
|
91
|
+
}
|
|
92
|
+
const diffs = _getDiffs(first, second);
|
|
93
|
+
const { length } = diffs;
|
|
94
|
+
if (length === 0) {
|
|
95
|
+
result.type = "none";
|
|
96
|
+
}
|
|
97
|
+
let index = 0;
|
|
98
|
+
for (;index < length; index += 1) {
|
|
99
|
+
const diff2 = diffs[index];
|
|
100
|
+
result.values[diff2.key] = { from: diff2.from, to: diff2.to };
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
function get(data, key) {
|
|
105
|
+
const parts = getString(key).split(".");
|
|
106
|
+
const { length } = parts;
|
|
107
|
+
let index = 0;
|
|
108
|
+
let value = typeof data === "object" ? data ?? {} : {};
|
|
109
|
+
for (;index < length; index += 1) {
|
|
110
|
+
value = _getValue(value, parts[index]);
|
|
111
|
+
if (value == null) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
17
117
|
function merge(...values) {
|
|
18
118
|
if (values.length === 0) {
|
|
19
119
|
return {};
|
|
@@ -52,7 +152,17 @@ var _createProxy = function(existing, value2) {
|
|
|
52
152
|
return property === "$" ? manager : Reflect.get(target, property);
|
|
53
153
|
},
|
|
54
154
|
set(target, property, value3) {
|
|
55
|
-
|
|
155
|
+
if (property === "$") {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
const isSubscribed = manager.subscribed;
|
|
159
|
+
const original = isSubscribed && !cloned.has(manager) ? clone(merge(manager.owner)) : undefined;
|
|
160
|
+
const actual = _createProxy(manager, value3);
|
|
161
|
+
const result = Reflect.set(target, property, actual);
|
|
162
|
+
if (result && isSubscribed) {
|
|
163
|
+
_onChange(manager, original);
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
56
166
|
}
|
|
57
167
|
});
|
|
58
168
|
const manager = existing ?? new Manager(proxy);
|
|
@@ -68,25 +178,93 @@ var _createProxy = function(existing, value2) {
|
|
|
68
178
|
}
|
|
69
179
|
return proxy;
|
|
70
180
|
};
|
|
181
|
+
var _emit = function(manager) {
|
|
182
|
+
const difference = diff(cloned.get(manager) ?? {}, clone(merge(manager.owner)));
|
|
183
|
+
const keys = Object.keys(difference.values);
|
|
184
|
+
const { length } = keys;
|
|
185
|
+
let index = 0;
|
|
186
|
+
for (;index < length; index += 1) {
|
|
187
|
+
const key = keys[index];
|
|
188
|
+
const subscribers = manager.subscribers.get(key);
|
|
189
|
+
if (subscribers === undefined || subscribers.size === 0) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const { from } = difference.values[key];
|
|
193
|
+
const to = get(manager.owner, key);
|
|
194
|
+
for (const subscriber of subscribers) {
|
|
195
|
+
subscriber(to, from);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
cloned.delete(manager);
|
|
199
|
+
};
|
|
71
200
|
var _isProxy = function(value2) {
|
|
72
201
|
return value2?.$ instanceof Manager;
|
|
73
202
|
};
|
|
203
|
+
var _onChange = function(manager, value2) {
|
|
204
|
+
cancelAnimationFrame(frames.get(manager));
|
|
205
|
+
if (!cloned.has(manager)) {
|
|
206
|
+
cloned.set(manager, value2);
|
|
207
|
+
}
|
|
208
|
+
frames.set(manager, requestAnimationFrame(() => {
|
|
209
|
+
_emit(manager);
|
|
210
|
+
}));
|
|
211
|
+
};
|
|
212
|
+
function cloneProxy(proxy) {
|
|
213
|
+
if (!_isProxy(proxy)) {
|
|
214
|
+
throw new Error("Value must be a proxy");
|
|
215
|
+
}
|
|
216
|
+
return proxy.$.clone();
|
|
217
|
+
}
|
|
74
218
|
function proxy(value2) {
|
|
75
219
|
if (!isArrayOrPlainObject(value2)) {
|
|
76
220
|
throw new Error("Proxy value must be an array or object");
|
|
77
221
|
}
|
|
78
222
|
return _createProxy(undefined, value2);
|
|
79
223
|
}
|
|
224
|
+
function subscribe(proxy2, key, subscriber) {
|
|
225
|
+
if (_isProxy(proxy2)) {
|
|
226
|
+
proxy2.$.on(key, subscriber);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function unsubscribe(proxy2, key, subscriber) {
|
|
230
|
+
if (_isProxy(proxy2)) {
|
|
231
|
+
proxy2.$.off(key, subscriber);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
80
234
|
|
|
81
235
|
class Manager {
|
|
82
236
|
owner;
|
|
237
|
+
count = 0;
|
|
238
|
+
subscribers = new Map;
|
|
239
|
+
get subscribed() {
|
|
240
|
+
return this.count > 0;
|
|
241
|
+
}
|
|
83
242
|
constructor(owner) {
|
|
84
243
|
this.owner = owner;
|
|
85
244
|
}
|
|
86
245
|
clone() {
|
|
87
|
-
return clone(merge(this.owner));
|
|
246
|
+
return _createProxy(undefined, clone(merge(this.owner)));
|
|
247
|
+
}
|
|
248
|
+
off(key, subscriber) {
|
|
249
|
+
if (this.subscribers.get(key)?.delete(subscriber) ?? false) {
|
|
250
|
+
this.count -= 1;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
on(key, subscriber) {
|
|
254
|
+
let subscribers = this.subscribers.get(key);
|
|
255
|
+
if (subscribers === undefined) {
|
|
256
|
+
subscribers = new Set;
|
|
257
|
+
this.subscribers.set(key, subscribers);
|
|
258
|
+
}
|
|
259
|
+
subscribers.add(subscriber);
|
|
260
|
+
this.count += 1;
|
|
88
261
|
}
|
|
89
262
|
}
|
|
263
|
+
var cloned = new Map;
|
|
264
|
+
var frames = new Map;
|
|
90
265
|
export {
|
|
91
|
-
|
|
266
|
+
unsubscribe,
|
|
267
|
+
subscribe,
|
|
268
|
+
proxy,
|
|
269
|
+
cloneProxy
|
|
92
270
|
};
|
package/dist/js/proxy.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/js/proxy.ts
|
|
2
2
|
import {isArrayOrPlainObject} from "./is";
|
|
3
|
-
import {clone, merge} from "./value";
|
|
3
|
+
import {clone, diff, get, merge} from "./value";
|
|
4
4
|
var _createProxy = function(existing, value2) {
|
|
5
5
|
if (!isArrayOrPlainObject(value2) || _isProxy(value2) && value2.$ === existing) {
|
|
6
6
|
return value2;
|
|
@@ -11,7 +11,17 @@ var _createProxy = function(existing, value2) {
|
|
|
11
11
|
return property === "$" ? manager : Reflect.get(target, property);
|
|
12
12
|
},
|
|
13
13
|
set(target, property, value3) {
|
|
14
|
-
|
|
14
|
+
if (property === "$") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
const isSubscribed = manager.subscribed;
|
|
18
|
+
const original = isSubscribed && !cloned.has(manager) ? clone(merge(manager.owner)) : undefined;
|
|
19
|
+
const actual = _createProxy(manager, value3);
|
|
20
|
+
const result = Reflect.set(target, property, actual);
|
|
21
|
+
if (result && isSubscribed) {
|
|
22
|
+
_onChange(manager, original);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
15
25
|
}
|
|
16
26
|
});
|
|
17
27
|
const manager = existing ?? new Manager(proxy);
|
|
@@ -27,25 +37,93 @@ var _createProxy = function(existing, value2) {
|
|
|
27
37
|
}
|
|
28
38
|
return proxy;
|
|
29
39
|
};
|
|
40
|
+
var _emit = function(manager) {
|
|
41
|
+
const difference = diff(cloned.get(manager) ?? {}, clone(merge(manager.owner)));
|
|
42
|
+
const keys = Object.keys(difference.values);
|
|
43
|
+
const { length } = keys;
|
|
44
|
+
let index = 0;
|
|
45
|
+
for (;index < length; index += 1) {
|
|
46
|
+
const key = keys[index];
|
|
47
|
+
const subscribers = manager.subscribers.get(key);
|
|
48
|
+
if (subscribers === undefined || subscribers.size === 0) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const { from } = difference.values[key];
|
|
52
|
+
const to = get(manager.owner, key);
|
|
53
|
+
for (const subscriber of subscribers) {
|
|
54
|
+
subscriber(to, from);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
cloned.delete(manager);
|
|
58
|
+
};
|
|
30
59
|
var _isProxy = function(value2) {
|
|
31
60
|
return value2?.$ instanceof Manager;
|
|
32
61
|
};
|
|
62
|
+
var _onChange = function(manager, value2) {
|
|
63
|
+
cancelAnimationFrame(frames.get(manager));
|
|
64
|
+
if (!cloned.has(manager)) {
|
|
65
|
+
cloned.set(manager, value2);
|
|
66
|
+
}
|
|
67
|
+
frames.set(manager, requestAnimationFrame(() => {
|
|
68
|
+
_emit(manager);
|
|
69
|
+
}));
|
|
70
|
+
};
|
|
71
|
+
function cloneProxy(proxy) {
|
|
72
|
+
if (!_isProxy(proxy)) {
|
|
73
|
+
throw new Error("Value must be a proxy");
|
|
74
|
+
}
|
|
75
|
+
return proxy.$.clone();
|
|
76
|
+
}
|
|
33
77
|
function proxy(value2) {
|
|
34
78
|
if (!isArrayOrPlainObject(value2)) {
|
|
35
79
|
throw new Error("Proxy value must be an array or object");
|
|
36
80
|
}
|
|
37
81
|
return _createProxy(undefined, value2);
|
|
38
82
|
}
|
|
83
|
+
function subscribe(proxy2, key, subscriber) {
|
|
84
|
+
if (_isProxy(proxy2)) {
|
|
85
|
+
proxy2.$.on(key, subscriber);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function unsubscribe(proxy2, key, subscriber) {
|
|
89
|
+
if (_isProxy(proxy2)) {
|
|
90
|
+
proxy2.$.off(key, subscriber);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
39
93
|
|
|
40
94
|
class Manager {
|
|
41
95
|
owner;
|
|
96
|
+
count = 0;
|
|
97
|
+
subscribers = new Map;
|
|
98
|
+
get subscribed() {
|
|
99
|
+
return this.count > 0;
|
|
100
|
+
}
|
|
42
101
|
constructor(owner) {
|
|
43
102
|
this.owner = owner;
|
|
44
103
|
}
|
|
45
104
|
clone() {
|
|
46
|
-
return clone(merge(this.owner));
|
|
105
|
+
return _createProxy(undefined, clone(merge(this.owner)));
|
|
106
|
+
}
|
|
107
|
+
off(key, subscriber) {
|
|
108
|
+
if (this.subscribers.get(key)?.delete(subscriber) ?? false) {
|
|
109
|
+
this.count -= 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
on(key, subscriber) {
|
|
113
|
+
let subscribers = this.subscribers.get(key);
|
|
114
|
+
if (subscribers === undefined) {
|
|
115
|
+
subscribers = new Set;
|
|
116
|
+
this.subscribers.set(key, subscribers);
|
|
117
|
+
}
|
|
118
|
+
subscribers.add(subscriber);
|
|
119
|
+
this.count += 1;
|
|
47
120
|
}
|
|
48
121
|
}
|
|
122
|
+
var cloned = new Map;
|
|
123
|
+
var frames = new Map;
|
|
49
124
|
export {
|
|
50
|
-
|
|
125
|
+
unsubscribe,
|
|
126
|
+
subscribe,
|
|
127
|
+
proxy,
|
|
128
|
+
cloneProxy
|
|
51
129
|
};
|
package/dist/js/value.js
CHANGED
|
@@ -41,14 +41,17 @@ var _getDiffs = function(first, second, prefix) {
|
|
|
41
41
|
const to = second?.[key];
|
|
42
42
|
if (!Object.is(from, to)) {
|
|
43
43
|
const prefixed = _getKey(prefix, key);
|
|
44
|
-
|
|
44
|
+
const change = {
|
|
45
45
|
from,
|
|
46
46
|
to,
|
|
47
47
|
key: prefixed
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
};
|
|
49
|
+
const nested = isArrayOrPlainObject(from) || isArrayOrPlainObject(to);
|
|
50
|
+
const diffs = nested ? _getDiffs(from, to, prefixed) : [];
|
|
51
|
+
if (!nested || nested && diffs.length > 0) {
|
|
52
|
+
changes.push(change);
|
|
51
53
|
}
|
|
54
|
+
changes.push(...diffs);
|
|
52
55
|
}
|
|
53
56
|
checked.add(key);
|
|
54
57
|
}
|
package/dist/js/value.mjs
CHANGED
|
@@ -22,14 +22,17 @@ var _getDiffs = function(first, second, prefix) {
|
|
|
22
22
|
const to = second?.[key];
|
|
23
23
|
if (!Object.is(from, to)) {
|
|
24
24
|
const prefixed = _getKey(prefix, key);
|
|
25
|
-
|
|
25
|
+
const change = {
|
|
26
26
|
from,
|
|
27
27
|
to,
|
|
28
28
|
key: prefixed
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
};
|
|
30
|
+
const nested = isArrayOrPlainObject(from) || isArrayOrPlainObject(to);
|
|
31
|
+
const diffs = nested ? _getDiffs(from, to, prefixed) : [];
|
|
32
|
+
if (!nested || nested && diffs.length > 0) {
|
|
33
|
+
changes.push(change);
|
|
32
34
|
}
|
|
35
|
+
changes.push(...diffs);
|
|
33
36
|
}
|
|
34
37
|
checked.add(key);
|
|
35
38
|
}
|
package/package.json
CHANGED
package/src/js/proxy.ts
CHANGED
|
@@ -1,18 +1,54 @@
|
|
|
1
1
|
import {ArrayOrPlainObject, PlainObject, isArrayOrPlainObject} from './is';
|
|
2
|
-
import {clone, merge} from './value';
|
|
2
|
+
import {clone, diff, get, merge} from './value';
|
|
3
3
|
|
|
4
4
|
class Manager<T extends ArrayOrPlainObject = PlainObject> {
|
|
5
|
+
private count = 0;
|
|
6
|
+
|
|
7
|
+
readonly subscribers = new Map<string, Set<Subscriber>>();
|
|
8
|
+
|
|
9
|
+
get subscribed(): boolean {
|
|
10
|
+
return this.count > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
constructor(readonly owner: Proxied<T>) {}
|
|
6
14
|
|
|
7
|
-
clone():
|
|
8
|
-
return clone(merge(this.owner)) as
|
|
15
|
+
clone(): T {
|
|
16
|
+
return _createProxy(undefined, clone(merge(this.owner))) as T;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
off<T1 = unknown, T2 = T1>(
|
|
20
|
+
key: string,
|
|
21
|
+
subscriber: Subscriber<T1, T2>,
|
|
22
|
+
): void {
|
|
23
|
+
if (this.subscribers.get(key)?.delete(subscriber as never) ?? false) {
|
|
24
|
+
this.count -= 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
on<T1 = unknown, T2 = T1>(key: string, subscriber: Subscriber<T1, T2>): void {
|
|
29
|
+
let subscribers = this.subscribers.get(key);
|
|
30
|
+
|
|
31
|
+
if (subscribers === undefined) {
|
|
32
|
+
subscribers = new Set<Subscriber>();
|
|
33
|
+
|
|
34
|
+
this.subscribers.set(key, subscribers);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
subscribers.add(subscriber as never);
|
|
38
|
+
|
|
39
|
+
this.count += 1;
|
|
9
40
|
}
|
|
10
41
|
}
|
|
11
42
|
|
|
12
|
-
|
|
43
|
+
type Proxied<T extends ArrayOrPlainObject = PlainObject> = {
|
|
13
44
|
$: Manager<T>;
|
|
14
45
|
} & T;
|
|
15
46
|
|
|
47
|
+
export type Subscriber<T1 = unknown, T2 = T1> = (to: T1, from: T2) => void;
|
|
48
|
+
|
|
49
|
+
const cloned = new Map<Manager, unknown>();
|
|
50
|
+
const frames = new Map<Manager, number>();
|
|
51
|
+
|
|
16
52
|
function _createProxy<T extends ArrayOrPlainObject>(
|
|
17
53
|
existing: Manager | undefined,
|
|
18
54
|
value: T,
|
|
@@ -31,10 +67,25 @@ function _createProxy<T extends ArrayOrPlainObject>(
|
|
|
31
67
|
return property === '$' ? manager : Reflect.get(target, property);
|
|
32
68
|
},
|
|
33
69
|
set(target, property, value) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
if (property === '$') {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const isSubscribed = manager.subscribed;
|
|
75
|
+
|
|
76
|
+
const original =
|
|
77
|
+
isSubscribed && !cloned.has(manager)
|
|
78
|
+
? clone(merge(manager.owner))
|
|
79
|
+
: undefined;
|
|
80
|
+
|
|
81
|
+
const actual = _createProxy(manager, value);
|
|
82
|
+
const result = Reflect.set(target, property, actual);
|
|
83
|
+
|
|
84
|
+
if (result && isSubscribed) {
|
|
85
|
+
_onChange(manager, original);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return result;
|
|
38
89
|
},
|
|
39
90
|
}) as Proxied;
|
|
40
91
|
|
|
@@ -58,14 +109,99 @@ function _createProxy<T extends ArrayOrPlainObject>(
|
|
|
58
109
|
return proxy;
|
|
59
110
|
}
|
|
60
111
|
|
|
112
|
+
function _emit(manager: Manager): void {
|
|
113
|
+
const difference = diff(
|
|
114
|
+
cloned.get(manager) ?? {},
|
|
115
|
+
clone(merge(manager.owner)),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const keys = Object.keys(difference.values);
|
|
119
|
+
const {length} = keys;
|
|
120
|
+
|
|
121
|
+
let index = 0;
|
|
122
|
+
|
|
123
|
+
for (; index < length; index += 1) {
|
|
124
|
+
const key = keys[index];
|
|
125
|
+
const subscribers = manager.subscribers.get(key);
|
|
126
|
+
|
|
127
|
+
if (subscribers === undefined || subscribers.size === 0) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const {from} = difference.values[key];
|
|
132
|
+
const to = get(manager.owner, key);
|
|
133
|
+
|
|
134
|
+
for (const subscriber of subscribers) {
|
|
135
|
+
subscriber(to, from);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
cloned.delete(manager);
|
|
140
|
+
}
|
|
141
|
+
|
|
61
142
|
function _isProxy(value: unknown): value is Proxied {
|
|
62
143
|
return (value as Proxied)?.$ instanceof Manager;
|
|
63
144
|
}
|
|
64
145
|
|
|
65
|
-
|
|
146
|
+
function _onChange(manager: Manager, value: unknown): void {
|
|
147
|
+
cancelAnimationFrame(frames.get(manager) as number);
|
|
148
|
+
|
|
149
|
+
if (!cloned.has(manager)) {
|
|
150
|
+
cloned.set(manager, value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
frames.set(
|
|
154
|
+
manager,
|
|
155
|
+
requestAnimationFrame(() => {
|
|
156
|
+
_emit(manager);
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Clones and creates a new proxy
|
|
163
|
+
*/
|
|
164
|
+
export function cloneProxy<T extends ArrayOrPlainObject>(proxy: T): T {
|
|
165
|
+
if (!_isProxy(proxy)) {
|
|
166
|
+
throw new Error('Value must be a proxy');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return proxy.$.clone() as T;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Creates a proxy for an array or object
|
|
174
|
+
*/
|
|
175
|
+
export function proxy<T extends PlainObject>(value: T): T {
|
|
66
176
|
if (!isArrayOrPlainObject(value)) {
|
|
67
177
|
throw new Error('Proxy value must be an array or object');
|
|
68
178
|
}
|
|
69
179
|
|
|
70
|
-
return _createProxy(undefined, value) as
|
|
180
|
+
return _createProxy(undefined, value) as T;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Subscribes to changes for a key in a proxy
|
|
185
|
+
*/
|
|
186
|
+
export function subscribe<T1 = ArrayOrPlainObject, T2 = unknown, T3 = T2>(
|
|
187
|
+
proxy: T1,
|
|
188
|
+
key: string,
|
|
189
|
+
subscriber: Subscriber<T2, T3>,
|
|
190
|
+
): void {
|
|
191
|
+
if (_isProxy(proxy)) {
|
|
192
|
+
proxy.$.on(key, subscriber);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Unsubscribes from changes for a key in a proxy
|
|
198
|
+
*/
|
|
199
|
+
export function unsubscribe<T1 = ArrayOrPlainObject, T2 = unknown, T3 = T2>(
|
|
200
|
+
proxy: T1,
|
|
201
|
+
key: string,
|
|
202
|
+
subscriber: Subscriber<T2, T3>,
|
|
203
|
+
): void {
|
|
204
|
+
if (_isProxy(proxy)) {
|
|
205
|
+
proxy.$.off(key, subscriber);
|
|
206
|
+
}
|
|
71
207
|
}
|
package/src/js/value.ts
CHANGED
|
@@ -58,15 +58,20 @@ function _getDiffs(
|
|
|
58
58
|
if (!Object.is(from, to)) {
|
|
59
59
|
const prefixed = _getKey(prefix, key);
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
const change = {
|
|
62
62
|
from,
|
|
63
63
|
to,
|
|
64
64
|
key: prefixed,
|
|
65
|
-
}
|
|
65
|
+
};
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const nested = isArrayOrPlainObject(from) || isArrayOrPlainObject(to);
|
|
68
|
+
const diffs = nested ? _getDiffs(from, to, prefixed) : [];
|
|
69
|
+
|
|
70
|
+
if (!nested || (nested && diffs.length > 0)) {
|
|
71
|
+
changes.push(change);
|
|
69
72
|
}
|
|
73
|
+
|
|
74
|
+
changes.push(...diffs);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
checked.add(key);
|
package/types/proxy.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { ArrayOrPlainObject, PlainObject } from './is';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare function proxy<T extends PlainObject>(value: T):
|
|
11
|
-
|
|
2
|
+
export type Subscriber<T1 = unknown, T2 = T1> = (to: T1, from: T2) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Clones and creates a new proxy
|
|
5
|
+
*/
|
|
6
|
+
export declare function cloneProxy<T extends ArrayOrPlainObject>(proxy: T): T;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a proxy for an array or object
|
|
9
|
+
*/
|
|
10
|
+
export declare function proxy<T extends PlainObject>(value: T): T;
|
|
11
|
+
/**
|
|
12
|
+
* Subscribes to changes for a key in a proxy
|
|
13
|
+
*/
|
|
14
|
+
export declare function subscribe<T1 = ArrayOrPlainObject, T2 = unknown, T3 = T2>(proxy: T1, key: string, subscriber: Subscriber<T2, T3>): void;
|
|
15
|
+
/**
|
|
16
|
+
* Unsubscribes from changes for a key in a proxy
|
|
17
|
+
*/
|
|
18
|
+
export declare function unsubscribe<T1 = ArrayOrPlainObject, T2 = unknown, T3 = T2>(proxy: T1, key: string, subscriber: Subscriber<T2, T3>): void;
|