aberdeen 1.0.11 → 1.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/README.md +1 -1
- package/dist/aberdeen.d.ts +38 -17
- package/dist/aberdeen.js +203 -34
- package/dist/aberdeen.js.map +3 -3
- package/dist/prediction.d.ts +2 -2
- package/dist/prediction.js.map +1 -1
- package/dist/route.js +2 -2
- package/dist/route.js.map +3 -3
- package/dist-min/aberdeen.js +5 -5
- package/dist-min/aberdeen.js.map +3 -3
- package/dist-min/prediction.js.map +1 -1
- package/dist-min/route.js +2 -2
- package/dist-min/route.js.map +3 -3
- package/html-to-aberdeen +354 -0
- package/package.json +2 -2
- package/src/aberdeen.ts +321 -99
- package/src/prediction.ts +4 -4
- package/src/route.ts +1 -1
package/src/aberdeen.ts
CHANGED
|
@@ -19,15 +19,8 @@ let runQueueDepth = 0; // Incremented when a queue event causes another queue ev
|
|
|
19
19
|
let topRedrawScope: Scope | undefined; // The scope that triggered the current redraw. Elements drawn at this scope level may trigger 'create' animations.
|
|
20
20
|
|
|
21
21
|
/** @internal */
|
|
22
|
-
export type TargetType = any[] | { [key: string]: any }
|
|
23
|
-
|
|
24
|
-
export type DatumType =
|
|
25
|
-
| TargetType
|
|
26
|
-
| boolean
|
|
27
|
-
| number
|
|
28
|
-
| string
|
|
29
|
-
| null
|
|
30
|
-
| undefined;
|
|
22
|
+
export type TargetType = any[] | { [key: string | symbol]: any } | Map<any, any>;
|
|
23
|
+
|
|
31
24
|
|
|
32
25
|
function queue(runner: QueueRunner) {
|
|
33
26
|
if (!sortedQueue) {
|
|
@@ -92,9 +85,9 @@ export function runQueue(): void {
|
|
|
92
85
|
* be a number, string, or an array of numbers/strings. The sort key is used to sort items
|
|
93
86
|
* based on their values. The sort key can also be `undefined`, which indicates that the item
|
|
94
87
|
* should be ignored.
|
|
95
|
-
* @
|
|
88
|
+
* @internal
|
|
96
89
|
*/
|
|
97
|
-
export type SortKeyType = number | string | Array<number | string> | undefined;
|
|
90
|
+
export type SortKeyType = number | string | Array<number | string> | undefined | void;
|
|
98
91
|
|
|
99
92
|
/**
|
|
100
93
|
* Given an integer number or a string, this function returns a string that can be concatenated
|
|
@@ -173,7 +166,7 @@ abstract class Scope implements QueueRunner {
|
|
|
173
166
|
|
|
174
167
|
[ptr: ReverseSortedSetPointer]: this;
|
|
175
168
|
|
|
176
|
-
abstract onChange(index: any, newData:
|
|
169
|
+
abstract onChange(index: any, newData: any, oldData: any): void;
|
|
177
170
|
abstract queueRun(): void;
|
|
178
171
|
|
|
179
172
|
abstract getLastNode(): Node | undefined;
|
|
@@ -252,7 +245,7 @@ abstract class ContentScope extends Scope {
|
|
|
252
245
|
return this.getLastNode() || this.getPrecedingNode();
|
|
253
246
|
}
|
|
254
247
|
|
|
255
|
-
onChange(index: any, newData:
|
|
248
|
+
onChange(index: any, newData: any, oldData: any) {
|
|
256
249
|
queue(this);
|
|
257
250
|
}
|
|
258
251
|
|
|
@@ -445,7 +438,7 @@ class SetArgScope extends ChainedScope {
|
|
|
445
438
|
constructor(
|
|
446
439
|
parentElement: Element,
|
|
447
440
|
public key: string,
|
|
448
|
-
public target: { value:
|
|
441
|
+
public target: { value: any },
|
|
449
442
|
) {
|
|
450
443
|
super(parentElement);
|
|
451
444
|
this.redraw();
|
|
@@ -463,7 +456,7 @@ let immediateQueue: ReverseSortedSet<Scope, "prio"> = new ReverseSortedSet(
|
|
|
463
456
|
);
|
|
464
457
|
|
|
465
458
|
class ImmediateScope extends RegularScope {
|
|
466
|
-
onChange(index: any, newData:
|
|
459
|
+
onChange(index: any, newData: any, oldData: any) {
|
|
467
460
|
immediateQueue.add(this);
|
|
468
461
|
}
|
|
469
462
|
}
|
|
@@ -518,9 +511,9 @@ class OnEachScope extends Scope {
|
|
|
518
511
|
constructor(
|
|
519
512
|
proxy: TargetType,
|
|
520
513
|
/** A function that renders an item */
|
|
521
|
-
public renderer: (value:
|
|
514
|
+
public renderer: (value: any, key: any) => void,
|
|
522
515
|
/** A function returning a number/string/array that defines the position of an item */
|
|
523
|
-
public makeSortKey?: (value:
|
|
516
|
+
public makeSortKey?: (value: any, key: any) => SortKeyType,
|
|
524
517
|
) {
|
|
525
518
|
super();
|
|
526
519
|
const target: TargetType = (this.target =
|
|
@@ -540,8 +533,8 @@ class OnEachScope extends Scope {
|
|
|
540
533
|
}
|
|
541
534
|
}
|
|
542
535
|
} else {
|
|
543
|
-
for (const key
|
|
544
|
-
if (
|
|
536
|
+
for (const [key, value] of getEntries(target)) {
|
|
537
|
+
if (value !== undefined) {
|
|
545
538
|
new OnEachItemScope(this, key, false);
|
|
546
539
|
}
|
|
547
540
|
}
|
|
@@ -552,7 +545,7 @@ class OnEachScope extends Scope {
|
|
|
552
545
|
return findLastNodeInPrevSiblings(this.prevSibling);
|
|
553
546
|
}
|
|
554
547
|
|
|
555
|
-
onChange(index: any, newData:
|
|
548
|
+
onChange(index: any, newData: any, oldData: any) {
|
|
556
549
|
if (!(this.target instanceof Array) || typeof index === "number")
|
|
557
550
|
this.changedIndexes.add(index);
|
|
558
551
|
queue(this);
|
|
@@ -565,7 +558,14 @@ class OnEachScope extends Scope {
|
|
|
565
558
|
const oldScope = this.byIndex.get(index);
|
|
566
559
|
if (oldScope) oldScope.remove();
|
|
567
560
|
|
|
568
|
-
|
|
561
|
+
let hasValue;
|
|
562
|
+
if (this.target instanceof Map) {
|
|
563
|
+
hasValue = this.target.has(index);
|
|
564
|
+
} else {
|
|
565
|
+
hasValue = (this.target as any)[index] !== undefined;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (!hasValue) {
|
|
569
569
|
this.byIndex.delete(index);
|
|
570
570
|
} else {
|
|
571
571
|
new OnEachItemScope(this, index, true);
|
|
@@ -688,9 +688,16 @@ class OnEachItemScope extends ContentScope {
|
|
|
688
688
|
// a wildcard subscription to delete/recreate any scopes when that changes.
|
|
689
689
|
// We ARE creating a proxy around the value though (in case its an object/array),
|
|
690
690
|
// so we'll have our own scope subscribe to changes on that.
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
691
|
+
let value: any;
|
|
692
|
+
const target = this.parent.target;
|
|
693
|
+
let itemIndex = this.itemIndex;
|
|
694
|
+
if (target instanceof Map) {
|
|
695
|
+
value = optProxy(target.get(itemIndex));
|
|
696
|
+
// For Maps, the key may be an object. If so, we'll proxy it as well.
|
|
697
|
+
itemIndex = optProxy(itemIndex);
|
|
698
|
+
} else {
|
|
699
|
+
value = optProxy((target as any)[itemIndex]);
|
|
700
|
+
}
|
|
694
701
|
|
|
695
702
|
// Since makeSortKey may get() the Store, we'll need to set currentScope first.
|
|
696
703
|
const savedScope = currentScope;
|
|
@@ -699,14 +706,14 @@ class OnEachItemScope extends ContentScope {
|
|
|
699
706
|
let sortKey: undefined | string | number;
|
|
700
707
|
try {
|
|
701
708
|
if (this.parent.makeSortKey) {
|
|
702
|
-
const rawSortKey = this.parent.makeSortKey(value,
|
|
709
|
+
const rawSortKey = this.parent.makeSortKey(value, itemIndex);
|
|
703
710
|
if (rawSortKey != null)
|
|
704
711
|
sortKey =
|
|
705
712
|
rawSortKey instanceof Array
|
|
706
713
|
? rawSortKey.map(partToStr).join("")
|
|
707
714
|
: rawSortKey;
|
|
708
715
|
} else {
|
|
709
|
-
sortKey =
|
|
716
|
+
sortKey = itemIndex;
|
|
710
717
|
}
|
|
711
718
|
if (typeof sortKey === "number") sortKey = partToStr(sortKey);
|
|
712
719
|
|
|
@@ -720,7 +727,7 @@ class OnEachItemScope extends ContentScope {
|
|
|
720
727
|
// We're not adding `this` to the `sortedSet` (yet), as that may not be needed,
|
|
721
728
|
// in case no nodes are created. We'll do it just-in-time in `getPrecedingNode`.
|
|
722
729
|
|
|
723
|
-
if (sortKey != null) this.parent.renderer(value,
|
|
730
|
+
if (sortKey != null) this.parent.renderer(value, itemIndex);
|
|
724
731
|
} catch (e) {
|
|
725
732
|
handleError(e, sortKey != null);
|
|
726
733
|
}
|
|
@@ -778,11 +785,16 @@ const ANY_SYMBOL = Symbol("any");
|
|
|
778
785
|
*/
|
|
779
786
|
const TARGET_SYMBOL = Symbol("target");
|
|
780
787
|
|
|
788
|
+
/**
|
|
789
|
+
* Symbol used internally to track Map size without clashing with actual Map keys named "size".
|
|
790
|
+
*/
|
|
791
|
+
const MAP_SIZE_SYMBOL = Symbol("mapSize");
|
|
792
|
+
|
|
781
793
|
const subscribers = new WeakMap<
|
|
782
794
|
TargetType,
|
|
783
795
|
Map<
|
|
784
796
|
any,
|
|
785
|
-
Set<Scope | ((index: any, newData:
|
|
797
|
+
Set<Scope | ((index: any, newData: any, oldData: any) => void)>
|
|
786
798
|
>
|
|
787
799
|
>();
|
|
788
800
|
let peeking = 0; // When > 0, we're not subscribing to any changes
|
|
@@ -794,8 +806,8 @@ function subscribe(
|
|
|
794
806
|
| Scope
|
|
795
807
|
| ((
|
|
796
808
|
index: any,
|
|
797
|
-
newData:
|
|
798
|
-
oldData:
|
|
809
|
+
newData: any,
|
|
810
|
+
oldData: any,
|
|
799
811
|
) => void) = currentScope,
|
|
800
812
|
) {
|
|
801
813
|
if (observer === ROOT_SCOPE || peeking) return;
|
|
@@ -822,10 +834,18 @@ function subscribe(
|
|
|
822
834
|
}
|
|
823
835
|
}
|
|
824
836
|
|
|
825
|
-
|
|
826
|
-
|
|
837
|
+
/**
|
|
838
|
+
* Records in TypeScript pretend that they can have number keys, but in reality they are converted to string.
|
|
839
|
+
* This type changes (number | something) types to (string | something) types, maintaining typing precision as much as possible.
|
|
840
|
+
* @internal
|
|
841
|
+
*/
|
|
827
842
|
type KeyToString<K> = K extends number ? string : K extends string | symbol ? K : K extends number | infer U ? string | U : K;
|
|
828
843
|
|
|
844
|
+
export function onEach<K, T>(
|
|
845
|
+
target: Map<K, undefined | T>,
|
|
846
|
+
render: (value: T, key: K) => void,
|
|
847
|
+
makeKey?: (value: T, key: K) => SortKeyType,
|
|
848
|
+
): void;
|
|
829
849
|
export function onEach<T>(
|
|
830
850
|
target: ReadonlyArray<undefined | T>,
|
|
831
851
|
render: (value: T, index: number) => void,
|
|
@@ -894,8 +914,8 @@ export function onEach<K extends string | number | symbol, T>(
|
|
|
894
914
|
*/
|
|
895
915
|
export function onEach(
|
|
896
916
|
target: TargetType,
|
|
897
|
-
render: (value:
|
|
898
|
-
makeKey?: (value:
|
|
917
|
+
render: (value: any, index: any) => void,
|
|
918
|
+
makeKey?: (value: any, key: any) => SortKeyType,
|
|
899
919
|
): void {
|
|
900
920
|
if (!target || typeof target !== "object")
|
|
901
921
|
throw new Error("onEach requires an object");
|
|
@@ -946,23 +966,23 @@ export function isEmpty(proxied: TargetType): boolean {
|
|
|
946
966
|
const scope = currentScope;
|
|
947
967
|
|
|
948
968
|
if (target instanceof Array) {
|
|
949
|
-
subscribe(
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
(index: any, newData: DatumType, oldData: DatumType) => {
|
|
953
|
-
if (!newData !== !oldData) queue(scope);
|
|
954
|
-
},
|
|
955
|
-
);
|
|
969
|
+
subscribe(target, "length", (index: any, newData: any, oldData: any) => {
|
|
970
|
+
if (!newData !== !oldData) queue(scope);
|
|
971
|
+
});
|
|
956
972
|
return !target.length;
|
|
957
973
|
}
|
|
974
|
+
|
|
975
|
+
if (target instanceof Map) {
|
|
976
|
+
subscribe(target, MAP_SIZE_SYMBOL, (index: any, newData: any, oldData: any) => {
|
|
977
|
+
if (!newData !== !oldData) queue(scope);
|
|
978
|
+
});
|
|
979
|
+
return !target.size;
|
|
980
|
+
}
|
|
981
|
+
|
|
958
982
|
const result = isObjEmpty(target);
|
|
959
|
-
subscribe(
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
(index: any, newData: DatumType, oldData: DatumType) => {
|
|
963
|
-
if (result ? oldData === undefined : newData === undefined) queue(scope);
|
|
964
|
-
},
|
|
965
|
-
);
|
|
983
|
+
subscribe(target, ANY_SYMBOL, (index: any, newData: any, oldData: any) => {
|
|
984
|
+
if (result ? oldData === undefined : newData === undefined) queue(scope);
|
|
985
|
+
});
|
|
966
986
|
return result;
|
|
967
987
|
}
|
|
968
988
|
|
|
@@ -999,6 +1019,7 @@ export interface ValueRef<T> {
|
|
|
999
1019
|
*/
|
|
1000
1020
|
export function count(proxied: TargetType): ValueRef<number> {
|
|
1001
1021
|
if (proxied instanceof Array) return ref(proxied, "length");
|
|
1022
|
+
if (proxied instanceof Map) return ref(proxied, "size");
|
|
1002
1023
|
|
|
1003
1024
|
const target = (proxied as any)[TARGET_SYMBOL] || proxied;
|
|
1004
1025
|
let cnt = 0;
|
|
@@ -1008,7 +1029,7 @@ export function count(proxied: TargetType): ValueRef<number> {
|
|
|
1008
1029
|
subscribe(
|
|
1009
1030
|
target,
|
|
1010
1031
|
ANY_SYMBOL,
|
|
1011
|
-
(index: any, newData:
|
|
1032
|
+
(index: any, newData: any, oldData: any) => {
|
|
1012
1033
|
if (oldData === newData) {
|
|
1013
1034
|
} else if (oldData === undefined) result.value = ++cnt;
|
|
1014
1035
|
else if (newData === undefined) result.value = --cnt;
|
|
@@ -1022,8 +1043,8 @@ export function count(proxied: TargetType): ValueRef<number> {
|
|
|
1022
1043
|
export function defaultEmitHandler(
|
|
1023
1044
|
target: TargetType,
|
|
1024
1045
|
index: string | symbol | number,
|
|
1025
|
-
newData:
|
|
1026
|
-
oldData:
|
|
1046
|
+
newData: any,
|
|
1047
|
+
oldData: any,
|
|
1027
1048
|
) {
|
|
1028
1049
|
// We're triggering for values changing from undefined to undefined, as this *may*
|
|
1029
1050
|
// indicate a change from or to `[empty]` (such as `[,1][0]`).
|
|
@@ -1127,6 +1148,96 @@ const arrayHandler: ProxyHandler<any[]> = {
|
|
|
1127
1148
|
},
|
|
1128
1149
|
};
|
|
1129
1150
|
|
|
1151
|
+
const mapMethodHandlers = {
|
|
1152
|
+
get(this: any, key: any): any {
|
|
1153
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1154
|
+
subscribe(target, key);
|
|
1155
|
+
return optProxy(target.get(key));
|
|
1156
|
+
},
|
|
1157
|
+
set(this: any, key: any, newData: any): any {
|
|
1158
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1159
|
+
// Make sure newData is unproxied
|
|
1160
|
+
if (typeof newData === "object" && newData)
|
|
1161
|
+
newData = (newData as any)[TARGET_SYMBOL] || newData;
|
|
1162
|
+
const oldData = target.get(key);
|
|
1163
|
+
if (newData !== oldData) {
|
|
1164
|
+
target.set(key, newData);
|
|
1165
|
+
emit(target, key, newData, oldData);
|
|
1166
|
+
emit(target, MAP_SIZE_SYMBOL, target.size, target.size - (oldData === undefined ? 1 : 0));
|
|
1167
|
+
runImmediateQueue();
|
|
1168
|
+
}
|
|
1169
|
+
return this;
|
|
1170
|
+
},
|
|
1171
|
+
delete(this: any, key: any): boolean {
|
|
1172
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1173
|
+
const oldData = target.get(key);
|
|
1174
|
+
const result: boolean = target.delete(key);
|
|
1175
|
+
if (result) {
|
|
1176
|
+
emit(target, key, undefined, oldData);
|
|
1177
|
+
emit(target, MAP_SIZE_SYMBOL, target.size, target.size + 1);
|
|
1178
|
+
runImmediateQueue();
|
|
1179
|
+
}
|
|
1180
|
+
return result;
|
|
1181
|
+
},
|
|
1182
|
+
clear(this: any): void {
|
|
1183
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1184
|
+
const oldSize = target.size;
|
|
1185
|
+
for (const key of target.keys()) {
|
|
1186
|
+
const oldData = target.get(key);
|
|
1187
|
+
emit(target, key, undefined, oldData);
|
|
1188
|
+
}
|
|
1189
|
+
target.clear();
|
|
1190
|
+
emit(target, MAP_SIZE_SYMBOL, 0, oldSize);
|
|
1191
|
+
runImmediateQueue();
|
|
1192
|
+
},
|
|
1193
|
+
has(this: any, key: any): boolean {
|
|
1194
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1195
|
+
subscribe(target, key);
|
|
1196
|
+
return target.has(key);
|
|
1197
|
+
},
|
|
1198
|
+
keys(this: any): IterableIterator<any> {
|
|
1199
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1200
|
+
subscribe(target, ANY_SYMBOL);
|
|
1201
|
+
return target.keys();
|
|
1202
|
+
},
|
|
1203
|
+
values(this: any): IterableIterator<any> {
|
|
1204
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1205
|
+
subscribe(target, ANY_SYMBOL);
|
|
1206
|
+
return target.values();
|
|
1207
|
+
},
|
|
1208
|
+
entries(this: any): IterableIterator<[any, any]> {
|
|
1209
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1210
|
+
subscribe(target, ANY_SYMBOL);
|
|
1211
|
+
return target.entries();
|
|
1212
|
+
},
|
|
1213
|
+
[Symbol.iterator](this: any): IterableIterator<[any, any]> {
|
|
1214
|
+
const target: Map<any, any> = this[TARGET_SYMBOL];
|
|
1215
|
+
subscribe(target, ANY_SYMBOL);
|
|
1216
|
+
return target[Symbol.iterator]();
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
const mapHandler: ProxyHandler<Map<any, any>> = {
|
|
1221
|
+
get(target: Map<any, any>, prop: any) {
|
|
1222
|
+
if (prop === TARGET_SYMBOL) return target;
|
|
1223
|
+
|
|
1224
|
+
// Handle Map methods using lookup object
|
|
1225
|
+
if (prop in mapMethodHandlers) {
|
|
1226
|
+
return (mapMethodHandlers as any)[prop];
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// Handle size property
|
|
1230
|
+
if (prop === "size") {
|
|
1231
|
+
subscribe(target, MAP_SIZE_SYMBOL);
|
|
1232
|
+
return target.size;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// Handle other properties normally
|
|
1236
|
+
subscribe(target, prop);
|
|
1237
|
+
return optProxy((target as any)[prop]);
|
|
1238
|
+
},
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1130
1241
|
const proxyMap = new WeakMap<TargetType, /*Proxy*/ TargetType>();
|
|
1131
1242
|
|
|
1132
1243
|
function optProxy(value: any): any {
|
|
@@ -1141,15 +1252,21 @@ function optProxy(value: any): any {
|
|
|
1141
1252
|
let proxied = proxyMap.get(value);
|
|
1142
1253
|
if (proxied) return proxied; // Only one proxy per target!
|
|
1143
1254
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
)
|
|
1255
|
+
let handler;
|
|
1256
|
+
if (value instanceof Array) {
|
|
1257
|
+
handler = arrayHandler;
|
|
1258
|
+
} else if (value instanceof Map) {
|
|
1259
|
+
handler = mapHandler;
|
|
1260
|
+
} else {
|
|
1261
|
+
handler = objectHandler;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
proxied = new Proxy(value, handler);
|
|
1148
1265
|
proxyMap.set(value, proxied as TargetType);
|
|
1149
1266
|
return proxied;
|
|
1150
1267
|
}
|
|
1151
1268
|
|
|
1152
|
-
export function proxy<T extends
|
|
1269
|
+
export function proxy<T extends any>(
|
|
1153
1270
|
target: Array<T>,
|
|
1154
1271
|
): Array<
|
|
1155
1272
|
T extends number
|
|
@@ -1161,7 +1278,7 @@ export function proxy<T extends DatumType>(
|
|
|
1161
1278
|
: T
|
|
1162
1279
|
>;
|
|
1163
1280
|
export function proxy<T extends object>(target: T): T;
|
|
1164
|
-
export function proxy<T extends
|
|
1281
|
+
export function proxy<T extends any>(
|
|
1165
1282
|
target: T,
|
|
1166
1283
|
): ValueRef<
|
|
1167
1284
|
T extends number
|
|
@@ -1288,14 +1405,17 @@ function destroyWithClass(element: Element, cls: string) {
|
|
|
1288
1405
|
* will recursively copy properties into the existing `dst` object instead of replacing it.
|
|
1289
1406
|
* This minimizes change notifications for reactive updates.
|
|
1290
1407
|
* - **Handles Proxies:** Can accept proxied or unproxied objects/arrays for both `dst` and `src`.
|
|
1408
|
+
* - **Cross-Type Copying:** Supports copying between Maps and objects. When copying from an object
|
|
1409
|
+
* to a Map, object properties become Map entries. When copying from a Map to an object, Map entries
|
|
1410
|
+
* become object properties (only for Maps with string/number/symbol keys).
|
|
1291
1411
|
*
|
|
1292
|
-
* @param dst - The destination object/array (proxied or unproxied).
|
|
1293
|
-
* @param src - The source object/array (proxied or unproxied). It won't be modified.
|
|
1412
|
+
* @param dst - The destination object/array/Map (proxied or unproxied).
|
|
1413
|
+
* @param src - The source object/array/Map (proxied or unproxied). It won't be modified.
|
|
1294
1414
|
* @param flags - Bitmask controlling copy behavior:
|
|
1295
1415
|
* - {@link MERGE}: Performs a partial update. Properties in `dst` not present in `src` are kept.
|
|
1296
1416
|
* `null`/`undefined` in `src` delete properties in `dst`. Handles partial array updates via object keys.
|
|
1297
1417
|
* - {@link SHALLOW}: Performs a shallow copy; when an array/object of the right type doesn't exist in `dst` yet, a reference to the array/object in `src` will be made, instead of creating a copy. If the array/object already exists, it won't be replaced (by a reference), but all items will be individually checked and copied like normal, keeping changes (and therefore UI updates) to a minimum.
|
|
1298
|
-
* @template T - The type of the
|
|
1418
|
+
* @template T - The type of the destination object.
|
|
1299
1419
|
* @throws Error if attempting to copy an array into a non-array or vice versa (unless {@link MERGE} is set, allowing for sparse array updates).
|
|
1300
1420
|
*
|
|
1301
1421
|
* @example Basic Copy
|
|
@@ -1306,6 +1426,22 @@ function destroyWithClass(element: Element, cls: string) {
|
|
|
1306
1426
|
* console.log(dest); // proxy({ a: 1, b: { c: 2 } })
|
|
1307
1427
|
* ```
|
|
1308
1428
|
*
|
|
1429
|
+
* @example Map to Object
|
|
1430
|
+
* ```typescript
|
|
1431
|
+
* const source = new Map([['x', 3], ['y', 4]]);
|
|
1432
|
+
* const dest = proxy({});
|
|
1433
|
+
* copy(dest, source);
|
|
1434
|
+
* console.log(dest); // proxy({ x: 3, y: 4 })
|
|
1435
|
+
* ```
|
|
1436
|
+
*
|
|
1437
|
+
* @example Object to Map
|
|
1438
|
+
* ```typescript
|
|
1439
|
+
* const source = { x: 3, y: 4 };
|
|
1440
|
+
* const dest = proxy(new Map());
|
|
1441
|
+
* copy(dest, source);
|
|
1442
|
+
* console.log(dest); // proxy(Map([['x', 3], ['y', 4]]))
|
|
1443
|
+
* ```
|
|
1444
|
+
*
|
|
1309
1445
|
* @example MERGE
|
|
1310
1446
|
* ```typescript
|
|
1311
1447
|
* const source = { b: { c: 99 }, d: undefined }; // d: undefined will delete
|
|
@@ -1331,7 +1467,17 @@ function destroyWithClass(element: Element, cls: string) {
|
|
|
1331
1467
|
* console.log(source.nested); // [1, 2, 3] (source was modified)
|
|
1332
1468
|
* ```
|
|
1333
1469
|
*/
|
|
1334
|
-
|
|
1470
|
+
|
|
1471
|
+
// Overload for Map destination with object source
|
|
1472
|
+
export function copy<K, V>(dst: Map<K, V>, src: Record<K extends string | number | symbol ? K : never, V> | Partial<Record<K extends string | number | symbol ? K : never, V>>, flags?: number): void;
|
|
1473
|
+
// Overload for Map destination with Map source
|
|
1474
|
+
export function copy<K, V>(dst: Map<K, V>, src: Map<K, V> | Partial<Map<K, V>>, flags?: number): void;
|
|
1475
|
+
// Overload for object destination with Map source
|
|
1476
|
+
export function copy<T extends Record<string | number | symbol, any>>(dst: T, src: Map<keyof T, T[keyof T]>, flags?: number): void;
|
|
1477
|
+
// Overload for same-type copying
|
|
1478
|
+
export function copy<T extends object>(dst: T, src: Partial<T>, flags?: number): void;
|
|
1479
|
+
// Implementation
|
|
1480
|
+
export function copy(dst: any, src: any, flags = 0) {
|
|
1335
1481
|
copyRecurse(dst, src, flags);
|
|
1336
1482
|
runImmediateQueue();
|
|
1337
1483
|
}
|
|
@@ -1352,11 +1498,24 @@ const COPY_EMIT = 64;
|
|
|
1352
1498
|
* @returns A new unproxied array or object (of the same type as `src`), containing a deep (by default) copy of `src`.
|
|
1353
1499
|
*/
|
|
1354
1500
|
export function clone<T extends object>(src: T, flags = 0): T {
|
|
1355
|
-
|
|
1501
|
+
let dst: T;
|
|
1502
|
+
if (src instanceof Map) {
|
|
1503
|
+
dst = new Map() as T;
|
|
1504
|
+
} else {
|
|
1505
|
+
dst = Object.create(Object.getPrototypeOf(src)) as T;
|
|
1506
|
+
}
|
|
1356
1507
|
copyRecurse(dst, src, flags);
|
|
1357
1508
|
return dst;
|
|
1358
1509
|
}
|
|
1359
1510
|
|
|
1511
|
+
function getEntries(subject: any) {
|
|
1512
|
+
return (subject instanceof Map) ? subject.entries() : Object.entries(subject);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
function getKeys(subject: any) {
|
|
1516
|
+
return (subject instanceof Map) ? subject.keys() : Object.keys(subject);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1360
1519
|
function copyRecurse(dst: any, src: any, flags: number) {
|
|
1361
1520
|
// We never want to subscribe to reads we do to the target (to find changes). So we'll
|
|
1362
1521
|
// take the unproxied version and `emit` updates ourselve.
|
|
@@ -1380,9 +1539,9 @@ function copyRecurse(dst: any, src: any, flags: number) {
|
|
|
1380
1539
|
const dstLen = dst.length;
|
|
1381
1540
|
const srcLen = src.length;
|
|
1382
1541
|
for (let i = 0; i < srcLen; i++) {
|
|
1383
|
-
copyValue(dst,
|
|
1542
|
+
copyValue(dst, i, src[i], flags);
|
|
1384
1543
|
}
|
|
1385
|
-
// Leaving additional values in the old array doesn't make sense
|
|
1544
|
+
// Leaving additional values in the old array doesn't make sense, so we'll do this even when MERGE is set:
|
|
1386
1545
|
if (srcLen !== dstLen) {
|
|
1387
1546
|
if (flags & COPY_EMIT) {
|
|
1388
1547
|
for (let i = srcLen; i < dstLen; i++) {
|
|
@@ -1397,16 +1556,23 @@ function copyRecurse(dst: any, src: any, flags: number) {
|
|
|
1397
1556
|
}
|
|
1398
1557
|
}
|
|
1399
1558
|
} else {
|
|
1400
|
-
|
|
1401
|
-
|
|
1559
|
+
// Copy all entries from src to dst (both of which can be Map or object)
|
|
1560
|
+
for (const [key, value] of getEntries(src)) {
|
|
1561
|
+
copyValue(dst, key, value, flags);
|
|
1402
1562
|
}
|
|
1563
|
+
|
|
1564
|
+
// Remove entries from dst that don't exist in src (unless MERGE flag is set)
|
|
1403
1565
|
if (!(flags & MERGE)) {
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1566
|
+
if (src instanceof Map) {
|
|
1567
|
+
for (const key of getKeys(dst)) {
|
|
1568
|
+
if (!src.has(key)) {
|
|
1569
|
+
deleteKey(dst, key, flags);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
} else {
|
|
1573
|
+
for (const key of getKeys(dst)) {
|
|
1574
|
+
if (!(key in src)) {
|
|
1575
|
+
deleteKey(dst, key, flags);
|
|
1410
1576
|
}
|
|
1411
1577
|
}
|
|
1412
1578
|
}
|
|
@@ -1414,17 +1580,26 @@ function copyRecurse(dst: any, src: any, flags: number) {
|
|
|
1414
1580
|
}
|
|
1415
1581
|
}
|
|
1416
1582
|
|
|
1417
|
-
function
|
|
1418
|
-
|
|
1419
|
-
|
|
1583
|
+
function deleteKey(dst: any, key: any, flags: number) {
|
|
1584
|
+
let old;
|
|
1585
|
+
if (dst instanceof Map) {
|
|
1586
|
+
old = dst.get(key);
|
|
1587
|
+
dst.delete(key);
|
|
1588
|
+
} else {
|
|
1589
|
+
old = dst[key];
|
|
1590
|
+
delete dst[key];
|
|
1591
|
+
}
|
|
1592
|
+
if (flags & COPY_EMIT && old !== undefined) {
|
|
1593
|
+
emit(dst, key, undefined, old);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
function copyValue(dst: any, index: any, srcValue: any, flags: number) {
|
|
1598
|
+
const dstValue = dst instanceof Map ? dst.get(index) : dst[index];
|
|
1420
1599
|
if (srcValue !== dstValue) {
|
|
1421
1600
|
if (
|
|
1422
|
-
srcValue &&
|
|
1423
|
-
dstValue &&
|
|
1424
|
-
typeof srcValue === "object" &&
|
|
1425
|
-
typeof dstValue === "object" &&
|
|
1426
|
-
(srcValue.constructor === dstValue.constructor ||
|
|
1427
|
-
(flags & MERGE && dstValue instanceof Array))
|
|
1601
|
+
srcValue && dstValue && typeof srcValue === "object" && typeof dstValue === "object" &&
|
|
1602
|
+
(srcValue.constructor === dstValue.constructor || (flags & MERGE && dstValue instanceof Array))
|
|
1428
1603
|
) {
|
|
1429
1604
|
copyRecurse(dstValue, srcValue, flags);
|
|
1430
1605
|
return;
|
|
@@ -1438,10 +1613,14 @@ function copyValue(dst: any, src: any, index: any, flags: number) {
|
|
|
1438
1613
|
copyRecurse(copy, srcValue, 0);
|
|
1439
1614
|
srcValue = copy;
|
|
1440
1615
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1616
|
+
if (dst instanceof Map) {
|
|
1617
|
+
if (flags & MERGE && srcValue == null) dst.delete(index);
|
|
1618
|
+
else dst.set(index, srcValue);
|
|
1619
|
+
} else {
|
|
1620
|
+
if (flags & MERGE && srcValue == null) delete dst[index];
|
|
1621
|
+
else dst[index] = srcValue;
|
|
1622
|
+
}
|
|
1623
|
+
if (flags & COPY_EMIT) emit(dst, index, srcValue, dstValue);
|
|
1445
1624
|
}
|
|
1446
1625
|
}
|
|
1447
1626
|
|
|
@@ -2224,6 +2403,11 @@ export function peek<T>(func: () => T): T {
|
|
|
2224
2403
|
}
|
|
2225
2404
|
}
|
|
2226
2405
|
|
|
2406
|
+
/** When using a Map as `source`. */
|
|
2407
|
+
export function map<K, IN, OUT>(
|
|
2408
|
+
source: Map<K, IN>,
|
|
2409
|
+
func: (value: IN, key: K) => undefined | OUT,
|
|
2410
|
+
): Map<K, OUT>;
|
|
2227
2411
|
/** When using an object as `source`. */
|
|
2228
2412
|
export function map<IN, const IN_KEY extends string | number | symbol, OUT>(
|
|
2229
2413
|
source: Record<IN_KEY, IN>,
|
|
@@ -2279,23 +2463,38 @@ export function map<IN, OUT>(
|
|
|
2279
2463
|
*/
|
|
2280
2464
|
export function map(
|
|
2281
2465
|
source: any,
|
|
2282
|
-
func: (value:
|
|
2466
|
+
func: (value: any, key: any) => any,
|
|
2283
2467
|
): any {
|
|
2284
|
-
|
|
2285
|
-
|
|
2468
|
+
let out;
|
|
2469
|
+
if (source instanceof Array) {
|
|
2470
|
+
out = optProxy([]);
|
|
2471
|
+
} else if (source instanceof Map) {
|
|
2472
|
+
out = optProxy(new Map());
|
|
2473
|
+
} else {
|
|
2474
|
+
out = optProxy({});
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
onEach(source, (item: any, key: symbol | string | number) => {
|
|
2286
2478
|
const value = func(item, key);
|
|
2287
2479
|
if (value !== undefined) {
|
|
2288
|
-
out
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2480
|
+
if (out instanceof Map) {
|
|
2481
|
+
out.set(key, value);
|
|
2482
|
+
clean(() => {
|
|
2483
|
+
out.delete(key);
|
|
2484
|
+
});
|
|
2485
|
+
} else {
|
|
2486
|
+
out[key] = value;
|
|
2487
|
+
clean(() => {
|
|
2488
|
+
delete out[key];
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2292
2491
|
}
|
|
2293
2492
|
});
|
|
2294
2493
|
return out;
|
|
2295
2494
|
}
|
|
2296
2495
|
|
|
2297
2496
|
/** When using an array as `source`. */
|
|
2298
|
-
export function multiMap<IN, OUT extends { [key: string | symbol]:
|
|
2497
|
+
export function multiMap<IN, OUT extends { [key: string | symbol]: any }>(
|
|
2299
2498
|
source: Array<IN>,
|
|
2300
2499
|
func: (value: IN, index: number) => OUT | undefined,
|
|
2301
2500
|
): OUT;
|
|
@@ -2303,8 +2502,14 @@ export function multiMap<IN, OUT extends { [key: string | symbol]: DatumType }>(
|
|
|
2303
2502
|
export function multiMap<
|
|
2304
2503
|
K extends string | number | symbol,
|
|
2305
2504
|
IN,
|
|
2306
|
-
OUT extends { [key: string | symbol]:
|
|
2505
|
+
OUT extends { [key: string | symbol]: any },
|
|
2307
2506
|
>(source: Record<K, IN>, func: (value: IN, index: KeyToString<K>) => OUT | undefined): OUT;
|
|
2507
|
+
/** When using a Map as `source`. */
|
|
2508
|
+
export function multiMap<
|
|
2509
|
+
K,
|
|
2510
|
+
IN,
|
|
2511
|
+
OUT extends { [key: string | symbol]: any },
|
|
2512
|
+
>(source: Map<K, IN>, func: (value: IN, key: K) => OUT | undefined): OUT;
|
|
2308
2513
|
/**
|
|
2309
2514
|
* Reactively maps items from a source proxy (array or object) to a target proxied object,
|
|
2310
2515
|
* where each source item can contribute multiple key-value pairs to the target.
|
|
@@ -2348,10 +2553,10 @@ export function multiMap<
|
|
|
2348
2553
|
*/
|
|
2349
2554
|
export function multiMap(
|
|
2350
2555
|
source: any,
|
|
2351
|
-
func: (value:
|
|
2556
|
+
func: (value: any, key: any) => Record<string | symbol, any>,
|
|
2352
2557
|
): any {
|
|
2353
2558
|
const out = optProxy({});
|
|
2354
|
-
onEach(source, (item:
|
|
2559
|
+
onEach(source, (item: any, key: symbol | string | number) => {
|
|
2355
2560
|
const pairs = func(item, key);
|
|
2356
2561
|
if (pairs) {
|
|
2357
2562
|
for (const key in pairs) out[key] = pairs[key];
|
|
@@ -2377,6 +2582,15 @@ export function partition<
|
|
|
2377
2582
|
source: Record<IN_K, IN_V>,
|
|
2378
2583
|
func: (value: IN_V, key: IN_K) => undefined | OUT_K | OUT_K[],
|
|
2379
2584
|
): Record<OUT_K, Record<IN_K, IN_V>>;
|
|
2585
|
+
/** When using a Map as `source`. */
|
|
2586
|
+
export function partition<
|
|
2587
|
+
IN_K extends string | number | symbol,
|
|
2588
|
+
OUT_K extends string | number | symbol,
|
|
2589
|
+
IN_V,
|
|
2590
|
+
>(
|
|
2591
|
+
source: Map<IN_K, IN_V>,
|
|
2592
|
+
func: (value: IN_V, key: IN_K) => undefined | OUT_K | OUT_K[],
|
|
2593
|
+
): Record<OUT_K, Record<IN_K, IN_V>>;
|
|
2380
2594
|
|
|
2381
2595
|
/**
|
|
2382
2596
|
* @overload
|
|
@@ -2505,7 +2719,15 @@ export function partition<
|
|
|
2505
2719
|
*/
|
|
2506
2720
|
export function dump<T>(data: T): T {
|
|
2507
2721
|
if (data && typeof data === "object") {
|
|
2508
|
-
|
|
2722
|
+
let label;
|
|
2723
|
+
if (data instanceof Array) {
|
|
2724
|
+
label = "<array>";
|
|
2725
|
+
} else if (data instanceof Map) {
|
|
2726
|
+
label = "<Map>";
|
|
2727
|
+
} else {
|
|
2728
|
+
label = "<object>";
|
|
2729
|
+
}
|
|
2730
|
+
$({ text: label });
|
|
2509
2731
|
$("ul", () => {
|
|
2510
2732
|
onEach(data as any, (value, key) => {
|
|
2511
2733
|
$(`li:${JSON.stringify(key)}: `, () => {
|
|
@@ -2549,8 +2771,8 @@ export function withEmitHandler(
|
|
|
2549
2771
|
handler: (
|
|
2550
2772
|
target: TargetType,
|
|
2551
2773
|
index: any,
|
|
2552
|
-
newData:
|
|
2553
|
-
oldData:
|
|
2774
|
+
newData: any,
|
|
2775
|
+
oldData: any,
|
|
2554
2776
|
) => void,
|
|
2555
2777
|
func: () => void,
|
|
2556
2778
|
) {
|