wunderbaum 0.13.0 → 0.14.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/LICENSE +1 -1
- package/dist/wunderbaum.css +31 -11
- package/dist/wunderbaum.css.map +1 -1
- package/dist/wunderbaum.d.ts +535 -296
- package/dist/wunderbaum.esm.js +619 -221
- package/dist/wunderbaum.esm.min.js +28 -28
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +619 -221
- package/dist/wunderbaum.umd.min.js +32 -32
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +3 -2
- package/src/common.ts +23 -4
- package/src/types.ts +86 -40
- package/src/util.ts +75 -2
- package/src/wb_ext_dnd.ts +21 -20
- package/src/wb_ext_edit.ts +1 -1
- package/src/wb_ext_filter.ts +6 -2
- package/src/wb_ext_grid.ts +1 -1
- package/src/wb_extension_base.ts +16 -1
- package/src/wb_node.ts +222 -106
- package/src/wb_options.ts +169 -108
- package/src/wunderbaum.ts +351 -127
package/dist/wunderbaum.umd.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/*!
|
|
8
8
|
* Wunderbaum - debounce.ts
|
|
9
9
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
10
|
-
* v0.
|
|
10
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
11
11
|
*/
|
|
12
12
|
/*
|
|
13
13
|
* debounce & throttle, taken from https://github.com/lodash/lodash v4.17.21
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
/*!
|
|
300
300
|
* Wunderbaum - util
|
|
301
301
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
302
|
-
* v0.
|
|
302
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
303
303
|
*/
|
|
304
304
|
/** @module util */
|
|
305
305
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -449,7 +449,7 @@
|
|
|
449
449
|
}
|
|
450
450
|
return obj;
|
|
451
451
|
}
|
|
452
|
-
/** Shortcut for `throw new Error(msg)
|
|
452
|
+
/** Shortcut for `throw new Error(msg)`. */
|
|
453
453
|
function error(msg) {
|
|
454
454
|
throw new Error(msg);
|
|
455
455
|
}
|
|
@@ -964,6 +964,10 @@
|
|
|
964
964
|
}
|
|
965
965
|
throw new Error(`Expected a string like '123px': ${defaults}`);
|
|
966
966
|
}
|
|
967
|
+
/** Cast any value to <T>. */
|
|
968
|
+
function unsafeCast(value) {
|
|
969
|
+
return value;
|
|
970
|
+
}
|
|
967
971
|
/** Return the the boolean value of the first non-null element.
|
|
968
972
|
* Example:
|
|
969
973
|
* ```js
|
|
@@ -1037,7 +1041,7 @@
|
|
|
1037
1041
|
const throttledFn = (...args) => {
|
|
1038
1042
|
if (waiting) {
|
|
1039
1043
|
pendingArgs = args;
|
|
1040
|
-
// console.log(`adaptiveThrottle()
|
|
1044
|
+
// console.log(`adaptiveThrottle() queueing request #${waiting}...`, args);
|
|
1041
1045
|
waiting += 1;
|
|
1042
1046
|
}
|
|
1043
1047
|
else {
|
|
@@ -1092,6 +1096,60 @@
|
|
|
1092
1096
|
};
|
|
1093
1097
|
return throttledFn;
|
|
1094
1098
|
}
|
|
1099
|
+
/**
|
|
1100
|
+
* MurmurHash3 implementation for strings.
|
|
1101
|
+
* @param key The input string to hash.
|
|
1102
|
+
* @param asString Optional convert result to zero-padded string of 8 characters.
|
|
1103
|
+
* @param seed Optional seed value.
|
|
1104
|
+
* @returns A 32-bit hash as a number or string.
|
|
1105
|
+
*/
|
|
1106
|
+
function murmurHash3(key, asString = true, seed = 0) {
|
|
1107
|
+
let h1 = seed;
|
|
1108
|
+
const remainder = key.length & 3; // key.length % 4
|
|
1109
|
+
const bytes = key.length - remainder;
|
|
1110
|
+
const c1 = 0xcc9e2d51;
|
|
1111
|
+
const c2 = 0x1b873593;
|
|
1112
|
+
let i = 0;
|
|
1113
|
+
while (i < bytes) {
|
|
1114
|
+
let k1 = (key.charCodeAt(i) & 0xff) |
|
|
1115
|
+
((key.charCodeAt(++i) & 0xff) << 8) |
|
|
1116
|
+
((key.charCodeAt(++i) & 0xff) << 16) |
|
|
1117
|
+
((key.charCodeAt(++i) & 0xff) << 24);
|
|
1118
|
+
++i;
|
|
1119
|
+
k1 = Math.imul(k1, c1);
|
|
1120
|
+
k1 = (k1 << 15) | (k1 >>> 17);
|
|
1121
|
+
k1 = Math.imul(k1, c2);
|
|
1122
|
+
h1 ^= k1;
|
|
1123
|
+
h1 = (h1 << 13) | (h1 >>> 19);
|
|
1124
|
+
h1 = Math.imul(h1, 5) + 0xe6546b64;
|
|
1125
|
+
}
|
|
1126
|
+
let k1 = 0;
|
|
1127
|
+
switch (remainder) {
|
|
1128
|
+
case 3:
|
|
1129
|
+
k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
|
|
1130
|
+
// fall through
|
|
1131
|
+
case 2:
|
|
1132
|
+
k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
|
|
1133
|
+
// fall through
|
|
1134
|
+
case 1:
|
|
1135
|
+
k1 ^= key.charCodeAt(i) & 0xff;
|
|
1136
|
+
k1 = Math.imul(k1, c1);
|
|
1137
|
+
k1 = (k1 << 15) | (k1 >>> 17);
|
|
1138
|
+
k1 = Math.imul(k1, c2);
|
|
1139
|
+
h1 ^= k1;
|
|
1140
|
+
}
|
|
1141
|
+
h1 ^= key.length;
|
|
1142
|
+
h1 ^= h1 >>> 16;
|
|
1143
|
+
h1 = Math.imul(h1, 0x85ebca6b);
|
|
1144
|
+
h1 ^= h1 >>> 13;
|
|
1145
|
+
h1 = Math.imul(h1, 0xc2b2ae35);
|
|
1146
|
+
h1 ^= h1 >>> 16;
|
|
1147
|
+
if (asString) {
|
|
1148
|
+
// Convert to 8 digit hex string
|
|
1149
|
+
return (h1 >>> 0).toString(16).padStart(8, "0");
|
|
1150
|
+
}
|
|
1151
|
+
return h1 >>> 0; // Convert to unsigned 32-bit integer
|
|
1152
|
+
}
|
|
1095
1153
|
|
|
1096
1154
|
var util = /*#__PURE__*/Object.freeze({
|
|
1097
1155
|
__proto__: null,
|
|
@@ -1122,6 +1180,7 @@
|
|
|
1122
1180
|
isFunction: isFunction,
|
|
1123
1181
|
isMac: isMac,
|
|
1124
1182
|
isPlainObject: isPlainObject,
|
|
1183
|
+
murmurHash3: murmurHash3,
|
|
1125
1184
|
noop: noop,
|
|
1126
1185
|
onEvent: onEvent,
|
|
1127
1186
|
overrideMethod: overrideMethod,
|
|
@@ -1135,13 +1194,14 @@
|
|
|
1135
1194
|
toPixel: toPixel,
|
|
1136
1195
|
toSet: toSet,
|
|
1137
1196
|
toggleCheckbox: toggleCheckbox,
|
|
1138
|
-
type: type
|
|
1197
|
+
type: type,
|
|
1198
|
+
unsafeCast: unsafeCast
|
|
1139
1199
|
});
|
|
1140
1200
|
|
|
1141
1201
|
/*!
|
|
1142
1202
|
* Wunderbaum - types
|
|
1143
1203
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1144
|
-
* v0.
|
|
1204
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1145
1205
|
*/
|
|
1146
1206
|
/**
|
|
1147
1207
|
* Possible values for {@link WunderbaumNode.update} and {@link Wunderbaum.update}.
|
|
@@ -1209,7 +1269,7 @@
|
|
|
1209
1269
|
/*!
|
|
1210
1270
|
* Wunderbaum - wb_extension_base
|
|
1211
1271
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1212
|
-
* v0.
|
|
1272
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1213
1273
|
*/
|
|
1214
1274
|
class WunderbaumExtension {
|
|
1215
1275
|
constructor(tree, id, defaults) {
|
|
@@ -1268,7 +1328,7 @@
|
|
|
1268
1328
|
/*!
|
|
1269
1329
|
* Wunderbaum - ext-filter
|
|
1270
1330
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1271
|
-
* v0.
|
|
1331
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1272
1332
|
*/
|
|
1273
1333
|
const START_MARKER = "\uFFF7";
|
|
1274
1334
|
const END_MARKER = "\uFFF8";
|
|
@@ -1556,6 +1616,10 @@
|
|
|
1556
1616
|
*/
|
|
1557
1617
|
filterBranches(filter, options) {
|
|
1558
1618
|
assert(options.matchBranch === undefined, "filterBranches() is deprecated.");
|
|
1619
|
+
this.tree.logDeprecate("filterBranches()", {
|
|
1620
|
+
since: "0.9.0",
|
|
1621
|
+
hint: "Use `filterNodes` instead and set `options.matchBranch: true`",
|
|
1622
|
+
});
|
|
1559
1623
|
options.matchBranch = true;
|
|
1560
1624
|
return this._applyFilterNoUpdate(filter, options);
|
|
1561
1625
|
}
|
|
@@ -1620,7 +1684,7 @@
|
|
|
1620
1684
|
}
|
|
1621
1685
|
}
|
|
1622
1686
|
/**
|
|
1623
|
-
* @description Marks the matching
|
|
1687
|
+
* @description Marks the matching characters of `text` either by `mark` or
|
|
1624
1688
|
* by exotic*Chars (if `escapeTitles` is `true`) based on `matches`
|
|
1625
1689
|
* which is an array of matching groups.
|
|
1626
1690
|
* @param {string} text
|
|
@@ -1659,7 +1723,7 @@
|
|
|
1659
1723
|
/*!
|
|
1660
1724
|
* Wunderbaum - ext-keynav
|
|
1661
1725
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1662
|
-
* v0.
|
|
1726
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1663
1727
|
*/
|
|
1664
1728
|
const QUICKSEARCH_DELAY = 500;
|
|
1665
1729
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -2023,7 +2087,7 @@
|
|
|
2023
2087
|
/*!
|
|
2024
2088
|
* Wunderbaum - ext-logger
|
|
2025
2089
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2026
|
-
* v0.
|
|
2090
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2027
2091
|
*/
|
|
2028
2092
|
class LoggerExtension extends WunderbaumExtension {
|
|
2029
2093
|
constructor(tree) {
|
|
@@ -2065,7 +2129,7 @@
|
|
|
2065
2129
|
/*!
|
|
2066
2130
|
* Wunderbaum - ext-dnd
|
|
2067
2131
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2068
|
-
* v0.
|
|
2132
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2069
2133
|
*/
|
|
2070
2134
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2071
2135
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -2301,7 +2365,6 @@
|
|
|
2301
2365
|
*/
|
|
2302
2366
|
onDragEvent(e) {
|
|
2303
2367
|
var _a;
|
|
2304
|
-
// const tree = this.tree;
|
|
2305
2368
|
const dndOpts = this.treeOpts.dnd;
|
|
2306
2369
|
const srcNode = Wunderbaum.getNode(e);
|
|
2307
2370
|
if (!srcNode) {
|
|
@@ -2327,7 +2390,7 @@
|
|
|
2327
2390
|
return false;
|
|
2328
2391
|
}
|
|
2329
2392
|
const nodeData = srcNode.toDict(true, (n) => {
|
|
2330
|
-
// We don't want to
|
|
2393
|
+
// We don't want to reuse the key on drop:
|
|
2331
2394
|
n._orgKey = n.key;
|
|
2332
2395
|
delete n.key;
|
|
2333
2396
|
});
|
|
@@ -2389,6 +2452,7 @@
|
|
|
2389
2452
|
};
|
|
2390
2453
|
if (!targetNode) {
|
|
2391
2454
|
this._leaveNode();
|
|
2455
|
+
e.preventDefault(); // Don't open file in browser when dropped in empty area
|
|
2392
2456
|
return;
|
|
2393
2457
|
}
|
|
2394
2458
|
if (["drop"].includes(e.type)) {
|
|
@@ -2494,19 +2558,20 @@
|
|
|
2494
2558
|
nodeData = nodeData ? JSON.parse(nodeData) : null;
|
|
2495
2559
|
const srcNode = this.srcNode;
|
|
2496
2560
|
const lastDropEffect = this.lastDropEffect;
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2561
|
+
/* Before v0.14.0, we decoupled `_callEvent` like so:
|
|
2562
|
+
Decouple this call, because drop actions may prevent the dragend
|
|
2563
|
+
event from being fired on some browsers.
|
|
2564
|
+
setTimeout(() => {...}, 10);
|
|
2565
|
+
however this made e.dataTransfer.items inaccessible */
|
|
2566
|
+
targetNode._callEvent("dnd.drop", {
|
|
2567
|
+
event: e,
|
|
2568
|
+
region: region,
|
|
2569
|
+
suggestedDropMode: region === "over" ? "appendChild" : region,
|
|
2570
|
+
suggestedDropEffect: lastDropEffect,
|
|
2571
|
+
sourceNode: srcNode,
|
|
2572
|
+
sourceNodeData: nodeData,
|
|
2573
|
+
dataTransfer: e.dataTransfer,
|
|
2574
|
+
});
|
|
2510
2575
|
}
|
|
2511
2576
|
return false;
|
|
2512
2577
|
}
|
|
@@ -2515,7 +2580,7 @@
|
|
|
2515
2580
|
/*!
|
|
2516
2581
|
* Wunderbaum - drag_observer
|
|
2517
2582
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2518
|
-
* v0.
|
|
2583
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2519
2584
|
*/
|
|
2520
2585
|
/**
|
|
2521
2586
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2664,7 +2729,7 @@
|
|
|
2664
2729
|
/*!
|
|
2665
2730
|
* Wunderbaum - common
|
|
2666
2731
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2667
|
-
* v0.
|
|
2732
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2668
2733
|
*/
|
|
2669
2734
|
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
2670
2735
|
/**
|
|
@@ -2684,12 +2749,18 @@
|
|
|
2684
2749
|
const RENDER_MAX_PREFETCH = 5;
|
|
2685
2750
|
/** Minimum column width if not set otherwise. */
|
|
2686
2751
|
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2752
|
+
/**
|
|
2753
|
+
* A value for `node.type` that by convention may be used to mark a node as directory.
|
|
2754
|
+
* It may be used to sort 'directories' to the top.
|
|
2755
|
+
*/
|
|
2756
|
+
const NODE_TYPE_FOLDER = "folder";
|
|
2687
2757
|
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2688
2758
|
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2759
|
+
* `<` is ignored, because it is probably an html tag.
|
|
2689
2760
|
*/
|
|
2690
|
-
const
|
|
2691
|
-
|
|
2692
|
-
|
|
2761
|
+
const TEST_FILE_PATH = /^(?!.*<).*[/.]/;
|
|
2762
|
+
/** Regular expression to detect if a string describes an HTML element. */
|
|
2763
|
+
const TEST_HTML = /</;
|
|
2693
2764
|
/**
|
|
2694
2765
|
* Default node icons for icon libraries
|
|
2695
2766
|
*
|
|
@@ -2697,7 +2768,7 @@
|
|
|
2697
2768
|
* - 'fontawesome6' {@link https://fontawesome.com/icons}
|
|
2698
2769
|
*
|
|
2699
2770
|
*/
|
|
2700
|
-
const
|
|
2771
|
+
const defaultIconMaps = {
|
|
2701
2772
|
bootstrap: {
|
|
2702
2773
|
error: "bi bi-exclamation-triangle",
|
|
2703
2774
|
// loading: "bi bi-hourglass-split wb-busy",
|
|
@@ -2745,7 +2816,7 @@
|
|
|
2745
2816
|
radioChecked: "fa-solid fa-circle",
|
|
2746
2817
|
radioUnchecked: "fa-regular fa-circle",
|
|
2747
2818
|
radioUnknown: "fa-regular fa-circle-question",
|
|
2748
|
-
folder: "fa-
|
|
2819
|
+
folder: "fa-regular fa-folder-closed",
|
|
2749
2820
|
folderOpen: "fa-regular fa-folder-open",
|
|
2750
2821
|
folderLazy: "fa-solid fa-folder-plus",
|
|
2751
2822
|
doc: "fa-regular fa-file",
|
|
@@ -2818,12 +2889,20 @@
|
|
|
2818
2889
|
return reMatch.test(node.title);
|
|
2819
2890
|
};
|
|
2820
2891
|
}
|
|
2821
|
-
/** Compare two nodes by title (case-insensitive).
|
|
2892
|
+
/** Compare two nodes by title (case-insensitive).
|
|
2893
|
+
* @deprecated Use `key` option instead of `cmp` in sort methods.
|
|
2894
|
+
*/
|
|
2822
2895
|
function nodeTitleSorter(a, b) {
|
|
2823
2896
|
const x = a.title.toLowerCase();
|
|
2824
2897
|
const y = b.title.toLowerCase();
|
|
2825
2898
|
return x === y ? 0 : x > y ? 1 : -1;
|
|
2826
2899
|
}
|
|
2900
|
+
// /** Compare nodes by title (case-insensitive). */
|
|
2901
|
+
// export function nodeTitleKeyGetter(
|
|
2902
|
+
// node: WunderbaumNode
|
|
2903
|
+
// ): string | number | Array<any> {
|
|
2904
|
+
// return node.title.toLowerCase();
|
|
2905
|
+
// }
|
|
2827
2906
|
/**
|
|
2828
2907
|
* Convert 'flat' to 'nested' format.
|
|
2829
2908
|
*
|
|
@@ -3014,7 +3093,7 @@
|
|
|
3014
3093
|
/*!
|
|
3015
3094
|
* Wunderbaum - ext-grid
|
|
3016
3095
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3017
|
-
* v0.
|
|
3096
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
3018
3097
|
*/
|
|
3019
3098
|
class GridExtension extends WunderbaumExtension {
|
|
3020
3099
|
constructor(tree) {
|
|
@@ -3074,7 +3153,7 @@
|
|
|
3074
3153
|
super.init();
|
|
3075
3154
|
}
|
|
3076
3155
|
/**
|
|
3077
|
-
*
|
|
3156
|
+
* Handles drag and sragstop events for column resizing.
|
|
3078
3157
|
*/
|
|
3079
3158
|
handleDrag(e) {
|
|
3080
3159
|
const custom = e.customData;
|
|
@@ -3105,7 +3184,7 @@
|
|
|
3105
3184
|
/*!
|
|
3106
3185
|
* Wunderbaum - deferred
|
|
3107
3186
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3108
|
-
* v0.
|
|
3187
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
3109
3188
|
*/
|
|
3110
3189
|
/**
|
|
3111
3190
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -3158,7 +3237,7 @@
|
|
|
3158
3237
|
/*!
|
|
3159
3238
|
* Wunderbaum - wunderbaum_node
|
|
3160
3239
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3161
|
-
* v0.
|
|
3240
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
3162
3241
|
*/
|
|
3163
3242
|
/** WunderbaumNode properties that can be passed with source data.
|
|
3164
3243
|
* (Any other source properties will be stored as `node.data.PROP`.)
|
|
@@ -3209,7 +3288,7 @@
|
|
|
3209
3288
|
*/
|
|
3210
3289
|
class WunderbaumNode {
|
|
3211
3290
|
constructor(tree, parent, data) {
|
|
3212
|
-
var _a
|
|
3291
|
+
var _a;
|
|
3213
3292
|
/** Reference key. Unlike {@link key}, a `refKey` may occur multiple
|
|
3214
3293
|
* times within a tree (in this case we have 'clone nodes').
|
|
3215
3294
|
* @see Use {@link setKey} to modify.
|
|
@@ -3239,8 +3318,8 @@
|
|
|
3239
3318
|
assert(!data.children, "'children' not allowed here");
|
|
3240
3319
|
this.tree = tree;
|
|
3241
3320
|
this.parent = parent;
|
|
3242
|
-
this.key =
|
|
3243
|
-
this.title = "" + ((
|
|
3321
|
+
this.key = tree._calculateKey(data, parent);
|
|
3322
|
+
this.title = "" + ((_a = data.title) !== null && _a !== void 0 ? _a : "<" + this.key + ">");
|
|
3244
3323
|
this.expanded = !!data.expanded;
|
|
3245
3324
|
this.lazy = !!data.lazy;
|
|
3246
3325
|
// We set the following node properties only if a matching data value is
|
|
@@ -3361,8 +3440,14 @@
|
|
|
3361
3440
|
const forceExpand = applyMinExpanLevel && _level < tree.options.minExpandLevel;
|
|
3362
3441
|
for (const child of nodeData) {
|
|
3363
3442
|
const subChildren = child.children;
|
|
3443
|
+
// Remove children property from source data because it should not be
|
|
3444
|
+
// passed to the constructor of WunderbaumNode:
|
|
3364
3445
|
delete child.children;
|
|
3365
3446
|
const n = new WunderbaumNode(tree, this, child);
|
|
3447
|
+
// Set `children` property again, so it can be used in `reload()`
|
|
3448
|
+
if (subChildren != null) {
|
|
3449
|
+
child.children = subChildren;
|
|
3450
|
+
}
|
|
3366
3451
|
if (forceExpand && !n.isUnloaded()) {
|
|
3367
3452
|
n.expanded = true;
|
|
3368
3453
|
}
|
|
@@ -3778,15 +3863,12 @@
|
|
|
3778
3863
|
}
|
|
3779
3864
|
return l;
|
|
3780
3865
|
}
|
|
3781
|
-
/** Return a string representing the
|
|
3866
|
+
/** Return a string representing the hierarchical node path, e.g. "a/b/c".
|
|
3782
3867
|
* @param includeSelf
|
|
3783
3868
|
* @param part property name or callback
|
|
3784
3869
|
* @param separator
|
|
3785
3870
|
*/
|
|
3786
3871
|
getPath(includeSelf = true, part = "title", separator = "/") {
|
|
3787
|
-
// includeSelf = includeSelf !== false;
|
|
3788
|
-
// part = part || "title";
|
|
3789
|
-
// separator = separator || "/";
|
|
3790
3872
|
let val;
|
|
3791
3873
|
const path = [];
|
|
3792
3874
|
const isFunc = typeof part === "function";
|
|
@@ -3801,7 +3883,7 @@
|
|
|
3801
3883
|
}, includeSelf);
|
|
3802
3884
|
return path.join(separator);
|
|
3803
3885
|
}
|
|
3804
|
-
/** Return the
|
|
3886
|
+
/** Return the preceding node (under the same parent) or null. */
|
|
3805
3887
|
getPrevSibling() {
|
|
3806
3888
|
const ac = this.parent.children;
|
|
3807
3889
|
const idx = ac.indexOf(this);
|
|
@@ -3830,7 +3912,7 @@
|
|
|
3830
3912
|
hasClass(className) {
|
|
3831
3913
|
return this.classes ? this.classes.has(className) : false;
|
|
3832
3914
|
}
|
|
3833
|
-
/** Return true if node
|
|
3915
|
+
/** Return true if node is the currently focused node. @since 0.9.0 */
|
|
3834
3916
|
hasFocus() {
|
|
3835
3917
|
return this.tree.focusNode === this;
|
|
3836
3918
|
}
|
|
@@ -3885,7 +3967,7 @@
|
|
|
3885
3967
|
* an expand operation is currently possible.
|
|
3886
3968
|
*/
|
|
3887
3969
|
isExpandable(andCollapsed = false) {
|
|
3888
|
-
// `false` is never expandable (
|
|
3970
|
+
// `false` is never expandable (unofficial)
|
|
3889
3971
|
if ((andCollapsed && this.expanded) || this.children === false) {
|
|
3890
3972
|
return false;
|
|
3891
3973
|
}
|
|
@@ -3948,11 +4030,11 @@
|
|
|
3948
4030
|
isPartsel() {
|
|
3949
4031
|
return !this.selected && !!this._partsel;
|
|
3950
4032
|
}
|
|
3951
|
-
/** Return true if this node has DOM
|
|
4033
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
3952
4034
|
isRadio() {
|
|
3953
4035
|
return !!this.parent.radiogroup || this.getOption("checkbox") === "radio";
|
|
3954
4036
|
}
|
|
3955
|
-
/** Return true if this node has DOM
|
|
4037
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
3956
4038
|
isRendered() {
|
|
3957
4039
|
return !!this._rowElem;
|
|
3958
4040
|
}
|
|
@@ -4707,9 +4789,9 @@
|
|
|
4707
4789
|
const typeInfo = this.type ? tree.types[this.type] : null;
|
|
4708
4790
|
const rowDiv = this._rowElem;
|
|
4709
4791
|
// Row markup already exists
|
|
4710
|
-
const
|
|
4711
|
-
const
|
|
4712
|
-
const
|
|
4792
|
+
const nodeSpan = rowDiv.querySelector("span.wb-node");
|
|
4793
|
+
const expanderElem = nodeSpan.querySelector("i.wb-expander");
|
|
4794
|
+
const checkboxElem = nodeSpan.querySelector("i.wb-checkbox");
|
|
4713
4795
|
const rowClasses = ["wb-row"];
|
|
4714
4796
|
this.expanded ? rowClasses.push("wb-expanded") : 0;
|
|
4715
4797
|
this.lazy ? rowClasses.push("wb-lazy") : 0;
|
|
@@ -4734,7 +4816,7 @@
|
|
|
4734
4816
|
if (typeInfo && typeInfo.classes) {
|
|
4735
4817
|
rowDiv.classList.add(...typeInfo.classes);
|
|
4736
4818
|
}
|
|
4737
|
-
if (
|
|
4819
|
+
if (expanderElem) {
|
|
4738
4820
|
let image = null;
|
|
4739
4821
|
if (this._isLoading) {
|
|
4740
4822
|
image = iconMap.loading;
|
|
@@ -4751,16 +4833,20 @@
|
|
|
4751
4833
|
image = iconMap.expanderLazy;
|
|
4752
4834
|
}
|
|
4753
4835
|
if (image == null) {
|
|
4754
|
-
|
|
4836
|
+
expanderElem.className = "wb-expander";
|
|
4837
|
+
expanderElem.classList.add("wb-indent");
|
|
4838
|
+
}
|
|
4839
|
+
else if (TEST_HTML.test(image)) {
|
|
4840
|
+
expanderElem.replaceWith(elemFromHtml(image));
|
|
4755
4841
|
}
|
|
4756
|
-
else if (
|
|
4757
|
-
|
|
4842
|
+
else if (TEST_FILE_PATH.test(image)) {
|
|
4843
|
+
expanderElem.style.backgroundImage = `url('${image}')`;
|
|
4758
4844
|
}
|
|
4759
4845
|
else {
|
|
4760
|
-
|
|
4846
|
+
expanderElem.className = "wb-expander " + image;
|
|
4761
4847
|
}
|
|
4762
4848
|
}
|
|
4763
|
-
if (
|
|
4849
|
+
if (checkboxElem) {
|
|
4764
4850
|
let cbclass = "wb-checkbox ";
|
|
4765
4851
|
if (this.isRadio()) {
|
|
4766
4852
|
cbclass += "wb-radio ";
|
|
@@ -4784,7 +4870,7 @@
|
|
|
4784
4870
|
cbclass += iconMap.checkUnchecked;
|
|
4785
4871
|
}
|
|
4786
4872
|
}
|
|
4787
|
-
|
|
4873
|
+
checkboxElem.className = cbclass;
|
|
4788
4874
|
}
|
|
4789
4875
|
// Fix active cell in cell-nav mode
|
|
4790
4876
|
if (!opts.isNew) {
|
|
@@ -4794,9 +4880,9 @@
|
|
|
4794
4880
|
colSpan.classList.remove("wb-error", "wb-invalid");
|
|
4795
4881
|
}
|
|
4796
4882
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
4797
|
-
const iconSpan =
|
|
4883
|
+
const iconSpan = nodeSpan.querySelector("i.wb-icon");
|
|
4798
4884
|
if (iconSpan) {
|
|
4799
|
-
this._createIcon(
|
|
4885
|
+
this._createIcon(nodeSpan, iconSpan, !expanderElem);
|
|
4800
4886
|
}
|
|
4801
4887
|
}
|
|
4802
4888
|
// Adjust column width
|
|
@@ -5101,6 +5187,32 @@
|
|
|
5101
5187
|
setKey(key, refKey) {
|
|
5102
5188
|
throw new Error("Not yet implemented");
|
|
5103
5189
|
}
|
|
5190
|
+
// /**
|
|
5191
|
+
// * Calculate a *stable*, unique key for this node from its refKey (or title).
|
|
5192
|
+
// * We also add information from the parent, because a refKey may occur multiple
|
|
5193
|
+
// * times in a tree.
|
|
5194
|
+
// */
|
|
5195
|
+
// calcUniqueKey() {
|
|
5196
|
+
// // Assuming that the parent's key was calculated the same way, we implicitly
|
|
5197
|
+
// // involve the whole refKey-path:
|
|
5198
|
+
// const s = this.key + (this.refKey || this.title);
|
|
5199
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
5200
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
5201
|
+
// const h1 = util.murmurHash3(s, true);
|
|
5202
|
+
// return "id_" + h1 + util.murmurHash3(h1 + s, true);
|
|
5203
|
+
// // const l = [];
|
|
5204
|
+
// // // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
5205
|
+
// // let node: WunderbaumNode = this;
|
|
5206
|
+
// // while (node.parent) {
|
|
5207
|
+
// // l.unshift(node.refKey || node.key);
|
|
5208
|
+
// // node = node.parent;
|
|
5209
|
+
// // }
|
|
5210
|
+
// // const path = l.join("/");
|
|
5211
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
5212
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
5213
|
+
// // const h1 = util.murmurHash3(path, true);
|
|
5214
|
+
// // return "id_" + h1 + util.murmurHash3(h1 + path, true);
|
|
5215
|
+
// }
|
|
5104
5216
|
/**
|
|
5105
5217
|
* Trigger a repaint, typically after a status or data change.
|
|
5106
5218
|
*
|
|
@@ -5132,6 +5244,23 @@
|
|
|
5132
5244
|
});
|
|
5133
5245
|
return nodeList;
|
|
5134
5246
|
}
|
|
5247
|
+
/**
|
|
5248
|
+
* Return an array of refKey values.
|
|
5249
|
+
*
|
|
5250
|
+
* RefKeys are unique identifiers for a node data, and are used to identify
|
|
5251
|
+
* clones.
|
|
5252
|
+
* If more than one node has the same refKey, it is only returned once.
|
|
5253
|
+
* @param selected if true, only return refKeys of selected nodes.
|
|
5254
|
+
*/
|
|
5255
|
+
getRefKeys(selected = false) {
|
|
5256
|
+
const refKeys = new Set();
|
|
5257
|
+
this.visit((node) => {
|
|
5258
|
+
if (node.refKey != null && (!selected || node.selected)) {
|
|
5259
|
+
refKeys.add(node.refKey);
|
|
5260
|
+
}
|
|
5261
|
+
});
|
|
5262
|
+
return Array.from(refKeys);
|
|
5263
|
+
}
|
|
5135
5264
|
/** Toggle the check/uncheck state. */
|
|
5136
5265
|
toggleSelected(options) {
|
|
5137
5266
|
let flag = this.isSelected();
|
|
@@ -5311,9 +5440,11 @@
|
|
|
5311
5440
|
if (selectMode === "hier") {
|
|
5312
5441
|
this.fixSelection3AfterClick();
|
|
5313
5442
|
}
|
|
5314
|
-
else if (selectMode === "single") {
|
|
5443
|
+
else if (selectMode === "single" && flag) {
|
|
5315
5444
|
tree.visit((n) => {
|
|
5316
|
-
n
|
|
5445
|
+
if (n !== this) {
|
|
5446
|
+
n.selected = false;
|
|
5447
|
+
}
|
|
5317
5448
|
});
|
|
5318
5449
|
}
|
|
5319
5450
|
}
|
|
@@ -5415,30 +5546,16 @@
|
|
|
5415
5546
|
this.tooltip = tooltip;
|
|
5416
5547
|
this.update();
|
|
5417
5548
|
}
|
|
5418
|
-
_sortChildren(cmp, deep) {
|
|
5419
|
-
const cl = this.children;
|
|
5420
|
-
if (!cl) {
|
|
5421
|
-
return;
|
|
5422
|
-
}
|
|
5423
|
-
cl.sort(cmp);
|
|
5424
|
-
if (deep) {
|
|
5425
|
-
for (let i = 0, l = cl.length; i < l; i++) {
|
|
5426
|
-
if (cl[i].children) {
|
|
5427
|
-
cl[i]._sortChildren(cmp, deep);
|
|
5428
|
-
}
|
|
5429
|
-
}
|
|
5430
|
-
}
|
|
5431
|
-
}
|
|
5432
5549
|
/**
|
|
5433
5550
|
* Sort child list by title or custom criteria.
|
|
5434
5551
|
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
5435
5552
|
* (defaults to sorting by title).
|
|
5436
5553
|
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
5554
|
+
* @deprecated use {@link sort}
|
|
5437
5555
|
*/
|
|
5438
5556
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
5439
|
-
this.
|
|
5440
|
-
this.
|
|
5441
|
-
// this.triggerModify("sort"); // TODO
|
|
5557
|
+
this.tree.logDeprecate("node.sortChildren()", { since: "0.14.0" });
|
|
5558
|
+
return this.sort({ cmp: cmp ? cmp : undefined, deep: deep });
|
|
5442
5559
|
}
|
|
5443
5560
|
/**
|
|
5444
5561
|
* Renumber nodes `_nativeIndex`. This is useful to allow to restore the
|
|
@@ -5460,74 +5577,142 @@
|
|
|
5460
5577
|
/**
|
|
5461
5578
|
* Convenience method to implement column sorting.
|
|
5462
5579
|
* @since 0.11.0
|
|
5580
|
+
* @deprecated use {@link sort}
|
|
5463
5581
|
*/
|
|
5464
5582
|
sortByProperty(options) {
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5583
|
+
this.tree.logDeprecate("node.sortByProperty()", { since: "0.14.0" });
|
|
5584
|
+
return this.sort(options);
|
|
5585
|
+
}
|
|
5586
|
+
/**
|
|
5587
|
+
* Implement column sorting.
|
|
5588
|
+
* @since 0.14.0
|
|
5589
|
+
*/
|
|
5590
|
+
sort(options) {
|
|
5591
|
+
const tree = this.tree;
|
|
5592
|
+
let { propName = undefined, deep = true, key = undefined, order = undefined, caseInsensitive = true, cmp = undefined,
|
|
5593
|
+
// Support click on column sort header:
|
|
5594
|
+
updateColInfo = false, nativeOrderPropName = "_nativeIndex", colId = undefined, } = options;
|
|
5595
|
+
propName !== null && propName !== void 0 ? propName : (propName = colId);
|
|
5596
|
+
if (propName === "*") {
|
|
5597
|
+
propName = "title";
|
|
5598
|
+
}
|
|
5599
|
+
const isFolder = tree.options.sortFoldersFirst === true
|
|
5600
|
+
? (node) => node.hasChildren() !== false || node.type === NODE_TYPE_FOLDER
|
|
5601
|
+
: tree.options.sortFoldersFirst;
|
|
5469
5602
|
if (updateColInfo) {
|
|
5470
|
-
colDef = this.tree["_columnsById"][options.colId];
|
|
5603
|
+
const colDef = this.tree["_columnsById"][options.colId];
|
|
5471
5604
|
assert(colDef, `Invalid colId specified: ${options.colId}`);
|
|
5472
|
-
order =
|
|
5473
|
-
(_a = options.order) !== null && _a !== void 0 ? _a : rotate(colDef.sortOrder, ["asc", "desc", undefined]);
|
|
5605
|
+
order !== null && order !== void 0 ? order : (order = rotate(colDef.sortOrder, ["asc", "desc", undefined]));
|
|
5474
5606
|
for (const col of this.tree.columns) {
|
|
5475
5607
|
col.sortOrder = col === colDef ? order : undefined;
|
|
5476
5608
|
}
|
|
5609
|
+
if (order === undefined) {
|
|
5610
|
+
propName = nativeOrderPropName;
|
|
5611
|
+
order = "asc";
|
|
5612
|
+
}
|
|
5477
5613
|
this.tree.update(ChangeType.colStructure);
|
|
5478
5614
|
}
|
|
5479
5615
|
else {
|
|
5480
|
-
|
|
5616
|
+
propName !== null && propName !== void 0 ? propName : (propName = "title");
|
|
5617
|
+
order !== null && order !== void 0 ? order : (order = "asc");
|
|
5618
|
+
}
|
|
5619
|
+
this.logDebug(`sort(), propName=${propName}, ${order}`, options);
|
|
5620
|
+
assert(propName || cmp || key, "No `propName` or `key` specified");
|
|
5621
|
+
// Define a key callback from the parameters we have
|
|
5622
|
+
if (key == null && cmp == null) {
|
|
5623
|
+
key = (node) => {
|
|
5624
|
+
let val;
|
|
5625
|
+
if (NODE_DICT_PROPS.has(propName)) {
|
|
5626
|
+
val = node[propName];
|
|
5627
|
+
}
|
|
5628
|
+
else {
|
|
5629
|
+
val = node.data[propName];
|
|
5630
|
+
}
|
|
5631
|
+
if (caseInsensitive && typeof val === "string") {
|
|
5632
|
+
val = val.toLowerCase();
|
|
5633
|
+
}
|
|
5634
|
+
return val;
|
|
5635
|
+
};
|
|
5481
5636
|
}
|
|
5482
|
-
|
|
5483
|
-
if (
|
|
5484
|
-
|
|
5637
|
+
// Define a compare callback that uses the key callback
|
|
5638
|
+
if (cmp) {
|
|
5639
|
+
assert(!key, "`key` and `cmp` are mutually exclusive");
|
|
5640
|
+
tree.logDeprecate("SortOptions.cmp", {
|
|
5641
|
+
since: "0.14.0",
|
|
5642
|
+
hint: "use the `key` callback instead",
|
|
5643
|
+
});
|
|
5485
5644
|
}
|
|
5486
|
-
|
|
5487
|
-
propName
|
|
5488
|
-
|
|
5645
|
+
else {
|
|
5646
|
+
if (options.propName || options.caseInsensitive) {
|
|
5647
|
+
tree.logWarn("sort(): ignoring propName, caseInsensitive");
|
|
5648
|
+
}
|
|
5649
|
+
cmp = (a, b) => {
|
|
5650
|
+
if (isFolder) {
|
|
5651
|
+
const isFolderA = isFolder(a);
|
|
5652
|
+
if (isFolderA !== isFolder(b)) {
|
|
5653
|
+
return isFolderA ? -1 : 1;
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
let x = key(a);
|
|
5657
|
+
let y = key(b);
|
|
5658
|
+
// Assure we have reasonable comparisons with null values:
|
|
5659
|
+
if (x == null) {
|
|
5660
|
+
x = typeof y === "string" ? "" : 0;
|
|
5661
|
+
}
|
|
5662
|
+
else if (typeof x === "boolean") {
|
|
5663
|
+
x = x ? 1 : 0;
|
|
5664
|
+
}
|
|
5665
|
+
if (y == null) {
|
|
5666
|
+
y = typeof x === "string" ? "" : 0;
|
|
5667
|
+
}
|
|
5668
|
+
else if (typeof y === "boolean") {
|
|
5669
|
+
y = y ? 1 : 0;
|
|
5670
|
+
}
|
|
5671
|
+
if (order === "desc") {
|
|
5672
|
+
return x === y ? 0 : x > y ? -1 : 1;
|
|
5673
|
+
}
|
|
5674
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
5675
|
+
};
|
|
5489
5676
|
}
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
let av, bv;
|
|
5494
|
-
if (NODE_DICT_PROPS.has(propName)) {
|
|
5495
|
-
av = a[propName];
|
|
5496
|
-
bv = b[propName];
|
|
5497
|
-
}
|
|
5498
|
-
else {
|
|
5499
|
-
av = a.data[propName];
|
|
5500
|
-
bv = b.data[propName];
|
|
5501
|
-
}
|
|
5502
|
-
if (av == null && bv == null) {
|
|
5503
|
-
return 0;
|
|
5504
|
-
}
|
|
5505
|
-
if (av == null) {
|
|
5506
|
-
av = typeof bv === "string" ? "" : 0;
|
|
5507
|
-
}
|
|
5508
|
-
else if (typeof av === "boolean") {
|
|
5509
|
-
av = av ? 1 : 0;
|
|
5510
|
-
}
|
|
5511
|
-
if (bv == null) {
|
|
5512
|
-
bv = typeof av === "string" ? "" : 0;
|
|
5513
|
-
}
|
|
5514
|
-
else if (typeof bv === "boolean") {
|
|
5515
|
-
bv = bv ? 1 : 0;
|
|
5677
|
+
function _sortChildren(cl) {
|
|
5678
|
+
if (!cl) {
|
|
5679
|
+
return;
|
|
5516
5680
|
}
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5681
|
+
cl.sort(cmp);
|
|
5682
|
+
if (deep) {
|
|
5683
|
+
for (let i = 0, l = cl.length; i < l; i++) {
|
|
5684
|
+
if (cl[i].children) {
|
|
5685
|
+
_sortChildren(cl[i].children);
|
|
5686
|
+
}
|
|
5523
5687
|
}
|
|
5524
5688
|
}
|
|
5525
|
-
|
|
5526
|
-
|
|
5689
|
+
}
|
|
5690
|
+
if (this.children) {
|
|
5691
|
+
_sortChildren(this.children);
|
|
5692
|
+
}
|
|
5693
|
+
this.tree.update(ChangeType.structure);
|
|
5694
|
+
// this.triggerModify("sort"); // TODO
|
|
5695
|
+
}
|
|
5696
|
+
/**
|
|
5697
|
+
* Re-apply current sorting if any (use after lazy load).
|
|
5698
|
+
* Example:
|
|
5699
|
+
* ```js
|
|
5700
|
+
* load: function (e) {
|
|
5701
|
+
* // Whe loading a lazy branch, apply current sort order if any
|
|
5702
|
+
* e.node.resort();
|
|
5703
|
+
* },
|
|
5704
|
+
* ```
|
|
5705
|
+
* @since 0.14.0
|
|
5706
|
+
*/
|
|
5707
|
+
resort(options = {}) {
|
|
5708
|
+
for (const colDef of this.tree.columns) {
|
|
5709
|
+
if (colDef.sortOrder) {
|
|
5710
|
+
options.colId = colDef.id;
|
|
5711
|
+
options.order = colDef.sortOrder;
|
|
5712
|
+
this.sort(options);
|
|
5713
|
+
break;
|
|
5527
5714
|
}
|
|
5528
|
-
|
|
5529
|
-
};
|
|
5530
|
-
return this.sortChildren(cmp, deep);
|
|
5715
|
+
}
|
|
5531
5716
|
}
|
|
5532
5717
|
/**
|
|
5533
5718
|
* Trigger `modifyChild` event on a parent to signal that a child was modified.
|
|
@@ -5564,7 +5749,8 @@
|
|
|
5564
5749
|
* @param {function} callback the callback function.
|
|
5565
5750
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
5566
5751
|
* its children only.
|
|
5567
|
-
* @see
|
|
5752
|
+
* @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
|
|
5753
|
+
* @see {@link Wunderbaum.visit}.
|
|
5568
5754
|
*/
|
|
5569
5755
|
visit(callback, includeSelf = false) {
|
|
5570
5756
|
let res = true;
|
|
@@ -5637,7 +5823,7 @@
|
|
|
5637
5823
|
/*!
|
|
5638
5824
|
* Wunderbaum - ext-edit
|
|
5639
5825
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
5640
|
-
* v0.
|
|
5826
|
+
* v0.14.0, Fri, 20 Mar 2026 16:58:31 GMT (https://github.com/mar10/wunderbaum)
|
|
5641
5827
|
*/
|
|
5642
5828
|
// const START_MARKER = "\uFFF7";
|
|
5643
5829
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5872,7 +6058,7 @@
|
|
|
5872
6058
|
newValue = newValue.trim();
|
|
5873
6059
|
}
|
|
5874
6060
|
if (!node) {
|
|
5875
|
-
this.tree.logDebug("stopEditTitle: not in edit mode.");
|
|
6061
|
+
// this.tree.logDebug("stopEditTitle: not in edit mode.");
|
|
5876
6062
|
return;
|
|
5877
6063
|
}
|
|
5878
6064
|
node.logDebug(`stopEditTitle(${apply})`, options, focusElem, newValue);
|
|
@@ -5972,8 +6158,8 @@
|
|
|
5972
6158
|
* https://github.com/mar10/wunderbaum
|
|
5973
6159
|
*
|
|
5974
6160
|
* Released under the MIT license.
|
|
5975
|
-
* @version v0.
|
|
5976
|
-
* @date
|
|
6161
|
+
* @version v0.14.0
|
|
6162
|
+
* @date Fri, 20 Mar 2026 16:58:31 GMT
|
|
5977
6163
|
*/
|
|
5978
6164
|
// import "./wunderbaum.scss";
|
|
5979
6165
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -6022,17 +6208,17 @@
|
|
|
6022
6208
|
this._disableUpdateIgnoreCount = 0;
|
|
6023
6209
|
this._activeNode = null;
|
|
6024
6210
|
this._focusNode = null;
|
|
6211
|
+
this._initialSource = null;
|
|
6025
6212
|
/** Shared properties, referenced by `node.type`. */
|
|
6026
6213
|
this.types = {};
|
|
6027
6214
|
/** List of column definitions. */
|
|
6028
|
-
this.columns = [];
|
|
6215
|
+
this.columns = [];
|
|
6029
6216
|
this._columnsById = {};
|
|
6030
6217
|
// Modification Status
|
|
6031
6218
|
this.pendingChangeTypes = new Set();
|
|
6032
6219
|
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
6033
6220
|
this._util = util;
|
|
6034
6221
|
// --- SELECT ---
|
|
6035
|
-
// /** @internal */
|
|
6036
6222
|
// public selectRangeAnchor: WunderbaumNode | null = null;
|
|
6037
6223
|
// --- BREADCRUMB ---
|
|
6038
6224
|
/** Filter options (used as defaults for calls to {@link Wunderbaum.filterNodes} ) */
|
|
@@ -6051,37 +6237,44 @@
|
|
|
6051
6237
|
this.lastQuicksearchTerm = "";
|
|
6052
6238
|
// --- EDIT ---
|
|
6053
6239
|
this.lastClickTime = 0;
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6240
|
+
// Set default options and merge with user options
|
|
6241
|
+
const initOptions = Object.assign({
|
|
6242
|
+
id: undefined,
|
|
6243
|
+
source: [], // URL for GET/PUT, Ajax options, or callback
|
|
6244
|
+
element: unsafeCast(null),
|
|
6058
6245
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
6059
6246
|
header: null, // Show/hide header (pass bool or string)
|
|
6060
|
-
// headerHeightPx: ROW_HEIGHT,
|
|
6061
6247
|
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
6062
6248
|
iconMap: "bootstrap",
|
|
6063
|
-
columns: null,
|
|
6064
|
-
types:
|
|
6065
|
-
// escapeTitles: true,
|
|
6249
|
+
columns: [], //util.unsafeCast<ColumnDefinitionList>(null),
|
|
6250
|
+
types: {},
|
|
6066
6251
|
enabled: true,
|
|
6067
6252
|
fixedCol: false,
|
|
6068
6253
|
showSpinner: false,
|
|
6069
6254
|
checkbox: false,
|
|
6070
6255
|
minExpandLevel: 0,
|
|
6071
6256
|
emptyChildListExpandable: false,
|
|
6072
|
-
// updateThrottleWait: 200,
|
|
6073
6257
|
skeleton: false,
|
|
6258
|
+
autoCollapse: false,
|
|
6259
|
+
adjustHeight: true,
|
|
6074
6260
|
connectTopBreadcrumb: null,
|
|
6261
|
+
columnsFilterable: false,
|
|
6262
|
+
columnsMenu: false,
|
|
6263
|
+
columnsResizable: false,
|
|
6264
|
+
columnsSortable: false,
|
|
6075
6265
|
selectMode: "multi", // SelectModeType
|
|
6266
|
+
scrollIntoViewOnExpandClick: true,
|
|
6267
|
+
// --- Extensions (actually set by exensions on init)
|
|
6268
|
+
dnd: unsafeCast(null),
|
|
6269
|
+
edit: unsafeCast(null),
|
|
6270
|
+
filter: unsafeCast(null),
|
|
6076
6271
|
// --- KeyNav ---
|
|
6077
|
-
navigationModeOption: null,
|
|
6272
|
+
navigationModeOption: unsafeCast(null),
|
|
6078
6273
|
quicksearch: true,
|
|
6079
6274
|
// --- Events ---
|
|
6080
|
-
iconBadge: null,
|
|
6081
|
-
change: null,
|
|
6082
|
-
//
|
|
6083
|
-
error: null,
|
|
6084
|
-
receive: null,
|
|
6275
|
+
// iconBadge: null,
|
|
6276
|
+
// change: null,
|
|
6277
|
+
// ...
|
|
6085
6278
|
// --- Strings ---
|
|
6086
6279
|
strings: {
|
|
6087
6280
|
loadError: "Error",
|
|
@@ -6092,7 +6285,9 @@
|
|
|
6092
6285
|
noMatch: "No results",
|
|
6093
6286
|
matchIndex: "${match} of ${matches}",
|
|
6094
6287
|
},
|
|
6095
|
-
}, options)
|
|
6288
|
+
}, options);
|
|
6289
|
+
const opts = initOptions;
|
|
6290
|
+
this.options = opts;
|
|
6096
6291
|
const readyDeferred = new Deferred();
|
|
6097
6292
|
this.ready = readyDeferred.promise();
|
|
6098
6293
|
let readyOk = false;
|
|
@@ -6119,7 +6314,8 @@
|
|
|
6119
6314
|
this._callEvent("init", { error: err });
|
|
6120
6315
|
}
|
|
6121
6316
|
});
|
|
6122
|
-
this.id =
|
|
6317
|
+
this.id = initOptions.id || "wb_" + ++Wunderbaum.sequence;
|
|
6318
|
+
delete initOptions.id;
|
|
6123
6319
|
this.root = new WbSystemRoot(this);
|
|
6124
6320
|
this._registerExtension(new KeynavExtension(this));
|
|
6125
6321
|
this._registerExtension(new EditExtension(this));
|
|
@@ -6129,19 +6325,20 @@
|
|
|
6129
6325
|
this._registerExtension(new LoggerExtension(this));
|
|
6130
6326
|
this._updateViewportThrottled = adaptiveThrottle(this._updateViewportImmediately.bind(this), {});
|
|
6131
6327
|
// --- Evaluate options
|
|
6132
|
-
this.columns =
|
|
6133
|
-
delete
|
|
6328
|
+
this.columns = initOptions.columns || [];
|
|
6329
|
+
delete initOptions.columns;
|
|
6134
6330
|
if (!this.columns || !this.columns.length) {
|
|
6135
6331
|
const title = typeof opts.header === "string" ? opts.header : this.id;
|
|
6136
6332
|
this.columns = [{ id: "*", title: title, width: "*" }];
|
|
6137
6333
|
}
|
|
6138
|
-
if (
|
|
6139
|
-
this.setTypes(
|
|
6334
|
+
if (initOptions.types) {
|
|
6335
|
+
this.setTypes(initOptions.types, true);
|
|
6140
6336
|
}
|
|
6141
|
-
delete
|
|
6337
|
+
delete initOptions.types;
|
|
6142
6338
|
// --- Create Markup
|
|
6143
|
-
this.element = elemFromSelector(
|
|
6144
|
-
assert(!!this.element, `Invalid 'element' option: ${
|
|
6339
|
+
this.element = elemFromSelector(initOptions.element);
|
|
6340
|
+
assert(!!this.element, `Invalid 'element' option: ${initOptions.element}`);
|
|
6341
|
+
delete initOptions.element;
|
|
6145
6342
|
this.element.classList.add("wunderbaum");
|
|
6146
6343
|
if (!this.element.getAttribute("tabindex")) {
|
|
6147
6344
|
this.element.tabIndex = 0;
|
|
@@ -6217,11 +6414,11 @@
|
|
|
6217
6414
|
}
|
|
6218
6415
|
});
|
|
6219
6416
|
// --- Load initial data
|
|
6220
|
-
if (
|
|
6417
|
+
if (initOptions.source) {
|
|
6221
6418
|
if (opts.showSpinner) {
|
|
6222
6419
|
this.nodeListElement.innerHTML = `<progress class='spinner'>${opts.strings.loading}</progress>`;
|
|
6223
6420
|
}
|
|
6224
|
-
this.load(
|
|
6421
|
+
this.load(initOptions.source)
|
|
6225
6422
|
.then(() => {
|
|
6226
6423
|
// The source may have defined columns, so we may adjust the nav mode
|
|
6227
6424
|
if (opts.navigationModeOption == null) {
|
|
@@ -6254,15 +6451,18 @@
|
|
|
6254
6451
|
// has a wrong value at start???
|
|
6255
6452
|
this.update(ChangeType.any);
|
|
6256
6453
|
// --- Bind listeners
|
|
6257
|
-
this.
|
|
6258
|
-
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
6259
|
-
this.update(ChangeType.scroll);
|
|
6260
|
-
});
|
|
6454
|
+
this._registerEventHandlers();
|
|
6261
6455
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
6262
6456
|
// this.log("ResizeObserver: Size changed", entries);
|
|
6263
6457
|
this.update(ChangeType.resize);
|
|
6264
6458
|
});
|
|
6265
6459
|
this.resizeObserver.observe(this.element);
|
|
6460
|
+
}
|
|
6461
|
+
_registerEventHandlers() {
|
|
6462
|
+
this.element.addEventListener("scroll", (e) => {
|
|
6463
|
+
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
6464
|
+
this.update(ChangeType.scroll);
|
|
6465
|
+
});
|
|
6266
6466
|
onEvent(this.element, "click", ".wb-button,.wb-col-icon", (e) => {
|
|
6267
6467
|
var _a, _b;
|
|
6268
6468
|
const info = Wunderbaum.getEventInfo(e);
|
|
@@ -6278,9 +6478,6 @@
|
|
|
6278
6478
|
const node = info.node;
|
|
6279
6479
|
const mouseEvent = e;
|
|
6280
6480
|
// this.log("click", info);
|
|
6281
|
-
// if (this._selectRange(info) === false) {
|
|
6282
|
-
// return;
|
|
6283
|
-
// }
|
|
6284
6481
|
if (this._callEvent("click", { event: e, node: node, info: info }) === false) {
|
|
6285
6482
|
this.lastClickTime = Date.now();
|
|
6286
6483
|
return false;
|
|
@@ -6299,20 +6496,22 @@
|
|
|
6299
6496
|
(!slowClickDelay || Date.now() - this.lastClickTime < slowClickDelay)) {
|
|
6300
6497
|
node.startEditTitle();
|
|
6301
6498
|
}
|
|
6302
|
-
if (info.colIdx >= 0) {
|
|
6303
|
-
node.setActive(true, { colIdx: info.colIdx, event: e });
|
|
6304
|
-
}
|
|
6305
|
-
else {
|
|
6306
|
-
node.setActive(true, { event: e });
|
|
6307
|
-
}
|
|
6308
6499
|
if (info.region === NodeRegion.expander) {
|
|
6309
6500
|
node.setExpanded(!node.isExpanded(), {
|
|
6310
|
-
scrollIntoView: options.scrollIntoViewOnExpandClick !== false,
|
|
6501
|
+
scrollIntoView: this.options.scrollIntoViewOnExpandClick !== false,
|
|
6311
6502
|
});
|
|
6312
6503
|
}
|
|
6313
6504
|
else if (info.region === NodeRegion.checkbox) {
|
|
6314
6505
|
node.toggleSelected();
|
|
6315
6506
|
}
|
|
6507
|
+
else {
|
|
6508
|
+
if (info.colIdx >= 0) {
|
|
6509
|
+
node.setActive(true, { colIdx: info.colIdx, event: e });
|
|
6510
|
+
}
|
|
6511
|
+
else {
|
|
6512
|
+
node.setActive(true, { event: e });
|
|
6513
|
+
}
|
|
6514
|
+
}
|
|
6316
6515
|
}
|
|
6317
6516
|
this.lastClickTime = Date.now();
|
|
6318
6517
|
});
|
|
@@ -6348,7 +6547,7 @@
|
|
|
6348
6547
|
const targetNode = Wunderbaum.getNode(e);
|
|
6349
6548
|
this._callEvent("focus", { flag: flag, event: e });
|
|
6350
6549
|
if (flag && this.isRowNav() && !this.isEditingTitle()) {
|
|
6351
|
-
if (
|
|
6550
|
+
if (this.options.navigationModeOption === NavModeEnum.row) {
|
|
6352
6551
|
targetNode === null || targetNode === void 0 ? void 0 : targetNode.setActive();
|
|
6353
6552
|
}
|
|
6354
6553
|
else {
|
|
@@ -6415,11 +6614,12 @@
|
|
|
6415
6614
|
}
|
|
6416
6615
|
/**
|
|
6417
6616
|
* Return the icon-function -> icon-definition mapping.
|
|
6617
|
+
* @deprecated Use {@link Wunderbaum.iconMaps}
|
|
6418
6618
|
*/
|
|
6419
6619
|
get iconMap() {
|
|
6420
6620
|
const map = this.options.iconMap;
|
|
6421
6621
|
if (typeof map === "string") {
|
|
6422
|
-
return
|
|
6622
|
+
return defaultIconMaps[map];
|
|
6423
6623
|
}
|
|
6424
6624
|
return map;
|
|
6425
6625
|
}
|
|
@@ -6472,7 +6672,38 @@
|
|
|
6472
6672
|
ext.init();
|
|
6473
6673
|
}
|
|
6474
6674
|
}
|
|
6475
|
-
/**
|
|
6675
|
+
/**
|
|
6676
|
+
* Calculate a *stable*, unique key for a node from its refKey (or title).
|
|
6677
|
+
* We also add information from the parent, because a refKey may occur multiple
|
|
6678
|
+
* times in a tree (but not as child of the same parent).
|
|
6679
|
+
* @internal
|
|
6680
|
+
*/
|
|
6681
|
+
_calculateKey(data, parent) {
|
|
6682
|
+
if (data.key) {
|
|
6683
|
+
// Always use an explicitly passed key
|
|
6684
|
+
return data.key;
|
|
6685
|
+
}
|
|
6686
|
+
// Auto-keys are optional, use a monotonic counter by default:
|
|
6687
|
+
if (!this.options.autoKeys) {
|
|
6688
|
+
return "" + ++WunderbaumNode.sequence;
|
|
6689
|
+
}
|
|
6690
|
+
// Add the parent's key to the hash. Assuming this was generated by the
|
|
6691
|
+
// same algorithm, this should incorporate the whole path:
|
|
6692
|
+
const s = (parent ? parent.key : "") + (data.refKey || data.title);
|
|
6693
|
+
// 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
6694
|
+
// https://security.stackexchange.com/q/209882/207588
|
|
6695
|
+
const h1 = murmurHash3(s, true);
|
|
6696
|
+
let key = "id_" + h1 + murmurHash3(h1 + s, true);
|
|
6697
|
+
// Check for collisions
|
|
6698
|
+
// (Most likely if the same title occurs multiple in the same parent).
|
|
6699
|
+
const existingNode = this.keyMap.get(key);
|
|
6700
|
+
if (existingNode) {
|
|
6701
|
+
key += "." + ++Wunderbaum.sequence;
|
|
6702
|
+
this.logWarn(`Node with existing key: '${existingNode}', using ${key}.`, data);
|
|
6703
|
+
}
|
|
6704
|
+
return key;
|
|
6705
|
+
}
|
|
6706
|
+
/** Add node to tree's bookkeeping data structures. @internal */
|
|
6476
6707
|
_registerNode(node) {
|
|
6477
6708
|
const key = node.key;
|
|
6478
6709
|
assert(key != null, `Missing key: '${node}'.`);
|
|
@@ -6489,7 +6720,7 @@
|
|
|
6489
6720
|
}
|
|
6490
6721
|
}
|
|
6491
6722
|
}
|
|
6492
|
-
/** Remove node from tree's bookkeeping data structures. */
|
|
6723
|
+
/** Remove node from tree's bookkeeping data structures. @internal */
|
|
6493
6724
|
_unregisterNode(node) {
|
|
6494
6725
|
// Remove refKey reference from map (if any)
|
|
6495
6726
|
const rk = node.refKey;
|
|
@@ -6608,6 +6839,16 @@
|
|
|
6608
6839
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6609
6840
|
return this._getNodeByRowIdx(bottomIdx);
|
|
6610
6841
|
}
|
|
6842
|
+
/** Return preceding visible node in the viewport. */
|
|
6843
|
+
_getPrevNodeInView(node, ofs = 1) {
|
|
6844
|
+
this.visitRows((n) => {
|
|
6845
|
+
node = n;
|
|
6846
|
+
if (ofs-- <= 0) {
|
|
6847
|
+
return false;
|
|
6848
|
+
}
|
|
6849
|
+
}, { reverse: true, start: node || this.getActiveNode() });
|
|
6850
|
+
return node;
|
|
6851
|
+
}
|
|
6611
6852
|
/** Return following visible node in the viewport. */
|
|
6612
6853
|
_getNextNodeInView(node, options) {
|
|
6613
6854
|
let ofs = (options === null || options === void 0 ? void 0 : options.ofs) || 1;
|
|
@@ -6859,22 +7100,39 @@
|
|
|
6859
7100
|
/** Run code, but defer rendering of viewport until done.
|
|
6860
7101
|
*
|
|
6861
7102
|
* ```js
|
|
6862
|
-
* tree.runWithDeferredUpdate(() => {
|
|
6863
|
-
* return
|
|
7103
|
+
* const res = tree.runWithDeferredUpdate(() => {
|
|
7104
|
+
* return someFunctionThatWouldUpdateManyNodes();
|
|
6864
7105
|
* });
|
|
6865
7106
|
* ```
|
|
6866
7107
|
*/
|
|
6867
|
-
runWithDeferredUpdate(func
|
|
7108
|
+
runWithDeferredUpdate(func) {
|
|
6868
7109
|
try {
|
|
6869
7110
|
this.enableUpdate(false);
|
|
6870
7111
|
const res = func();
|
|
6871
|
-
assert(!(res instanceof Promise), `Promise return not allowed: ${res}`);
|
|
7112
|
+
assert(!(res instanceof Promise), `Promise return not allowed (see 'runWithDeferredUpdateAsync()'): ${res}`);
|
|
6872
7113
|
return res;
|
|
6873
7114
|
}
|
|
6874
7115
|
finally {
|
|
6875
7116
|
this.enableUpdate(true);
|
|
6876
7117
|
}
|
|
6877
7118
|
}
|
|
7119
|
+
/** Run code, but defer rendering of viewport until done.
|
|
7120
|
+
*
|
|
7121
|
+
* ```js
|
|
7122
|
+
* const res = await tree.runWithDeferredUpdate(async () => {
|
|
7123
|
+
* return someAsyncFunctionThatWouldUpdateManyNodes();
|
|
7124
|
+
* });
|
|
7125
|
+
* ```
|
|
7126
|
+
*/
|
|
7127
|
+
async runWithDeferredUpdateAsync(func) {
|
|
7128
|
+
try {
|
|
7129
|
+
this.enableUpdate(false);
|
|
7130
|
+
return await func();
|
|
7131
|
+
}
|
|
7132
|
+
finally {
|
|
7133
|
+
this.enableUpdate(true);
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
6878
7136
|
/** Recursively expand all expandable nodes (triggers lazy load if needed). */
|
|
6879
7137
|
async expandAll(flag = true, options) {
|
|
6880
7138
|
await this.root.expandAll(flag, options);
|
|
@@ -6894,6 +7152,17 @@
|
|
|
6894
7152
|
getSelectedNodes(stopOnParents = false) {
|
|
6895
7153
|
return this.root.getSelectedNodes(stopOnParents);
|
|
6896
7154
|
}
|
|
7155
|
+
/**
|
|
7156
|
+
* Return an array of refKey values.
|
|
7157
|
+
*
|
|
7158
|
+
* RefKeys are unique identifiers for a node data, and are used to identify
|
|
7159
|
+
* clones.
|
|
7160
|
+
* If more than one node has the same refKey, it is only returned once.
|
|
7161
|
+
* @param selected if true, only return refKeys of selected nodes.
|
|
7162
|
+
*/
|
|
7163
|
+
getRefKeys(selected = false) {
|
|
7164
|
+
return this.root.getRefKeys(selected);
|
|
7165
|
+
}
|
|
6897
7166
|
/*
|
|
6898
7167
|
* Return an array of selected nodes.
|
|
6899
7168
|
*/
|
|
@@ -7175,6 +7444,18 @@
|
|
|
7175
7444
|
format(name_cb, connectors) {
|
|
7176
7445
|
return this.root.format(name_cb, connectors);
|
|
7177
7446
|
}
|
|
7447
|
+
/**
|
|
7448
|
+
* Always returns null (so a tree instance behaves as `tree.root`).
|
|
7449
|
+
*/
|
|
7450
|
+
get parent() {
|
|
7451
|
+
return null;
|
|
7452
|
+
}
|
|
7453
|
+
/**
|
|
7454
|
+
* Return a list of top-level nodes.
|
|
7455
|
+
*/
|
|
7456
|
+
get children() {
|
|
7457
|
+
return this.root.children || [];
|
|
7458
|
+
}
|
|
7178
7459
|
/**
|
|
7179
7460
|
* Return the active cell (`span.wb-col`) of the currently active node or null.
|
|
7180
7461
|
*/
|
|
@@ -7293,7 +7574,7 @@
|
|
|
7293
7574
|
}
|
|
7294
7575
|
/** Return true if any node title or grid cell is currently beeing edited.
|
|
7295
7576
|
*
|
|
7296
|
-
* See also {@link
|
|
7577
|
+
* See also {@link isEditingTitle}.
|
|
7297
7578
|
*/
|
|
7298
7579
|
isEditing() {
|
|
7299
7580
|
const focusElem = this.nodeListElement.querySelector("input:focus,select:focus");
|
|
@@ -7301,7 +7582,7 @@
|
|
|
7301
7582
|
}
|
|
7302
7583
|
/** Return true if any node is currently in edit-title mode.
|
|
7303
7584
|
*
|
|
7304
|
-
* See also {@link WunderbaumNode.isEditingTitle} and {@link
|
|
7585
|
+
* See also {@link WunderbaumNode.isEditingTitle} and {@link isEditing}.
|
|
7305
7586
|
*/
|
|
7306
7587
|
isEditingTitle() {
|
|
7307
7588
|
return this._callMethod("edit.isEditingTitle");
|
|
@@ -7321,7 +7602,7 @@
|
|
|
7321
7602
|
return res;
|
|
7322
7603
|
}
|
|
7323
7604
|
/** Write to `console.log` with tree name as prefix if opts.debugLevel >= 4.
|
|
7324
|
-
* @see {@link
|
|
7605
|
+
* @see {@link logDebug}
|
|
7325
7606
|
*/
|
|
7326
7607
|
log(...args) {
|
|
7327
7608
|
if (this.options.debugLevel >= 4) {
|
|
@@ -7330,7 +7611,7 @@
|
|
|
7330
7611
|
}
|
|
7331
7612
|
/** Write to `console.debug` with tree name as prefix if opts.debugLevel >= 4.
|
|
7332
7613
|
* and browser console level includes debug/verbose messages.
|
|
7333
|
-
* @see {@link
|
|
7614
|
+
* @see {@link log}
|
|
7334
7615
|
*/
|
|
7335
7616
|
logDebug(...args) {
|
|
7336
7617
|
if (this.options.debugLevel >= 4) {
|
|
@@ -7368,6 +7649,19 @@
|
|
|
7368
7649
|
console.warn(this.toString(), ...args); // eslint-disable-line no-console
|
|
7369
7650
|
}
|
|
7370
7651
|
}
|
|
7652
|
+
/** Emit a warning for deprecated methods. @internal */
|
|
7653
|
+
logDeprecate(method, options) {
|
|
7654
|
+
if (this.options.debugLevel >= 2) {
|
|
7655
|
+
let msg = `${this}: ${method} is deprecated`;
|
|
7656
|
+
if (options === null || options === void 0 ? void 0 : options.since) {
|
|
7657
|
+
msg += ` since ${options.since}`;
|
|
7658
|
+
}
|
|
7659
|
+
if (options === null || options === void 0 ? void 0 : options.hint) {
|
|
7660
|
+
msg += ` (${options.since})`;
|
|
7661
|
+
}
|
|
7662
|
+
console.warn(msg + "."); // eslint-disable-line no-console
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7371
7665
|
/** Reset column widths to default. @since 0.10.0 */
|
|
7372
7666
|
resetColumns() {
|
|
7373
7667
|
this.columns.forEach((col) => {
|
|
@@ -7536,46 +7830,64 @@
|
|
|
7536
7830
|
this._focusNode = node;
|
|
7537
7831
|
}
|
|
7538
7832
|
/** Return the current selection/expansion/activation status. @experimental */
|
|
7539
|
-
getState(options) {
|
|
7833
|
+
getState(options = {}) {
|
|
7540
7834
|
var _a, _b;
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7835
|
+
const { activeKey = true, expandedKeys = false, selectedKeys = false, } = options;
|
|
7836
|
+
const expandSet = new Set();
|
|
7837
|
+
if (expandedKeys) {
|
|
7544
7838
|
for (const node of this) {
|
|
7545
|
-
if (node.
|
|
7546
|
-
|
|
7839
|
+
if (node.isExpanded() && node.hasChildren()) {
|
|
7840
|
+
expandSet.add(node.key);
|
|
7547
7841
|
}
|
|
7548
7842
|
}
|
|
7549
7843
|
}
|
|
7844
|
+
// Parents of active node are always expanded
|
|
7845
|
+
if (activeKey && this.activeNode) {
|
|
7846
|
+
this.activeNode.visitParents((n) => {
|
|
7847
|
+
if (n.parent) {
|
|
7848
|
+
expandSet.add(n.key);
|
|
7849
|
+
}
|
|
7850
|
+
}, false);
|
|
7851
|
+
}
|
|
7550
7852
|
const state = {
|
|
7853
|
+
expandedKeys: expandSet.size ? Array.from(expandSet) : undefined,
|
|
7551
7854
|
activeKey: (_b = (_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : null,
|
|
7552
7855
|
activeColIdx: this.activeColIdx,
|
|
7553
|
-
selectedKeys:
|
|
7554
|
-
?
|
|
7555
|
-
:
|
|
7556
|
-
expandedKeys: expandedKeys,
|
|
7856
|
+
selectedKeys: selectedKeys
|
|
7857
|
+
? this.getSelectedNodes().flatMap((n) => n.key)
|
|
7858
|
+
: undefined,
|
|
7557
7859
|
};
|
|
7558
7860
|
return state;
|
|
7559
7861
|
}
|
|
7560
7862
|
/** Apply selection/expansion/activation status. @experimental */
|
|
7561
|
-
setState(state, options) {
|
|
7562
|
-
|
|
7863
|
+
async setState(state, options = {}) {
|
|
7864
|
+
const { expandLazy = true } = options;
|
|
7865
|
+
return this.runWithDeferredUpdateAsync(async () => {
|
|
7563
7866
|
var _a, _b;
|
|
7564
|
-
if (state.
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7867
|
+
if (state.expandedKeys && state.expandedKeys.length) {
|
|
7868
|
+
if (expandLazy) {
|
|
7869
|
+
// Expand all keys recursively, even if they are not in the tree yet
|
|
7870
|
+
await this._loadLazyNodes(state.expandedKeys, {
|
|
7871
|
+
expand: true,
|
|
7872
|
+
noEvents: true,
|
|
7873
|
+
});
|
|
7568
7874
|
}
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7875
|
+
else {
|
|
7876
|
+
for (const key of state.expandedKeys) {
|
|
7877
|
+
(_a = this.findKey(key)) === null || _a === void 0 ? void 0 : _a.setExpanded(true);
|
|
7878
|
+
}
|
|
7573
7879
|
}
|
|
7574
7880
|
}
|
|
7575
7881
|
if (state.activeKey) {
|
|
7576
7882
|
this.setActiveNode(state.activeKey);
|
|
7577
7883
|
}
|
|
7578
|
-
if (state.
|
|
7884
|
+
if (state.selectedKeys) {
|
|
7885
|
+
this.selectAll(false);
|
|
7886
|
+
for (const key of state.selectedKeys) {
|
|
7887
|
+
(_b = this.findKey(key)) === null || _b === void 0 ? void 0 : _b.setSelected(true);
|
|
7888
|
+
}
|
|
7889
|
+
}
|
|
7890
|
+
if (this.isCellNav() && state.activeColIdx != null) {
|
|
7579
7891
|
this.setColumn(state.activeColIdx);
|
|
7580
7892
|
}
|
|
7581
7893
|
});
|
|
@@ -7736,18 +8048,33 @@
|
|
|
7736
8048
|
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
7737
8049
|
* (defaults to sorting by title).
|
|
7738
8050
|
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
8051
|
+
* @deprecated use {@link sort}
|
|
7739
8052
|
*/
|
|
7740
8053
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
7741
|
-
this.
|
|
8054
|
+
this.logDeprecate("sortChildren()", { since: "0.14.0" });
|
|
8055
|
+
return this.sort({
|
|
8056
|
+
cmp: cmp ? cmp : undefined,
|
|
8057
|
+
deep: deep,
|
|
8058
|
+
propName: "title",
|
|
8059
|
+
});
|
|
7742
8060
|
}
|
|
7743
8061
|
/**
|
|
7744
8062
|
* Convenience method to implement column sorting.
|
|
7745
8063
|
* @see {@link WunderbaumNode.sortByProperty}.
|
|
7746
8064
|
* @since 0.11.0
|
|
8065
|
+
* @deprecated use {@link sort}
|
|
7747
8066
|
*/
|
|
7748
8067
|
sortByProperty(options) {
|
|
8068
|
+
this.logDeprecate("sortByProperty()", { since: "0.14.0" });
|
|
7749
8069
|
this.root.sortByProperty(options);
|
|
7750
8070
|
}
|
|
8071
|
+
/**
|
|
8072
|
+
* Sort nodes list by title or custom criteria.
|
|
8073
|
+
* @since 0.14.0
|
|
8074
|
+
*/
|
|
8075
|
+
sort(options) {
|
|
8076
|
+
this.root.sort(options);
|
|
8077
|
+
}
|
|
7751
8078
|
/** Convert tree to an array of plain objects.
|
|
7752
8079
|
*
|
|
7753
8080
|
* @param callback is called for every node, in order to allow
|
|
@@ -7999,12 +8326,10 @@
|
|
|
7999
8326
|
iconElem = document.createElement("i");
|
|
8000
8327
|
iconElem.className = "wb-icon";
|
|
8001
8328
|
}
|
|
8002
|
-
else if (
|
|
8003
|
-
// HTML
|
|
8329
|
+
else if (TEST_HTML.test(icon)) {
|
|
8004
8330
|
iconElem = elemFromHtml(icon);
|
|
8005
8331
|
}
|
|
8006
|
-
else if (
|
|
8007
|
-
// Image URL
|
|
8332
|
+
else if (TEST_FILE_PATH.test(icon)) {
|
|
8008
8333
|
iconElem = elemFromHtml(`<i class="wb-icon" style="background-image: url('${icon}');">`);
|
|
8009
8334
|
}
|
|
8010
8335
|
else {
|
|
@@ -8242,7 +8567,8 @@
|
|
|
8242
8567
|
}
|
|
8243
8568
|
/**
|
|
8244
8569
|
* Call `callback(node)` for all nodes in hierarchical order (depth-first, pre-order).
|
|
8245
|
-
* @see
|
|
8570
|
+
* @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
|
|
8571
|
+
* @see {@link WunderbaumNode.visit}.
|
|
8246
8572
|
*
|
|
8247
8573
|
* @param {function} callback the callback function.
|
|
8248
8574
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
@@ -8385,11 +8711,71 @@
|
|
|
8385
8711
|
*
|
|
8386
8712
|
* Previous data is cleared. Note that also column- and type defintions may
|
|
8387
8713
|
* be passed with the `source` object.
|
|
8714
|
+
* @see {@link Wunderbaum.reload} for a shortcut to reload the last ajax request
|
|
8715
|
+
* and restore the previous state.
|
|
8388
8716
|
*/
|
|
8389
|
-
load(source) {
|
|
8717
|
+
async load(source) {
|
|
8390
8718
|
this.clear();
|
|
8719
|
+
this._initialSource = source;
|
|
8391
8720
|
return this.root.load(source);
|
|
8392
8721
|
}
|
|
8722
|
+
/** Reload the tree and optionally restore state.
|
|
8723
|
+
* Source defaults to last ajax url if any.
|
|
8724
|
+
* Restoring the active node requires stable keys
|
|
8725
|
+
* @see {@link WunderbaumOptions.autoKeys}
|
|
8726
|
+
* @see {@link Wunderbaum.load}
|
|
8727
|
+
* @experimental
|
|
8728
|
+
*/
|
|
8729
|
+
async reload(options = {}) {
|
|
8730
|
+
const { source = this._initialSource, reactivate = true } = options;
|
|
8731
|
+
if (!source) {
|
|
8732
|
+
this.logWarn("No previous ajax source to reload.");
|
|
8733
|
+
return;
|
|
8734
|
+
}
|
|
8735
|
+
if (!reactivate) {
|
|
8736
|
+
return this.load(source);
|
|
8737
|
+
}
|
|
8738
|
+
const state = this.getState();
|
|
8739
|
+
await this.load(source);
|
|
8740
|
+
return this.setState(state);
|
|
8741
|
+
}
|
|
8742
|
+
/**
|
|
8743
|
+
* Make sure that all nodes in the given keyList are accessible.
|
|
8744
|
+
* This may include loading lazy parent nodes.
|
|
8745
|
+
* Recursively load (and optionally expand) all requested node paths.
|
|
8746
|
+
*/
|
|
8747
|
+
async _loadLazyNodes(keyList, options = {}) {
|
|
8748
|
+
const { expand = true } = options;
|
|
8749
|
+
const keySet = new Set(keyList);
|
|
8750
|
+
// Make sure that all parent nodes are loaded (and expand if requested)
|
|
8751
|
+
while (keySet.size > 0) {
|
|
8752
|
+
const pendingNodes = [];
|
|
8753
|
+
const curSet = new Set(keySet);
|
|
8754
|
+
for (const key of curSet) {
|
|
8755
|
+
const node = this.findKey(key);
|
|
8756
|
+
if (!node) {
|
|
8757
|
+
continue; // key not yet found (need to load lazy parent?)
|
|
8758
|
+
}
|
|
8759
|
+
keySet.delete(key);
|
|
8760
|
+
if (expand) {
|
|
8761
|
+
pendingNodes.push(node.setExpanded(true));
|
|
8762
|
+
}
|
|
8763
|
+
else if (node.isUnloaded()) {
|
|
8764
|
+
pendingNodes.push(node.loadLazy());
|
|
8765
|
+
}
|
|
8766
|
+
if (node._rowElem) {
|
|
8767
|
+
node._render(); // show spinner even is update is suppressed
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
if (pendingNodes.length === 0) {
|
|
8771
|
+
// will not load any more nodes, so if if there are still keys
|
|
8772
|
+
// left in the set, we will never find them
|
|
8773
|
+
this.logWarn(`Could not expand ${keySet.size} nodes:`, keySet);
|
|
8774
|
+
break;
|
|
8775
|
+
}
|
|
8776
|
+
await Promise.allSettled(pendingNodes);
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8393
8779
|
/**
|
|
8394
8780
|
* Disable render requests during operations that would trigger many updates.
|
|
8395
8781
|
*
|
|
@@ -8487,9 +8873,21 @@
|
|
|
8487
8873
|
}
|
|
8488
8874
|
Wunderbaum.sequence = 0;
|
|
8489
8875
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8490
|
-
Wunderbaum.version = "v0.
|
|
8876
|
+
Wunderbaum.version = "v0.14.0"; // Set to semver by 'grunt release'
|
|
8491
8877
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8492
8878
|
Wunderbaum.util = util;
|
|
8879
|
+
/** A map of default iconMaps.
|
|
8880
|
+
* May be used as default, when passing partial icon definition maps:
|
|
8881
|
+
* ```js
|
|
8882
|
+
* const tree = new mar10.Wunderbaum({
|
|
8883
|
+
* ...
|
|
8884
|
+
* iconMap: Object.assign(Wunderbaum.iconMaps.bootstrap, {
|
|
8885
|
+
* folder: "bi bi-archive",
|
|
8886
|
+
* }),
|
|
8887
|
+
* });
|
|
8888
|
+
* ```
|
|
8889
|
+
*/
|
|
8890
|
+
Wunderbaum.iconMaps = defaultIconMaps;
|
|
8493
8891
|
|
|
8494
8892
|
exports.Wunderbaum = Wunderbaum;
|
|
8495
8893
|
|