wunderbaum 0.13.0 → 0.14.1
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 +622 -222
- package/dist/wunderbaum.esm.min.js +28 -28
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +622 -222
- 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 +225 -107
- 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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.1, Sun, 22 Mar 2026 05:52:05 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
|
|
@@ -3257,7 +3336,9 @@
|
|
|
3257
3336
|
: 0;
|
|
3258
3337
|
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
3259
3338
|
// Selection
|
|
3260
|
-
data.checkbox != null
|
|
3339
|
+
data.checkbox != null
|
|
3340
|
+
? (this.checkbox = intToBool(data.checkbox))
|
|
3341
|
+
: 0;
|
|
3261
3342
|
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
3262
3343
|
data.selected != null ? (this.selected = !!data.selected) : 0;
|
|
3263
3344
|
data.unselectable != null ? (this.unselectable = !!data.unselectable) : 0;
|
|
@@ -3361,8 +3442,14 @@
|
|
|
3361
3442
|
const forceExpand = applyMinExpanLevel && _level < tree.options.minExpandLevel;
|
|
3362
3443
|
for (const child of nodeData) {
|
|
3363
3444
|
const subChildren = child.children;
|
|
3445
|
+
// Remove children property from source data because it should not be
|
|
3446
|
+
// passed to the constructor of WunderbaumNode:
|
|
3364
3447
|
delete child.children;
|
|
3365
3448
|
const n = new WunderbaumNode(tree, this, child);
|
|
3449
|
+
// Set `children` property again, so it can be used in `reload()`
|
|
3450
|
+
if (subChildren != null) {
|
|
3451
|
+
child.children = subChildren;
|
|
3452
|
+
}
|
|
3366
3453
|
if (forceExpand && !n.isUnloaded()) {
|
|
3367
3454
|
n.expanded = true;
|
|
3368
3455
|
}
|
|
@@ -3778,15 +3865,12 @@
|
|
|
3778
3865
|
}
|
|
3779
3866
|
return l;
|
|
3780
3867
|
}
|
|
3781
|
-
/** Return a string representing the
|
|
3868
|
+
/** Return a string representing the hierarchical node path, e.g. "a/b/c".
|
|
3782
3869
|
* @param includeSelf
|
|
3783
3870
|
* @param part property name or callback
|
|
3784
3871
|
* @param separator
|
|
3785
3872
|
*/
|
|
3786
3873
|
getPath(includeSelf = true, part = "title", separator = "/") {
|
|
3787
|
-
// includeSelf = includeSelf !== false;
|
|
3788
|
-
// part = part || "title";
|
|
3789
|
-
// separator = separator || "/";
|
|
3790
3874
|
let val;
|
|
3791
3875
|
const path = [];
|
|
3792
3876
|
const isFunc = typeof part === "function";
|
|
@@ -3801,7 +3885,7 @@
|
|
|
3801
3885
|
}, includeSelf);
|
|
3802
3886
|
return path.join(separator);
|
|
3803
3887
|
}
|
|
3804
|
-
/** Return the
|
|
3888
|
+
/** Return the preceding node (under the same parent) or null. */
|
|
3805
3889
|
getPrevSibling() {
|
|
3806
3890
|
const ac = this.parent.children;
|
|
3807
3891
|
const idx = ac.indexOf(this);
|
|
@@ -3830,7 +3914,7 @@
|
|
|
3830
3914
|
hasClass(className) {
|
|
3831
3915
|
return this.classes ? this.classes.has(className) : false;
|
|
3832
3916
|
}
|
|
3833
|
-
/** Return true if node
|
|
3917
|
+
/** Return true if node is the currently focused node. @since 0.9.0 */
|
|
3834
3918
|
hasFocus() {
|
|
3835
3919
|
return this.tree.focusNode === this;
|
|
3836
3920
|
}
|
|
@@ -3885,7 +3969,7 @@
|
|
|
3885
3969
|
* an expand operation is currently possible.
|
|
3886
3970
|
*/
|
|
3887
3971
|
isExpandable(andCollapsed = false) {
|
|
3888
|
-
// `false` is never expandable (
|
|
3972
|
+
// `false` is never expandable (unofficial)
|
|
3889
3973
|
if ((andCollapsed && this.expanded) || this.children === false) {
|
|
3890
3974
|
return false;
|
|
3891
3975
|
}
|
|
@@ -3948,11 +4032,11 @@
|
|
|
3948
4032
|
isPartsel() {
|
|
3949
4033
|
return !this.selected && !!this._partsel;
|
|
3950
4034
|
}
|
|
3951
|
-
/** Return true if this node has DOM
|
|
4035
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
3952
4036
|
isRadio() {
|
|
3953
4037
|
return !!this.parent.radiogroup || this.getOption("checkbox") === "radio";
|
|
3954
4038
|
}
|
|
3955
|
-
/** Return true if this node has DOM
|
|
4039
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
3956
4040
|
isRendered() {
|
|
3957
4041
|
return !!this._rowElem;
|
|
3958
4042
|
}
|
|
@@ -4707,9 +4791,9 @@
|
|
|
4707
4791
|
const typeInfo = this.type ? tree.types[this.type] : null;
|
|
4708
4792
|
const rowDiv = this._rowElem;
|
|
4709
4793
|
// Row markup already exists
|
|
4710
|
-
const
|
|
4711
|
-
const
|
|
4712
|
-
const
|
|
4794
|
+
const nodeSpan = rowDiv.querySelector("span.wb-node");
|
|
4795
|
+
const expanderElem = nodeSpan.querySelector("i.wb-expander");
|
|
4796
|
+
const checkboxElem = nodeSpan.querySelector("i.wb-checkbox");
|
|
4713
4797
|
const rowClasses = ["wb-row"];
|
|
4714
4798
|
this.expanded ? rowClasses.push("wb-expanded") : 0;
|
|
4715
4799
|
this.lazy ? rowClasses.push("wb-lazy") : 0;
|
|
@@ -4734,7 +4818,7 @@
|
|
|
4734
4818
|
if (typeInfo && typeInfo.classes) {
|
|
4735
4819
|
rowDiv.classList.add(...typeInfo.classes);
|
|
4736
4820
|
}
|
|
4737
|
-
if (
|
|
4821
|
+
if (expanderElem) {
|
|
4738
4822
|
let image = null;
|
|
4739
4823
|
if (this._isLoading) {
|
|
4740
4824
|
image = iconMap.loading;
|
|
@@ -4751,16 +4835,20 @@
|
|
|
4751
4835
|
image = iconMap.expanderLazy;
|
|
4752
4836
|
}
|
|
4753
4837
|
if (image == null) {
|
|
4754
|
-
|
|
4838
|
+
expanderElem.className = "wb-expander";
|
|
4839
|
+
expanderElem.classList.add("wb-indent");
|
|
4840
|
+
}
|
|
4841
|
+
else if (TEST_HTML.test(image)) {
|
|
4842
|
+
expanderElem.replaceWith(elemFromHtml(image));
|
|
4755
4843
|
}
|
|
4756
|
-
else if (
|
|
4757
|
-
|
|
4844
|
+
else if (TEST_FILE_PATH.test(image)) {
|
|
4845
|
+
expanderElem.style.backgroundImage = `url('${image}')`;
|
|
4758
4846
|
}
|
|
4759
4847
|
else {
|
|
4760
|
-
|
|
4848
|
+
expanderElem.className = "wb-expander " + image;
|
|
4761
4849
|
}
|
|
4762
4850
|
}
|
|
4763
|
-
if (
|
|
4851
|
+
if (checkboxElem) {
|
|
4764
4852
|
let cbclass = "wb-checkbox ";
|
|
4765
4853
|
if (this.isRadio()) {
|
|
4766
4854
|
cbclass += "wb-radio ";
|
|
@@ -4784,7 +4872,7 @@
|
|
|
4784
4872
|
cbclass += iconMap.checkUnchecked;
|
|
4785
4873
|
}
|
|
4786
4874
|
}
|
|
4787
|
-
|
|
4875
|
+
checkboxElem.className = cbclass;
|
|
4788
4876
|
}
|
|
4789
4877
|
// Fix active cell in cell-nav mode
|
|
4790
4878
|
if (!opts.isNew) {
|
|
@@ -4794,9 +4882,9 @@
|
|
|
4794
4882
|
colSpan.classList.remove("wb-error", "wb-invalid");
|
|
4795
4883
|
}
|
|
4796
4884
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
4797
|
-
const iconSpan =
|
|
4885
|
+
const iconSpan = nodeSpan.querySelector("i.wb-icon");
|
|
4798
4886
|
if (iconSpan) {
|
|
4799
|
-
this._createIcon(
|
|
4887
|
+
this._createIcon(nodeSpan, iconSpan, !expanderElem);
|
|
4800
4888
|
}
|
|
4801
4889
|
}
|
|
4802
4890
|
// Adjust column width
|
|
@@ -5101,6 +5189,32 @@
|
|
|
5101
5189
|
setKey(key, refKey) {
|
|
5102
5190
|
throw new Error("Not yet implemented");
|
|
5103
5191
|
}
|
|
5192
|
+
// /**
|
|
5193
|
+
// * Calculate a *stable*, unique key for this node from its refKey (or title).
|
|
5194
|
+
// * We also add information from the parent, because a refKey may occur multiple
|
|
5195
|
+
// * times in a tree.
|
|
5196
|
+
// */
|
|
5197
|
+
// calcUniqueKey() {
|
|
5198
|
+
// // Assuming that the parent's key was calculated the same way, we implicitly
|
|
5199
|
+
// // involve the whole refKey-path:
|
|
5200
|
+
// const s = this.key + (this.refKey || this.title);
|
|
5201
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
5202
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
5203
|
+
// const h1 = util.murmurHash3(s, true);
|
|
5204
|
+
// return "id_" + h1 + util.murmurHash3(h1 + s, true);
|
|
5205
|
+
// // const l = [];
|
|
5206
|
+
// // // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
5207
|
+
// // let node: WunderbaumNode = this;
|
|
5208
|
+
// // while (node.parent) {
|
|
5209
|
+
// // l.unshift(node.refKey || node.key);
|
|
5210
|
+
// // node = node.parent;
|
|
5211
|
+
// // }
|
|
5212
|
+
// // const path = l.join("/");
|
|
5213
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
5214
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
5215
|
+
// // const h1 = util.murmurHash3(path, true);
|
|
5216
|
+
// // return "id_" + h1 + util.murmurHash3(h1 + path, true);
|
|
5217
|
+
// }
|
|
5104
5218
|
/**
|
|
5105
5219
|
* Trigger a repaint, typically after a status or data change.
|
|
5106
5220
|
*
|
|
@@ -5132,6 +5246,23 @@
|
|
|
5132
5246
|
});
|
|
5133
5247
|
return nodeList;
|
|
5134
5248
|
}
|
|
5249
|
+
/**
|
|
5250
|
+
* Return an array of refKey values.
|
|
5251
|
+
*
|
|
5252
|
+
* RefKeys are unique identifiers for a node data, and are used to identify
|
|
5253
|
+
* clones.
|
|
5254
|
+
* If more than one node has the same refKey, it is only returned once.
|
|
5255
|
+
* @param selected if true, only return refKeys of selected nodes.
|
|
5256
|
+
*/
|
|
5257
|
+
getRefKeys(selected = false) {
|
|
5258
|
+
const refKeys = new Set();
|
|
5259
|
+
this.visit((node) => {
|
|
5260
|
+
if (node.refKey != null && (!selected || node.selected)) {
|
|
5261
|
+
refKeys.add(node.refKey);
|
|
5262
|
+
}
|
|
5263
|
+
});
|
|
5264
|
+
return Array.from(refKeys);
|
|
5265
|
+
}
|
|
5135
5266
|
/** Toggle the check/uncheck state. */
|
|
5136
5267
|
toggleSelected(options) {
|
|
5137
5268
|
let flag = this.isSelected();
|
|
@@ -5311,9 +5442,11 @@
|
|
|
5311
5442
|
if (selectMode === "hier") {
|
|
5312
5443
|
this.fixSelection3AfterClick();
|
|
5313
5444
|
}
|
|
5314
|
-
else if (selectMode === "single") {
|
|
5445
|
+
else if (selectMode === "single" && flag) {
|
|
5315
5446
|
tree.visit((n) => {
|
|
5316
|
-
n
|
|
5447
|
+
if (n !== this) {
|
|
5448
|
+
n.selected = false;
|
|
5449
|
+
}
|
|
5317
5450
|
});
|
|
5318
5451
|
}
|
|
5319
5452
|
}
|
|
@@ -5415,30 +5548,16 @@
|
|
|
5415
5548
|
this.tooltip = tooltip;
|
|
5416
5549
|
this.update();
|
|
5417
5550
|
}
|
|
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
5551
|
/**
|
|
5433
5552
|
* Sort child list by title or custom criteria.
|
|
5434
5553
|
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
5435
5554
|
* (defaults to sorting by title).
|
|
5436
5555
|
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
5556
|
+
* @deprecated use {@link sort}
|
|
5437
5557
|
*/
|
|
5438
5558
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
5439
|
-
this.
|
|
5440
|
-
this.
|
|
5441
|
-
// this.triggerModify("sort"); // TODO
|
|
5559
|
+
this.tree.logDeprecate("node.sortChildren()", { since: "0.14.0" });
|
|
5560
|
+
return this.sort({ cmp: cmp ? cmp : undefined, deep: deep });
|
|
5442
5561
|
}
|
|
5443
5562
|
/**
|
|
5444
5563
|
* Renumber nodes `_nativeIndex`. This is useful to allow to restore the
|
|
@@ -5460,74 +5579,142 @@
|
|
|
5460
5579
|
/**
|
|
5461
5580
|
* Convenience method to implement column sorting.
|
|
5462
5581
|
* @since 0.11.0
|
|
5582
|
+
* @deprecated use {@link sort}
|
|
5463
5583
|
*/
|
|
5464
5584
|
sortByProperty(options) {
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5585
|
+
this.tree.logDeprecate("node.sortByProperty()", { since: "0.14.0" });
|
|
5586
|
+
return this.sort(options);
|
|
5587
|
+
}
|
|
5588
|
+
/**
|
|
5589
|
+
* Implement column sorting.
|
|
5590
|
+
* @since 0.14.0
|
|
5591
|
+
*/
|
|
5592
|
+
sort(options) {
|
|
5593
|
+
const tree = this.tree;
|
|
5594
|
+
let { propName = undefined, deep = true, key = undefined, order = undefined, caseInsensitive = true, cmp = undefined,
|
|
5595
|
+
// Support click on column sort header:
|
|
5596
|
+
updateColInfo = false, nativeOrderPropName = "_nativeIndex", colId = undefined, } = options;
|
|
5597
|
+
propName !== null && propName !== void 0 ? propName : (propName = colId);
|
|
5598
|
+
if (propName === "*") {
|
|
5599
|
+
propName = "title";
|
|
5600
|
+
}
|
|
5601
|
+
const isFolder = tree.options.sortFoldersFirst === true
|
|
5602
|
+
? (node) => node.hasChildren() !== false || node.type === NODE_TYPE_FOLDER
|
|
5603
|
+
: tree.options.sortFoldersFirst;
|
|
5469
5604
|
if (updateColInfo) {
|
|
5470
|
-
colDef = this.tree["_columnsById"][options.colId];
|
|
5605
|
+
const colDef = this.tree["_columnsById"][options.colId];
|
|
5471
5606
|
assert(colDef, `Invalid colId specified: ${options.colId}`);
|
|
5472
|
-
order =
|
|
5473
|
-
(_a = options.order) !== null && _a !== void 0 ? _a : rotate(colDef.sortOrder, ["asc", "desc", undefined]);
|
|
5607
|
+
order !== null && order !== void 0 ? order : (order = rotate(colDef.sortOrder, ["asc", "desc", undefined]));
|
|
5474
5608
|
for (const col of this.tree.columns) {
|
|
5475
5609
|
col.sortOrder = col === colDef ? order : undefined;
|
|
5476
5610
|
}
|
|
5611
|
+
if (order === undefined) {
|
|
5612
|
+
propName = nativeOrderPropName;
|
|
5613
|
+
order = "asc";
|
|
5614
|
+
}
|
|
5477
5615
|
this.tree.update(ChangeType.colStructure);
|
|
5478
5616
|
}
|
|
5479
5617
|
else {
|
|
5480
|
-
|
|
5618
|
+
propName !== null && propName !== void 0 ? propName : (propName = "title");
|
|
5619
|
+
order !== null && order !== void 0 ? order : (order = "asc");
|
|
5620
|
+
}
|
|
5621
|
+
this.logDebug(`sort(), propName=${propName}, ${order}`, options);
|
|
5622
|
+
assert(propName || cmp || key, "No `propName` or `key` specified");
|
|
5623
|
+
// Define a key callback from the parameters we have
|
|
5624
|
+
if (key == null && cmp == null) {
|
|
5625
|
+
key = (node) => {
|
|
5626
|
+
let val;
|
|
5627
|
+
if (NODE_DICT_PROPS.has(propName)) {
|
|
5628
|
+
val = node[propName];
|
|
5629
|
+
}
|
|
5630
|
+
else {
|
|
5631
|
+
val = node.data[propName];
|
|
5632
|
+
}
|
|
5633
|
+
if (caseInsensitive && typeof val === "string") {
|
|
5634
|
+
val = val.toLowerCase();
|
|
5635
|
+
}
|
|
5636
|
+
return val;
|
|
5637
|
+
};
|
|
5481
5638
|
}
|
|
5482
|
-
|
|
5483
|
-
if (
|
|
5484
|
-
|
|
5639
|
+
// Define a compare callback that uses the key callback
|
|
5640
|
+
if (cmp) {
|
|
5641
|
+
assert(!key, "`key` and `cmp` are mutually exclusive");
|
|
5642
|
+
tree.logDeprecate("SortOptions.cmp", {
|
|
5643
|
+
since: "0.14.0",
|
|
5644
|
+
hint: "use the `key` callback instead",
|
|
5645
|
+
});
|
|
5485
5646
|
}
|
|
5486
|
-
|
|
5487
|
-
propName
|
|
5488
|
-
|
|
5647
|
+
else {
|
|
5648
|
+
if (options.propName || options.caseInsensitive) {
|
|
5649
|
+
tree.logWarn("sort(): ignoring propName, caseInsensitive");
|
|
5650
|
+
}
|
|
5651
|
+
cmp = (a, b) => {
|
|
5652
|
+
if (isFolder) {
|
|
5653
|
+
const isFolderA = isFolder(a);
|
|
5654
|
+
if (isFolderA !== isFolder(b)) {
|
|
5655
|
+
return isFolderA ? -1 : 1;
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5658
|
+
let x = key(a);
|
|
5659
|
+
let y = key(b);
|
|
5660
|
+
// Assure we have reasonable comparisons with null values:
|
|
5661
|
+
if (x == null) {
|
|
5662
|
+
x = typeof y === "string" ? "" : 0;
|
|
5663
|
+
}
|
|
5664
|
+
else if (typeof x === "boolean") {
|
|
5665
|
+
x = x ? 1 : 0;
|
|
5666
|
+
}
|
|
5667
|
+
if (y == null) {
|
|
5668
|
+
y = typeof x === "string" ? "" : 0;
|
|
5669
|
+
}
|
|
5670
|
+
else if (typeof y === "boolean") {
|
|
5671
|
+
y = y ? 1 : 0;
|
|
5672
|
+
}
|
|
5673
|
+
if (order === "desc") {
|
|
5674
|
+
return x === y ? 0 : x > y ? -1 : 1;
|
|
5675
|
+
}
|
|
5676
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
5677
|
+
};
|
|
5489
5678
|
}
|
|
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;
|
|
5679
|
+
function _sortChildren(cl) {
|
|
5680
|
+
if (!cl) {
|
|
5681
|
+
return;
|
|
5516
5682
|
}
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5683
|
+
cl.sort(cmp);
|
|
5684
|
+
if (deep) {
|
|
5685
|
+
for (let i = 0, l = cl.length; i < l; i++) {
|
|
5686
|
+
if (cl[i].children) {
|
|
5687
|
+
_sortChildren(cl[i].children);
|
|
5688
|
+
}
|
|
5523
5689
|
}
|
|
5524
5690
|
}
|
|
5525
|
-
|
|
5526
|
-
|
|
5691
|
+
}
|
|
5692
|
+
if (this.children) {
|
|
5693
|
+
_sortChildren(this.children);
|
|
5694
|
+
}
|
|
5695
|
+
this.tree.update(ChangeType.structure);
|
|
5696
|
+
// this.triggerModify("sort"); // TODO
|
|
5697
|
+
}
|
|
5698
|
+
/**
|
|
5699
|
+
* Re-apply current sorting if any (use after lazy load).
|
|
5700
|
+
* Example:
|
|
5701
|
+
* ```js
|
|
5702
|
+
* load: function (e) {
|
|
5703
|
+
* // Whe loading a lazy branch, apply current sort order if any
|
|
5704
|
+
* e.node.resort();
|
|
5705
|
+
* },
|
|
5706
|
+
* ```
|
|
5707
|
+
* @since 0.14.0
|
|
5708
|
+
*/
|
|
5709
|
+
resort(options = {}) {
|
|
5710
|
+
for (const colDef of this.tree.columns) {
|
|
5711
|
+
if (colDef.sortOrder) {
|
|
5712
|
+
options.colId = colDef.id;
|
|
5713
|
+
options.order = colDef.sortOrder;
|
|
5714
|
+
this.sort(options);
|
|
5715
|
+
break;
|
|
5527
5716
|
}
|
|
5528
|
-
|
|
5529
|
-
};
|
|
5530
|
-
return this.sortChildren(cmp, deep);
|
|
5717
|
+
}
|
|
5531
5718
|
}
|
|
5532
5719
|
/**
|
|
5533
5720
|
* Trigger `modifyChild` event on a parent to signal that a child was modified.
|
|
@@ -5564,7 +5751,8 @@
|
|
|
5564
5751
|
* @param {function} callback the callback function.
|
|
5565
5752
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
5566
5753
|
* its children only.
|
|
5567
|
-
* @see
|
|
5754
|
+
* @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
|
|
5755
|
+
* @see {@link Wunderbaum.visit}.
|
|
5568
5756
|
*/
|
|
5569
5757
|
visit(callback, includeSelf = false) {
|
|
5570
5758
|
let res = true;
|
|
@@ -5637,7 +5825,7 @@
|
|
|
5637
5825
|
/*!
|
|
5638
5826
|
* Wunderbaum - ext-edit
|
|
5639
5827
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
5640
|
-
* v0.
|
|
5828
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
5641
5829
|
*/
|
|
5642
5830
|
// const START_MARKER = "\uFFF7";
|
|
5643
5831
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5872,7 +6060,7 @@
|
|
|
5872
6060
|
newValue = newValue.trim();
|
|
5873
6061
|
}
|
|
5874
6062
|
if (!node) {
|
|
5875
|
-
this.tree.logDebug("stopEditTitle: not in edit mode.");
|
|
6063
|
+
// this.tree.logDebug("stopEditTitle: not in edit mode.");
|
|
5876
6064
|
return;
|
|
5877
6065
|
}
|
|
5878
6066
|
node.logDebug(`stopEditTitle(${apply})`, options, focusElem, newValue);
|
|
@@ -5972,8 +6160,8 @@
|
|
|
5972
6160
|
* https://github.com/mar10/wunderbaum
|
|
5973
6161
|
*
|
|
5974
6162
|
* Released under the MIT license.
|
|
5975
|
-
* @version v0.
|
|
5976
|
-
* @date
|
|
6163
|
+
* @version v0.14.1
|
|
6164
|
+
* @date Sun, 22 Mar 2026 05:52:05 GMT
|
|
5977
6165
|
*/
|
|
5978
6166
|
// import "./wunderbaum.scss";
|
|
5979
6167
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -6022,17 +6210,17 @@
|
|
|
6022
6210
|
this._disableUpdateIgnoreCount = 0;
|
|
6023
6211
|
this._activeNode = null;
|
|
6024
6212
|
this._focusNode = null;
|
|
6213
|
+
this._initialSource = null;
|
|
6025
6214
|
/** Shared properties, referenced by `node.type`. */
|
|
6026
6215
|
this.types = {};
|
|
6027
6216
|
/** List of column definitions. */
|
|
6028
|
-
this.columns = [];
|
|
6217
|
+
this.columns = [];
|
|
6029
6218
|
this._columnsById = {};
|
|
6030
6219
|
// Modification Status
|
|
6031
6220
|
this.pendingChangeTypes = new Set();
|
|
6032
6221
|
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
6033
6222
|
this._util = util;
|
|
6034
6223
|
// --- SELECT ---
|
|
6035
|
-
// /** @internal */
|
|
6036
6224
|
// public selectRangeAnchor: WunderbaumNode | null = null;
|
|
6037
6225
|
// --- BREADCRUMB ---
|
|
6038
6226
|
/** Filter options (used as defaults for calls to {@link Wunderbaum.filterNodes} ) */
|
|
@@ -6051,37 +6239,44 @@
|
|
|
6051
6239
|
this.lastQuicksearchTerm = "";
|
|
6052
6240
|
// --- EDIT ---
|
|
6053
6241
|
this.lastClickTime = 0;
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6242
|
+
// Set default options and merge with user options
|
|
6243
|
+
const initOptions = Object.assign({
|
|
6244
|
+
id: undefined,
|
|
6245
|
+
source: [], // URL for GET/PUT, Ajax options, or callback
|
|
6246
|
+
element: unsafeCast(null),
|
|
6058
6247
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
6059
6248
|
header: null, // Show/hide header (pass bool or string)
|
|
6060
|
-
// headerHeightPx: ROW_HEIGHT,
|
|
6061
6249
|
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
6062
6250
|
iconMap: "bootstrap",
|
|
6063
|
-
columns: null,
|
|
6064
|
-
types:
|
|
6065
|
-
// escapeTitles: true,
|
|
6251
|
+
columns: [], //util.unsafeCast<ColumnDefinitionList>(null),
|
|
6252
|
+
types: {},
|
|
6066
6253
|
enabled: true,
|
|
6067
6254
|
fixedCol: false,
|
|
6068
6255
|
showSpinner: false,
|
|
6069
6256
|
checkbox: false,
|
|
6070
6257
|
minExpandLevel: 0,
|
|
6071
6258
|
emptyChildListExpandable: false,
|
|
6072
|
-
// updateThrottleWait: 200,
|
|
6073
6259
|
skeleton: false,
|
|
6260
|
+
autoCollapse: false,
|
|
6261
|
+
adjustHeight: true,
|
|
6074
6262
|
connectTopBreadcrumb: null,
|
|
6263
|
+
columnsFilterable: false,
|
|
6264
|
+
columnsMenu: false,
|
|
6265
|
+
columnsResizable: false,
|
|
6266
|
+
columnsSortable: false,
|
|
6075
6267
|
selectMode: "multi", // SelectModeType
|
|
6268
|
+
scrollIntoViewOnExpandClick: true,
|
|
6269
|
+
// --- Extensions (actually set by exensions on init)
|
|
6270
|
+
dnd: unsafeCast(null),
|
|
6271
|
+
edit: unsafeCast(null),
|
|
6272
|
+
filter: unsafeCast(null),
|
|
6076
6273
|
// --- KeyNav ---
|
|
6077
|
-
navigationModeOption: null,
|
|
6274
|
+
navigationModeOption: unsafeCast(null),
|
|
6078
6275
|
quicksearch: true,
|
|
6079
6276
|
// --- Events ---
|
|
6080
|
-
iconBadge: null,
|
|
6081
|
-
change: null,
|
|
6082
|
-
//
|
|
6083
|
-
error: null,
|
|
6084
|
-
receive: null,
|
|
6277
|
+
// iconBadge: null,
|
|
6278
|
+
// change: null,
|
|
6279
|
+
// ...
|
|
6085
6280
|
// --- Strings ---
|
|
6086
6281
|
strings: {
|
|
6087
6282
|
loadError: "Error",
|
|
@@ -6092,7 +6287,9 @@
|
|
|
6092
6287
|
noMatch: "No results",
|
|
6093
6288
|
matchIndex: "${match} of ${matches}",
|
|
6094
6289
|
},
|
|
6095
|
-
}, options)
|
|
6290
|
+
}, options);
|
|
6291
|
+
const opts = initOptions;
|
|
6292
|
+
this.options = opts;
|
|
6096
6293
|
const readyDeferred = new Deferred();
|
|
6097
6294
|
this.ready = readyDeferred.promise();
|
|
6098
6295
|
let readyOk = false;
|
|
@@ -6119,7 +6316,8 @@
|
|
|
6119
6316
|
this._callEvent("init", { error: err });
|
|
6120
6317
|
}
|
|
6121
6318
|
});
|
|
6122
|
-
this.id =
|
|
6319
|
+
this.id = initOptions.id || "wb_" + ++Wunderbaum.sequence;
|
|
6320
|
+
delete initOptions.id;
|
|
6123
6321
|
this.root = new WbSystemRoot(this);
|
|
6124
6322
|
this._registerExtension(new KeynavExtension(this));
|
|
6125
6323
|
this._registerExtension(new EditExtension(this));
|
|
@@ -6129,19 +6327,20 @@
|
|
|
6129
6327
|
this._registerExtension(new LoggerExtension(this));
|
|
6130
6328
|
this._updateViewportThrottled = adaptiveThrottle(this._updateViewportImmediately.bind(this), {});
|
|
6131
6329
|
// --- Evaluate options
|
|
6132
|
-
this.columns =
|
|
6133
|
-
delete
|
|
6330
|
+
this.columns = initOptions.columns || [];
|
|
6331
|
+
delete initOptions.columns;
|
|
6134
6332
|
if (!this.columns || !this.columns.length) {
|
|
6135
6333
|
const title = typeof opts.header === "string" ? opts.header : this.id;
|
|
6136
6334
|
this.columns = [{ id: "*", title: title, width: "*" }];
|
|
6137
6335
|
}
|
|
6138
|
-
if (
|
|
6139
|
-
this.setTypes(
|
|
6336
|
+
if (initOptions.types) {
|
|
6337
|
+
this.setTypes(initOptions.types, true);
|
|
6140
6338
|
}
|
|
6141
|
-
delete
|
|
6339
|
+
delete initOptions.types;
|
|
6142
6340
|
// --- Create Markup
|
|
6143
|
-
this.element = elemFromSelector(
|
|
6144
|
-
assert(!!this.element, `Invalid 'element' option: ${
|
|
6341
|
+
this.element = elemFromSelector(initOptions.element);
|
|
6342
|
+
assert(!!this.element, `Invalid 'element' option: ${initOptions.element}`);
|
|
6343
|
+
delete initOptions.element;
|
|
6145
6344
|
this.element.classList.add("wunderbaum");
|
|
6146
6345
|
if (!this.element.getAttribute("tabindex")) {
|
|
6147
6346
|
this.element.tabIndex = 0;
|
|
@@ -6217,11 +6416,11 @@
|
|
|
6217
6416
|
}
|
|
6218
6417
|
});
|
|
6219
6418
|
// --- Load initial data
|
|
6220
|
-
if (
|
|
6419
|
+
if (initOptions.source) {
|
|
6221
6420
|
if (opts.showSpinner) {
|
|
6222
6421
|
this.nodeListElement.innerHTML = `<progress class='spinner'>${opts.strings.loading}</progress>`;
|
|
6223
6422
|
}
|
|
6224
|
-
this.load(
|
|
6423
|
+
this.load(initOptions.source)
|
|
6225
6424
|
.then(() => {
|
|
6226
6425
|
// The source may have defined columns, so we may adjust the nav mode
|
|
6227
6426
|
if (opts.navigationModeOption == null) {
|
|
@@ -6254,15 +6453,18 @@
|
|
|
6254
6453
|
// has a wrong value at start???
|
|
6255
6454
|
this.update(ChangeType.any);
|
|
6256
6455
|
// --- Bind listeners
|
|
6257
|
-
this.
|
|
6258
|
-
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
6259
|
-
this.update(ChangeType.scroll);
|
|
6260
|
-
});
|
|
6456
|
+
this._registerEventHandlers();
|
|
6261
6457
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
6262
6458
|
// this.log("ResizeObserver: Size changed", entries);
|
|
6263
6459
|
this.update(ChangeType.resize);
|
|
6264
6460
|
});
|
|
6265
6461
|
this.resizeObserver.observe(this.element);
|
|
6462
|
+
}
|
|
6463
|
+
_registerEventHandlers() {
|
|
6464
|
+
this.element.addEventListener("scroll", (e) => {
|
|
6465
|
+
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
6466
|
+
this.update(ChangeType.scroll);
|
|
6467
|
+
});
|
|
6266
6468
|
onEvent(this.element, "click", ".wb-button,.wb-col-icon", (e) => {
|
|
6267
6469
|
var _a, _b;
|
|
6268
6470
|
const info = Wunderbaum.getEventInfo(e);
|
|
@@ -6278,9 +6480,6 @@
|
|
|
6278
6480
|
const node = info.node;
|
|
6279
6481
|
const mouseEvent = e;
|
|
6280
6482
|
// this.log("click", info);
|
|
6281
|
-
// if (this._selectRange(info) === false) {
|
|
6282
|
-
// return;
|
|
6283
|
-
// }
|
|
6284
6483
|
if (this._callEvent("click", { event: e, node: node, info: info }) === false) {
|
|
6285
6484
|
this.lastClickTime = Date.now();
|
|
6286
6485
|
return false;
|
|
@@ -6299,20 +6498,22 @@
|
|
|
6299
6498
|
(!slowClickDelay || Date.now() - this.lastClickTime < slowClickDelay)) {
|
|
6300
6499
|
node.startEditTitle();
|
|
6301
6500
|
}
|
|
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
6501
|
if (info.region === NodeRegion.expander) {
|
|
6309
6502
|
node.setExpanded(!node.isExpanded(), {
|
|
6310
|
-
scrollIntoView: options.scrollIntoViewOnExpandClick !== false,
|
|
6503
|
+
scrollIntoView: this.options.scrollIntoViewOnExpandClick !== false,
|
|
6311
6504
|
});
|
|
6312
6505
|
}
|
|
6313
6506
|
else if (info.region === NodeRegion.checkbox) {
|
|
6314
6507
|
node.toggleSelected();
|
|
6315
6508
|
}
|
|
6509
|
+
else {
|
|
6510
|
+
if (info.colIdx >= 0) {
|
|
6511
|
+
node.setActive(true, { colIdx: info.colIdx, event: e });
|
|
6512
|
+
}
|
|
6513
|
+
else {
|
|
6514
|
+
node.setActive(true, { event: e });
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6316
6517
|
}
|
|
6317
6518
|
this.lastClickTime = Date.now();
|
|
6318
6519
|
});
|
|
@@ -6348,7 +6549,7 @@
|
|
|
6348
6549
|
const targetNode = Wunderbaum.getNode(e);
|
|
6349
6550
|
this._callEvent("focus", { flag: flag, event: e });
|
|
6350
6551
|
if (flag && this.isRowNav() && !this.isEditingTitle()) {
|
|
6351
|
-
if (
|
|
6552
|
+
if (this.options.navigationModeOption === NavModeEnum.row) {
|
|
6352
6553
|
targetNode === null || targetNode === void 0 ? void 0 : targetNode.setActive();
|
|
6353
6554
|
}
|
|
6354
6555
|
else {
|
|
@@ -6415,11 +6616,12 @@
|
|
|
6415
6616
|
}
|
|
6416
6617
|
/**
|
|
6417
6618
|
* Return the icon-function -> icon-definition mapping.
|
|
6619
|
+
* @deprecated Use {@link Wunderbaum.iconMaps}
|
|
6418
6620
|
*/
|
|
6419
6621
|
get iconMap() {
|
|
6420
6622
|
const map = this.options.iconMap;
|
|
6421
6623
|
if (typeof map === "string") {
|
|
6422
|
-
return
|
|
6624
|
+
return defaultIconMaps[map];
|
|
6423
6625
|
}
|
|
6424
6626
|
return map;
|
|
6425
6627
|
}
|
|
@@ -6472,7 +6674,38 @@
|
|
|
6472
6674
|
ext.init();
|
|
6473
6675
|
}
|
|
6474
6676
|
}
|
|
6475
|
-
/**
|
|
6677
|
+
/**
|
|
6678
|
+
* Calculate a *stable*, unique key for a node from its refKey (or title).
|
|
6679
|
+
* We also add information from the parent, because a refKey may occur multiple
|
|
6680
|
+
* times in a tree (but not as child of the same parent).
|
|
6681
|
+
* @internal
|
|
6682
|
+
*/
|
|
6683
|
+
_calculateKey(data, parent) {
|
|
6684
|
+
if (data.key) {
|
|
6685
|
+
// Always use an explicitly passed key
|
|
6686
|
+
return data.key;
|
|
6687
|
+
}
|
|
6688
|
+
// Auto-keys are optional, use a monotonic counter by default:
|
|
6689
|
+
if (!this.options.autoKeys) {
|
|
6690
|
+
return "" + ++WunderbaumNode.sequence;
|
|
6691
|
+
}
|
|
6692
|
+
// Add the parent's key to the hash. Assuming this was generated by the
|
|
6693
|
+
// same algorithm, this should incorporate the whole path:
|
|
6694
|
+
const s = (parent ? parent.key : "") + (data.refKey || data.title);
|
|
6695
|
+
// 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
6696
|
+
// https://security.stackexchange.com/q/209882/207588
|
|
6697
|
+
const h1 = murmurHash3(s, true);
|
|
6698
|
+
let key = "id_" + h1 + murmurHash3(h1 + s, true);
|
|
6699
|
+
// Check for collisions
|
|
6700
|
+
// (Most likely if the same title occurs multiple in the same parent).
|
|
6701
|
+
const existingNode = this.keyMap.get(key);
|
|
6702
|
+
if (existingNode) {
|
|
6703
|
+
key += "." + ++Wunderbaum.sequence;
|
|
6704
|
+
this.logWarn(`Node with existing key: '${existingNode}', using ${key}.`, data);
|
|
6705
|
+
}
|
|
6706
|
+
return key;
|
|
6707
|
+
}
|
|
6708
|
+
/** Add node to tree's bookkeeping data structures. @internal */
|
|
6476
6709
|
_registerNode(node) {
|
|
6477
6710
|
const key = node.key;
|
|
6478
6711
|
assert(key != null, `Missing key: '${node}'.`);
|
|
@@ -6489,7 +6722,7 @@
|
|
|
6489
6722
|
}
|
|
6490
6723
|
}
|
|
6491
6724
|
}
|
|
6492
|
-
/** Remove node from tree's bookkeeping data structures. */
|
|
6725
|
+
/** Remove node from tree's bookkeeping data structures. @internal */
|
|
6493
6726
|
_unregisterNode(node) {
|
|
6494
6727
|
// Remove refKey reference from map (if any)
|
|
6495
6728
|
const rk = node.refKey;
|
|
@@ -6608,6 +6841,16 @@
|
|
|
6608
6841
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6609
6842
|
return this._getNodeByRowIdx(bottomIdx);
|
|
6610
6843
|
}
|
|
6844
|
+
/** Return preceding visible node in the viewport. */
|
|
6845
|
+
_getPrevNodeInView(node, ofs = 1) {
|
|
6846
|
+
this.visitRows((n) => {
|
|
6847
|
+
node = n;
|
|
6848
|
+
if (ofs-- <= 0) {
|
|
6849
|
+
return false;
|
|
6850
|
+
}
|
|
6851
|
+
}, { reverse: true, start: node || this.getActiveNode() });
|
|
6852
|
+
return node;
|
|
6853
|
+
}
|
|
6611
6854
|
/** Return following visible node in the viewport. */
|
|
6612
6855
|
_getNextNodeInView(node, options) {
|
|
6613
6856
|
let ofs = (options === null || options === void 0 ? void 0 : options.ofs) || 1;
|
|
@@ -6859,22 +7102,39 @@
|
|
|
6859
7102
|
/** Run code, but defer rendering of viewport until done.
|
|
6860
7103
|
*
|
|
6861
7104
|
* ```js
|
|
6862
|
-
* tree.runWithDeferredUpdate(() => {
|
|
6863
|
-
* return
|
|
7105
|
+
* const res = tree.runWithDeferredUpdate(() => {
|
|
7106
|
+
* return someFunctionThatWouldUpdateManyNodes();
|
|
6864
7107
|
* });
|
|
6865
7108
|
* ```
|
|
6866
7109
|
*/
|
|
6867
|
-
runWithDeferredUpdate(func
|
|
7110
|
+
runWithDeferredUpdate(func) {
|
|
6868
7111
|
try {
|
|
6869
7112
|
this.enableUpdate(false);
|
|
6870
7113
|
const res = func();
|
|
6871
|
-
assert(!(res instanceof Promise), `Promise return not allowed: ${res}`);
|
|
7114
|
+
assert(!(res instanceof Promise), `Promise return not allowed (see 'runWithDeferredUpdateAsync()'): ${res}`);
|
|
6872
7115
|
return res;
|
|
6873
7116
|
}
|
|
6874
7117
|
finally {
|
|
6875
7118
|
this.enableUpdate(true);
|
|
6876
7119
|
}
|
|
6877
7120
|
}
|
|
7121
|
+
/** Run code, but defer rendering of viewport until done.
|
|
7122
|
+
*
|
|
7123
|
+
* ```js
|
|
7124
|
+
* const res = await tree.runWithDeferredUpdate(async () => {
|
|
7125
|
+
* return someAsyncFunctionThatWouldUpdateManyNodes();
|
|
7126
|
+
* });
|
|
7127
|
+
* ```
|
|
7128
|
+
*/
|
|
7129
|
+
async runWithDeferredUpdateAsync(func) {
|
|
7130
|
+
try {
|
|
7131
|
+
this.enableUpdate(false);
|
|
7132
|
+
return await func();
|
|
7133
|
+
}
|
|
7134
|
+
finally {
|
|
7135
|
+
this.enableUpdate(true);
|
|
7136
|
+
}
|
|
7137
|
+
}
|
|
6878
7138
|
/** Recursively expand all expandable nodes (triggers lazy load if needed). */
|
|
6879
7139
|
async expandAll(flag = true, options) {
|
|
6880
7140
|
await this.root.expandAll(flag, options);
|
|
@@ -6894,6 +7154,17 @@
|
|
|
6894
7154
|
getSelectedNodes(stopOnParents = false) {
|
|
6895
7155
|
return this.root.getSelectedNodes(stopOnParents);
|
|
6896
7156
|
}
|
|
7157
|
+
/**
|
|
7158
|
+
* Return an array of refKey values.
|
|
7159
|
+
*
|
|
7160
|
+
* RefKeys are unique identifiers for a node data, and are used to identify
|
|
7161
|
+
* clones.
|
|
7162
|
+
* If more than one node has the same refKey, it is only returned once.
|
|
7163
|
+
* @param selected if true, only return refKeys of selected nodes.
|
|
7164
|
+
*/
|
|
7165
|
+
getRefKeys(selected = false) {
|
|
7166
|
+
return this.root.getRefKeys(selected);
|
|
7167
|
+
}
|
|
6897
7168
|
/*
|
|
6898
7169
|
* Return an array of selected nodes.
|
|
6899
7170
|
*/
|
|
@@ -7175,6 +7446,18 @@
|
|
|
7175
7446
|
format(name_cb, connectors) {
|
|
7176
7447
|
return this.root.format(name_cb, connectors);
|
|
7177
7448
|
}
|
|
7449
|
+
/**
|
|
7450
|
+
* Always returns null (so a tree instance behaves as `tree.root`).
|
|
7451
|
+
*/
|
|
7452
|
+
get parent() {
|
|
7453
|
+
return null;
|
|
7454
|
+
}
|
|
7455
|
+
/**
|
|
7456
|
+
* Return a list of top-level nodes.
|
|
7457
|
+
*/
|
|
7458
|
+
get children() {
|
|
7459
|
+
return this.root.children || [];
|
|
7460
|
+
}
|
|
7178
7461
|
/**
|
|
7179
7462
|
* Return the active cell (`span.wb-col`) of the currently active node or null.
|
|
7180
7463
|
*/
|
|
@@ -7293,7 +7576,7 @@
|
|
|
7293
7576
|
}
|
|
7294
7577
|
/** Return true if any node title or grid cell is currently beeing edited.
|
|
7295
7578
|
*
|
|
7296
|
-
* See also {@link
|
|
7579
|
+
* See also {@link isEditingTitle}.
|
|
7297
7580
|
*/
|
|
7298
7581
|
isEditing() {
|
|
7299
7582
|
const focusElem = this.nodeListElement.querySelector("input:focus,select:focus");
|
|
@@ -7301,7 +7584,7 @@
|
|
|
7301
7584
|
}
|
|
7302
7585
|
/** Return true if any node is currently in edit-title mode.
|
|
7303
7586
|
*
|
|
7304
|
-
* See also {@link WunderbaumNode.isEditingTitle} and {@link
|
|
7587
|
+
* See also {@link WunderbaumNode.isEditingTitle} and {@link isEditing}.
|
|
7305
7588
|
*/
|
|
7306
7589
|
isEditingTitle() {
|
|
7307
7590
|
return this._callMethod("edit.isEditingTitle");
|
|
@@ -7321,7 +7604,7 @@
|
|
|
7321
7604
|
return res;
|
|
7322
7605
|
}
|
|
7323
7606
|
/** Write to `console.log` with tree name as prefix if opts.debugLevel >= 4.
|
|
7324
|
-
* @see {@link
|
|
7607
|
+
* @see {@link logDebug}
|
|
7325
7608
|
*/
|
|
7326
7609
|
log(...args) {
|
|
7327
7610
|
if (this.options.debugLevel >= 4) {
|
|
@@ -7330,7 +7613,7 @@
|
|
|
7330
7613
|
}
|
|
7331
7614
|
/** Write to `console.debug` with tree name as prefix if opts.debugLevel >= 4.
|
|
7332
7615
|
* and browser console level includes debug/verbose messages.
|
|
7333
|
-
* @see {@link
|
|
7616
|
+
* @see {@link log}
|
|
7334
7617
|
*/
|
|
7335
7618
|
logDebug(...args) {
|
|
7336
7619
|
if (this.options.debugLevel >= 4) {
|
|
@@ -7368,6 +7651,19 @@
|
|
|
7368
7651
|
console.warn(this.toString(), ...args); // eslint-disable-line no-console
|
|
7369
7652
|
}
|
|
7370
7653
|
}
|
|
7654
|
+
/** Emit a warning for deprecated methods. @internal */
|
|
7655
|
+
logDeprecate(method, options) {
|
|
7656
|
+
if (this.options.debugLevel >= 2) {
|
|
7657
|
+
let msg = `${this}: ${method} is deprecated`;
|
|
7658
|
+
if (options === null || options === void 0 ? void 0 : options.since) {
|
|
7659
|
+
msg += ` since ${options.since}`;
|
|
7660
|
+
}
|
|
7661
|
+
if (options === null || options === void 0 ? void 0 : options.hint) {
|
|
7662
|
+
msg += ` (${options.since})`;
|
|
7663
|
+
}
|
|
7664
|
+
console.warn(msg + "."); // eslint-disable-line no-console
|
|
7665
|
+
}
|
|
7666
|
+
}
|
|
7371
7667
|
/** Reset column widths to default. @since 0.10.0 */
|
|
7372
7668
|
resetColumns() {
|
|
7373
7669
|
this.columns.forEach((col) => {
|
|
@@ -7536,46 +7832,64 @@
|
|
|
7536
7832
|
this._focusNode = node;
|
|
7537
7833
|
}
|
|
7538
7834
|
/** Return the current selection/expansion/activation status. @experimental */
|
|
7539
|
-
getState(options) {
|
|
7835
|
+
getState(options = {}) {
|
|
7540
7836
|
var _a, _b;
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7837
|
+
const { activeKey = true, expandedKeys = false, selectedKeys = false, } = options;
|
|
7838
|
+
const expandSet = new Set();
|
|
7839
|
+
if (expandedKeys) {
|
|
7544
7840
|
for (const node of this) {
|
|
7545
|
-
if (node.
|
|
7546
|
-
|
|
7841
|
+
if (node.isExpanded() && node.hasChildren()) {
|
|
7842
|
+
expandSet.add(node.key);
|
|
7547
7843
|
}
|
|
7548
7844
|
}
|
|
7549
7845
|
}
|
|
7846
|
+
// Parents of active node are always expanded
|
|
7847
|
+
if (activeKey && this.activeNode) {
|
|
7848
|
+
this.activeNode.visitParents((n) => {
|
|
7849
|
+
if (n.parent) {
|
|
7850
|
+
expandSet.add(n.key);
|
|
7851
|
+
}
|
|
7852
|
+
}, false);
|
|
7853
|
+
}
|
|
7550
7854
|
const state = {
|
|
7855
|
+
expandedKeys: expandSet.size ? Array.from(expandSet) : undefined,
|
|
7551
7856
|
activeKey: (_b = (_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : null,
|
|
7552
7857
|
activeColIdx: this.activeColIdx,
|
|
7553
|
-
selectedKeys:
|
|
7554
|
-
?
|
|
7555
|
-
:
|
|
7556
|
-
expandedKeys: expandedKeys,
|
|
7858
|
+
selectedKeys: selectedKeys
|
|
7859
|
+
? this.getSelectedNodes().flatMap((n) => n.key)
|
|
7860
|
+
: undefined,
|
|
7557
7861
|
};
|
|
7558
7862
|
return state;
|
|
7559
7863
|
}
|
|
7560
7864
|
/** Apply selection/expansion/activation status. @experimental */
|
|
7561
|
-
setState(state, options) {
|
|
7562
|
-
|
|
7865
|
+
async setState(state, options = {}) {
|
|
7866
|
+
const { expandLazy = true } = options;
|
|
7867
|
+
return this.runWithDeferredUpdateAsync(async () => {
|
|
7563
7868
|
var _a, _b;
|
|
7564
|
-
if (state.
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7869
|
+
if (state.expandedKeys && state.expandedKeys.length) {
|
|
7870
|
+
if (expandLazy) {
|
|
7871
|
+
// Expand all keys recursively, even if they are not in the tree yet
|
|
7872
|
+
await this._loadLazyNodes(state.expandedKeys, {
|
|
7873
|
+
expand: true,
|
|
7874
|
+
noEvents: true,
|
|
7875
|
+
});
|
|
7568
7876
|
}
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7877
|
+
else {
|
|
7878
|
+
for (const key of state.expandedKeys) {
|
|
7879
|
+
(_a = this.findKey(key)) === null || _a === void 0 ? void 0 : _a.setExpanded(true);
|
|
7880
|
+
}
|
|
7573
7881
|
}
|
|
7574
7882
|
}
|
|
7575
7883
|
if (state.activeKey) {
|
|
7576
7884
|
this.setActiveNode(state.activeKey);
|
|
7577
7885
|
}
|
|
7578
|
-
if (state.
|
|
7886
|
+
if (state.selectedKeys) {
|
|
7887
|
+
this.selectAll(false);
|
|
7888
|
+
for (const key of state.selectedKeys) {
|
|
7889
|
+
(_b = this.findKey(key)) === null || _b === void 0 ? void 0 : _b.setSelected(true);
|
|
7890
|
+
}
|
|
7891
|
+
}
|
|
7892
|
+
if (this.isCellNav() && state.activeColIdx != null) {
|
|
7579
7893
|
this.setColumn(state.activeColIdx);
|
|
7580
7894
|
}
|
|
7581
7895
|
});
|
|
@@ -7736,18 +8050,33 @@
|
|
|
7736
8050
|
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
7737
8051
|
* (defaults to sorting by title).
|
|
7738
8052
|
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
8053
|
+
* @deprecated use {@link sort}
|
|
7739
8054
|
*/
|
|
7740
8055
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
7741
|
-
this.
|
|
8056
|
+
this.logDeprecate("sortChildren()", { since: "0.14.0" });
|
|
8057
|
+
return this.sort({
|
|
8058
|
+
cmp: cmp ? cmp : undefined,
|
|
8059
|
+
deep: deep,
|
|
8060
|
+
propName: "title",
|
|
8061
|
+
});
|
|
7742
8062
|
}
|
|
7743
8063
|
/**
|
|
7744
8064
|
* Convenience method to implement column sorting.
|
|
7745
8065
|
* @see {@link WunderbaumNode.sortByProperty}.
|
|
7746
8066
|
* @since 0.11.0
|
|
8067
|
+
* @deprecated use {@link sort}
|
|
7747
8068
|
*/
|
|
7748
8069
|
sortByProperty(options) {
|
|
8070
|
+
this.logDeprecate("sortByProperty()", { since: "0.14.0" });
|
|
7749
8071
|
this.root.sortByProperty(options);
|
|
7750
8072
|
}
|
|
8073
|
+
/**
|
|
8074
|
+
* Sort nodes list by title or custom criteria.
|
|
8075
|
+
* @since 0.14.0
|
|
8076
|
+
*/
|
|
8077
|
+
sort(options) {
|
|
8078
|
+
this.root.sort(options);
|
|
8079
|
+
}
|
|
7751
8080
|
/** Convert tree to an array of plain objects.
|
|
7752
8081
|
*
|
|
7753
8082
|
* @param callback is called for every node, in order to allow
|
|
@@ -7999,12 +8328,10 @@
|
|
|
7999
8328
|
iconElem = document.createElement("i");
|
|
8000
8329
|
iconElem.className = "wb-icon";
|
|
8001
8330
|
}
|
|
8002
|
-
else if (
|
|
8003
|
-
// HTML
|
|
8331
|
+
else if (TEST_HTML.test(icon)) {
|
|
8004
8332
|
iconElem = elemFromHtml(icon);
|
|
8005
8333
|
}
|
|
8006
|
-
else if (
|
|
8007
|
-
// Image URL
|
|
8334
|
+
else if (TEST_FILE_PATH.test(icon)) {
|
|
8008
8335
|
iconElem = elemFromHtml(`<i class="wb-icon" style="background-image: url('${icon}');">`);
|
|
8009
8336
|
}
|
|
8010
8337
|
else {
|
|
@@ -8242,7 +8569,8 @@
|
|
|
8242
8569
|
}
|
|
8243
8570
|
/**
|
|
8244
8571
|
* Call `callback(node)` for all nodes in hierarchical order (depth-first, pre-order).
|
|
8245
|
-
* @see
|
|
8572
|
+
* @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
|
|
8573
|
+
* @see {@link WunderbaumNode.visit}.
|
|
8246
8574
|
*
|
|
8247
8575
|
* @param {function} callback the callback function.
|
|
8248
8576
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
@@ -8385,11 +8713,71 @@
|
|
|
8385
8713
|
*
|
|
8386
8714
|
* Previous data is cleared. Note that also column- and type defintions may
|
|
8387
8715
|
* be passed with the `source` object.
|
|
8716
|
+
* @see {@link Wunderbaum.reload} for a shortcut to reload the last ajax request
|
|
8717
|
+
* and restore the previous state.
|
|
8388
8718
|
*/
|
|
8389
|
-
load(source) {
|
|
8719
|
+
async load(source) {
|
|
8390
8720
|
this.clear();
|
|
8721
|
+
this._initialSource = source;
|
|
8391
8722
|
return this.root.load(source);
|
|
8392
8723
|
}
|
|
8724
|
+
/** Reload the tree and optionally restore state.
|
|
8725
|
+
* Source defaults to last ajax url if any.
|
|
8726
|
+
* Restoring the active node requires stable keys
|
|
8727
|
+
* @see {@link WunderbaumOptions.autoKeys}
|
|
8728
|
+
* @see {@link Wunderbaum.load}
|
|
8729
|
+
* @experimental
|
|
8730
|
+
*/
|
|
8731
|
+
async reload(options = {}) {
|
|
8732
|
+
const { source = this._initialSource, reactivate = true } = options;
|
|
8733
|
+
if (!source) {
|
|
8734
|
+
this.logWarn("No previous ajax source to reload.");
|
|
8735
|
+
return;
|
|
8736
|
+
}
|
|
8737
|
+
if (!reactivate) {
|
|
8738
|
+
return this.load(source);
|
|
8739
|
+
}
|
|
8740
|
+
const state = this.getState();
|
|
8741
|
+
await this.load(source);
|
|
8742
|
+
return this.setState(state);
|
|
8743
|
+
}
|
|
8744
|
+
/**
|
|
8745
|
+
* Make sure that all nodes in the given keyList are accessible.
|
|
8746
|
+
* This may include loading lazy parent nodes.
|
|
8747
|
+
* Recursively load (and optionally expand) all requested node paths.
|
|
8748
|
+
*/
|
|
8749
|
+
async _loadLazyNodes(keyList, options = {}) {
|
|
8750
|
+
const { expand = true } = options;
|
|
8751
|
+
const keySet = new Set(keyList);
|
|
8752
|
+
// Make sure that all parent nodes are loaded (and expand if requested)
|
|
8753
|
+
while (keySet.size > 0) {
|
|
8754
|
+
const pendingNodes = [];
|
|
8755
|
+
const curSet = new Set(keySet);
|
|
8756
|
+
for (const key of curSet) {
|
|
8757
|
+
const node = this.findKey(key);
|
|
8758
|
+
if (!node) {
|
|
8759
|
+
continue; // key not yet found (need to load lazy parent?)
|
|
8760
|
+
}
|
|
8761
|
+
keySet.delete(key);
|
|
8762
|
+
if (expand) {
|
|
8763
|
+
pendingNodes.push(node.setExpanded(true));
|
|
8764
|
+
}
|
|
8765
|
+
else if (node.isUnloaded()) {
|
|
8766
|
+
pendingNodes.push(node.loadLazy());
|
|
8767
|
+
}
|
|
8768
|
+
if (node._rowElem) {
|
|
8769
|
+
node._render(); // show spinner even is update is suppressed
|
|
8770
|
+
}
|
|
8771
|
+
}
|
|
8772
|
+
if (pendingNodes.length === 0) {
|
|
8773
|
+
// will not load any more nodes, so if if there are still keys
|
|
8774
|
+
// left in the set, we will never find them
|
|
8775
|
+
this.logWarn(`Could not expand ${keySet.size} nodes:`, keySet);
|
|
8776
|
+
break;
|
|
8777
|
+
}
|
|
8778
|
+
await Promise.allSettled(pendingNodes);
|
|
8779
|
+
}
|
|
8780
|
+
}
|
|
8393
8781
|
/**
|
|
8394
8782
|
* Disable render requests during operations that would trigger many updates.
|
|
8395
8783
|
*
|
|
@@ -8487,9 +8875,21 @@
|
|
|
8487
8875
|
}
|
|
8488
8876
|
Wunderbaum.sequence = 0;
|
|
8489
8877
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8490
|
-
Wunderbaum.version = "v0.
|
|
8878
|
+
Wunderbaum.version = "v0.14.1"; // Set to semver by 'grunt release'
|
|
8491
8879
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8492
8880
|
Wunderbaum.util = util;
|
|
8881
|
+
/** A map of default iconMaps.
|
|
8882
|
+
* May be used as default, when passing partial icon definition maps:
|
|
8883
|
+
* ```js
|
|
8884
|
+
* const tree = new mar10.Wunderbaum({
|
|
8885
|
+
* ...
|
|
8886
|
+
* iconMap: Object.assign(Wunderbaum.iconMaps.bootstrap, {
|
|
8887
|
+
* folder: "bi bi-archive",
|
|
8888
|
+
* }),
|
|
8889
|
+
* });
|
|
8890
|
+
* ```
|
|
8891
|
+
*/
|
|
8892
|
+
Wunderbaum.iconMaps = defaultIconMaps;
|
|
8493
8893
|
|
|
8494
8894
|
exports.Wunderbaum = Wunderbaum;
|
|
8495
8895
|
|