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.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - debounce.ts
|
|
3
3
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
4
|
-
* v0.
|
|
4
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
/*
|
|
7
7
|
* debounce & throttle, taken from https://github.com/lodash/lodash v4.17.21
|
|
@@ -293,7 +293,7 @@ function throttle(func, wait = 0, options = {}) {
|
|
|
293
293
|
/*!
|
|
294
294
|
* Wunderbaum - util
|
|
295
295
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
296
|
-
* v0.
|
|
296
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
297
297
|
*/
|
|
298
298
|
/** @module util */
|
|
299
299
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -443,7 +443,7 @@ function each(obj, callback) {
|
|
|
443
443
|
}
|
|
444
444
|
return obj;
|
|
445
445
|
}
|
|
446
|
-
/** Shortcut for `throw new Error(msg)
|
|
446
|
+
/** Shortcut for `throw new Error(msg)`. */
|
|
447
447
|
function error(msg) {
|
|
448
448
|
throw new Error(msg);
|
|
449
449
|
}
|
|
@@ -958,6 +958,10 @@ function toPixel(...defaults) {
|
|
|
958
958
|
}
|
|
959
959
|
throw new Error(`Expected a string like '123px': ${defaults}`);
|
|
960
960
|
}
|
|
961
|
+
/** Cast any value to <T>. */
|
|
962
|
+
function unsafeCast(value) {
|
|
963
|
+
return value;
|
|
964
|
+
}
|
|
961
965
|
/** Return the the boolean value of the first non-null element.
|
|
962
966
|
* Example:
|
|
963
967
|
* ```js
|
|
@@ -1031,7 +1035,7 @@ function adaptiveThrottle(callback, options) {
|
|
|
1031
1035
|
const throttledFn = (...args) => {
|
|
1032
1036
|
if (waiting) {
|
|
1033
1037
|
pendingArgs = args;
|
|
1034
|
-
// console.log(`adaptiveThrottle()
|
|
1038
|
+
// console.log(`adaptiveThrottle() queueing request #${waiting}...`, args);
|
|
1035
1039
|
waiting += 1;
|
|
1036
1040
|
}
|
|
1037
1041
|
else {
|
|
@@ -1086,6 +1090,60 @@ function adaptiveThrottle(callback, options) {
|
|
|
1086
1090
|
};
|
|
1087
1091
|
return throttledFn;
|
|
1088
1092
|
}
|
|
1093
|
+
/**
|
|
1094
|
+
* MurmurHash3 implementation for strings.
|
|
1095
|
+
* @param key The input string to hash.
|
|
1096
|
+
* @param asString Optional convert result to zero-padded string of 8 characters.
|
|
1097
|
+
* @param seed Optional seed value.
|
|
1098
|
+
* @returns A 32-bit hash as a number or string.
|
|
1099
|
+
*/
|
|
1100
|
+
function murmurHash3(key, asString = true, seed = 0) {
|
|
1101
|
+
let h1 = seed;
|
|
1102
|
+
const remainder = key.length & 3; // key.length % 4
|
|
1103
|
+
const bytes = key.length - remainder;
|
|
1104
|
+
const c1 = 0xcc9e2d51;
|
|
1105
|
+
const c2 = 0x1b873593;
|
|
1106
|
+
let i = 0;
|
|
1107
|
+
while (i < bytes) {
|
|
1108
|
+
let k1 = (key.charCodeAt(i) & 0xff) |
|
|
1109
|
+
((key.charCodeAt(++i) & 0xff) << 8) |
|
|
1110
|
+
((key.charCodeAt(++i) & 0xff) << 16) |
|
|
1111
|
+
((key.charCodeAt(++i) & 0xff) << 24);
|
|
1112
|
+
++i;
|
|
1113
|
+
k1 = Math.imul(k1, c1);
|
|
1114
|
+
k1 = (k1 << 15) | (k1 >>> 17);
|
|
1115
|
+
k1 = Math.imul(k1, c2);
|
|
1116
|
+
h1 ^= k1;
|
|
1117
|
+
h1 = (h1 << 13) | (h1 >>> 19);
|
|
1118
|
+
h1 = Math.imul(h1, 5) + 0xe6546b64;
|
|
1119
|
+
}
|
|
1120
|
+
let k1 = 0;
|
|
1121
|
+
switch (remainder) {
|
|
1122
|
+
case 3:
|
|
1123
|
+
k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
|
|
1124
|
+
// fall through
|
|
1125
|
+
case 2:
|
|
1126
|
+
k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
|
|
1127
|
+
// fall through
|
|
1128
|
+
case 1:
|
|
1129
|
+
k1 ^= key.charCodeAt(i) & 0xff;
|
|
1130
|
+
k1 = Math.imul(k1, c1);
|
|
1131
|
+
k1 = (k1 << 15) | (k1 >>> 17);
|
|
1132
|
+
k1 = Math.imul(k1, c2);
|
|
1133
|
+
h1 ^= k1;
|
|
1134
|
+
}
|
|
1135
|
+
h1 ^= key.length;
|
|
1136
|
+
h1 ^= h1 >>> 16;
|
|
1137
|
+
h1 = Math.imul(h1, 0x85ebca6b);
|
|
1138
|
+
h1 ^= h1 >>> 13;
|
|
1139
|
+
h1 = Math.imul(h1, 0xc2b2ae35);
|
|
1140
|
+
h1 ^= h1 >>> 16;
|
|
1141
|
+
if (asString) {
|
|
1142
|
+
// Convert to 8 digit hex string
|
|
1143
|
+
return (h1 >>> 0).toString(16).padStart(8, "0");
|
|
1144
|
+
}
|
|
1145
|
+
return h1 >>> 0; // Convert to unsigned 32-bit integer
|
|
1146
|
+
}
|
|
1089
1147
|
|
|
1090
1148
|
var util = /*#__PURE__*/Object.freeze({
|
|
1091
1149
|
__proto__: null,
|
|
@@ -1116,6 +1174,7 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
1116
1174
|
isFunction: isFunction,
|
|
1117
1175
|
isMac: isMac,
|
|
1118
1176
|
isPlainObject: isPlainObject,
|
|
1177
|
+
murmurHash3: murmurHash3,
|
|
1119
1178
|
noop: noop,
|
|
1120
1179
|
onEvent: onEvent,
|
|
1121
1180
|
overrideMethod: overrideMethod,
|
|
@@ -1129,13 +1188,14 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
1129
1188
|
toPixel: toPixel,
|
|
1130
1189
|
toSet: toSet,
|
|
1131
1190
|
toggleCheckbox: toggleCheckbox,
|
|
1132
|
-
type: type
|
|
1191
|
+
type: type,
|
|
1192
|
+
unsafeCast: unsafeCast
|
|
1133
1193
|
});
|
|
1134
1194
|
|
|
1135
1195
|
/*!
|
|
1136
1196
|
* Wunderbaum - types
|
|
1137
1197
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1138
|
-
* v0.
|
|
1198
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
1139
1199
|
*/
|
|
1140
1200
|
/**
|
|
1141
1201
|
* Possible values for {@link WunderbaumNode.update} and {@link Wunderbaum.update}.
|
|
@@ -1203,7 +1263,7 @@ var NavModeEnum;
|
|
|
1203
1263
|
/*!
|
|
1204
1264
|
* Wunderbaum - wb_extension_base
|
|
1205
1265
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1206
|
-
* v0.
|
|
1266
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
1207
1267
|
*/
|
|
1208
1268
|
class WunderbaumExtension {
|
|
1209
1269
|
constructor(tree, id, defaults) {
|
|
@@ -1262,7 +1322,7 @@ class WunderbaumExtension {
|
|
|
1262
1322
|
/*!
|
|
1263
1323
|
* Wunderbaum - ext-filter
|
|
1264
1324
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1265
|
-
* v0.
|
|
1325
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
1266
1326
|
*/
|
|
1267
1327
|
const START_MARKER = "\uFFF7";
|
|
1268
1328
|
const END_MARKER = "\uFFF8";
|
|
@@ -1550,6 +1610,10 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1550
1610
|
*/
|
|
1551
1611
|
filterBranches(filter, options) {
|
|
1552
1612
|
assert(options.matchBranch === undefined, "filterBranches() is deprecated.");
|
|
1613
|
+
this.tree.logDeprecate("filterBranches()", {
|
|
1614
|
+
since: "0.9.0",
|
|
1615
|
+
hint: "Use `filterNodes` instead and set `options.matchBranch: true`",
|
|
1616
|
+
});
|
|
1553
1617
|
options.matchBranch = true;
|
|
1554
1618
|
return this._applyFilterNoUpdate(filter, options);
|
|
1555
1619
|
}
|
|
@@ -1614,7 +1678,7 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1614
1678
|
}
|
|
1615
1679
|
}
|
|
1616
1680
|
/**
|
|
1617
|
-
* @description Marks the matching
|
|
1681
|
+
* @description Marks the matching characters of `text` either by `mark` or
|
|
1618
1682
|
* by exotic*Chars (if `escapeTitles` is `true`) based on `matches`
|
|
1619
1683
|
* which is an array of matching groups.
|
|
1620
1684
|
* @param {string} text
|
|
@@ -1653,7 +1717,7 @@ function _markFuzzyMatchedChars(text, matches, escapeTitles = true) {
|
|
|
1653
1717
|
/*!
|
|
1654
1718
|
* Wunderbaum - ext-keynav
|
|
1655
1719
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1656
|
-
* v0.
|
|
1720
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
1657
1721
|
*/
|
|
1658
1722
|
const QUICKSEARCH_DELAY = 500;
|
|
1659
1723
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -2017,7 +2081,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
2017
2081
|
/*!
|
|
2018
2082
|
* Wunderbaum - ext-logger
|
|
2019
2083
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2020
|
-
* v0.
|
|
2084
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
2021
2085
|
*/
|
|
2022
2086
|
class LoggerExtension extends WunderbaumExtension {
|
|
2023
2087
|
constructor(tree) {
|
|
@@ -2059,7 +2123,7 @@ class LoggerExtension extends WunderbaumExtension {
|
|
|
2059
2123
|
/*!
|
|
2060
2124
|
* Wunderbaum - ext-dnd
|
|
2061
2125
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2062
|
-
* v0.
|
|
2126
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
2063
2127
|
*/
|
|
2064
2128
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2065
2129
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -2295,7 +2359,6 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2295
2359
|
*/
|
|
2296
2360
|
onDragEvent(e) {
|
|
2297
2361
|
var _a;
|
|
2298
|
-
// const tree = this.tree;
|
|
2299
2362
|
const dndOpts = this.treeOpts.dnd;
|
|
2300
2363
|
const srcNode = Wunderbaum.getNode(e);
|
|
2301
2364
|
if (!srcNode) {
|
|
@@ -2321,7 +2384,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2321
2384
|
return false;
|
|
2322
2385
|
}
|
|
2323
2386
|
const nodeData = srcNode.toDict(true, (n) => {
|
|
2324
|
-
// We don't want to
|
|
2387
|
+
// We don't want to reuse the key on drop:
|
|
2325
2388
|
n._orgKey = n.key;
|
|
2326
2389
|
delete n.key;
|
|
2327
2390
|
});
|
|
@@ -2383,6 +2446,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2383
2446
|
};
|
|
2384
2447
|
if (!targetNode) {
|
|
2385
2448
|
this._leaveNode();
|
|
2449
|
+
e.preventDefault(); // Don't open file in browser when dropped in empty area
|
|
2386
2450
|
return;
|
|
2387
2451
|
}
|
|
2388
2452
|
if (["drop"].includes(e.type)) {
|
|
@@ -2488,19 +2552,20 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2488
2552
|
nodeData = nodeData ? JSON.parse(nodeData) : null;
|
|
2489
2553
|
const srcNode = this.srcNode;
|
|
2490
2554
|
const lastDropEffect = this.lastDropEffect;
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2555
|
+
/* Before v0.14.0, we decoupled `_callEvent` like so:
|
|
2556
|
+
Decouple this call, because drop actions may prevent the dragend
|
|
2557
|
+
event from being fired on some browsers.
|
|
2558
|
+
setTimeout(() => {...}, 10);
|
|
2559
|
+
however this made e.dataTransfer.items inaccessible */
|
|
2560
|
+
targetNode._callEvent("dnd.drop", {
|
|
2561
|
+
event: e,
|
|
2562
|
+
region: region,
|
|
2563
|
+
suggestedDropMode: region === "over" ? "appendChild" : region,
|
|
2564
|
+
suggestedDropEffect: lastDropEffect,
|
|
2565
|
+
sourceNode: srcNode,
|
|
2566
|
+
sourceNodeData: nodeData,
|
|
2567
|
+
dataTransfer: e.dataTransfer,
|
|
2568
|
+
});
|
|
2504
2569
|
}
|
|
2505
2570
|
return false;
|
|
2506
2571
|
}
|
|
@@ -2509,7 +2574,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2509
2574
|
/*!
|
|
2510
2575
|
* Wunderbaum - drag_observer
|
|
2511
2576
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2512
|
-
* v0.
|
|
2577
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
2513
2578
|
*/
|
|
2514
2579
|
/**
|
|
2515
2580
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2658,7 +2723,7 @@ class DragObserver {
|
|
|
2658
2723
|
/*!
|
|
2659
2724
|
* Wunderbaum - common
|
|
2660
2725
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2661
|
-
* v0.
|
|
2726
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
2662
2727
|
*/
|
|
2663
2728
|
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
2664
2729
|
/**
|
|
@@ -2678,12 +2743,18 @@ const TITLE_SPAN_PAD_Y = 7;
|
|
|
2678
2743
|
const RENDER_MAX_PREFETCH = 5;
|
|
2679
2744
|
/** Minimum column width if not set otherwise. */
|
|
2680
2745
|
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2746
|
+
/**
|
|
2747
|
+
* A value for `node.type` that by convention may be used to mark a node as directory.
|
|
2748
|
+
* It may be used to sort 'directories' to the top.
|
|
2749
|
+
*/
|
|
2750
|
+
const NODE_TYPE_FOLDER = "folder";
|
|
2681
2751
|
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2682
2752
|
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2753
|
+
* `<` is ignored, because it is probably an html tag.
|
|
2683
2754
|
*/
|
|
2684
|
-
const
|
|
2685
|
-
|
|
2686
|
-
|
|
2755
|
+
const TEST_FILE_PATH = /^(?!.*<).*[/.]/;
|
|
2756
|
+
/** Regular expression to detect if a string describes an HTML element. */
|
|
2757
|
+
const TEST_HTML = /</;
|
|
2687
2758
|
/**
|
|
2688
2759
|
* Default node icons for icon libraries
|
|
2689
2760
|
*
|
|
@@ -2691,7 +2762,7 @@ const TEST_IMG = new RegExp(/\.|\//);
|
|
|
2691
2762
|
* - 'fontawesome6' {@link https://fontawesome.com/icons}
|
|
2692
2763
|
*
|
|
2693
2764
|
*/
|
|
2694
|
-
const
|
|
2765
|
+
const defaultIconMaps = {
|
|
2695
2766
|
bootstrap: {
|
|
2696
2767
|
error: "bi bi-exclamation-triangle",
|
|
2697
2768
|
// loading: "bi bi-hourglass-split wb-busy",
|
|
@@ -2739,7 +2810,7 @@ const iconMaps = {
|
|
|
2739
2810
|
radioChecked: "fa-solid fa-circle",
|
|
2740
2811
|
radioUnchecked: "fa-regular fa-circle",
|
|
2741
2812
|
radioUnknown: "fa-regular fa-circle-question",
|
|
2742
|
-
folder: "fa-
|
|
2813
|
+
folder: "fa-regular fa-folder-closed",
|
|
2743
2814
|
folderOpen: "fa-regular fa-folder-open",
|
|
2744
2815
|
folderLazy: "fa-solid fa-folder-plus",
|
|
2745
2816
|
doc: "fa-regular fa-file",
|
|
@@ -2812,12 +2883,20 @@ function makeNodeTitleStartMatcher(s) {
|
|
|
2812
2883
|
return reMatch.test(node.title);
|
|
2813
2884
|
};
|
|
2814
2885
|
}
|
|
2815
|
-
/** Compare two nodes by title (case-insensitive).
|
|
2886
|
+
/** Compare two nodes by title (case-insensitive).
|
|
2887
|
+
* @deprecated Use `key` option instead of `cmp` in sort methods.
|
|
2888
|
+
*/
|
|
2816
2889
|
function nodeTitleSorter(a, b) {
|
|
2817
2890
|
const x = a.title.toLowerCase();
|
|
2818
2891
|
const y = b.title.toLowerCase();
|
|
2819
2892
|
return x === y ? 0 : x > y ? 1 : -1;
|
|
2820
2893
|
}
|
|
2894
|
+
// /** Compare nodes by title (case-insensitive). */
|
|
2895
|
+
// export function nodeTitleKeyGetter(
|
|
2896
|
+
// node: WunderbaumNode
|
|
2897
|
+
// ): string | number | Array<any> {
|
|
2898
|
+
// return node.title.toLowerCase();
|
|
2899
|
+
// }
|
|
2821
2900
|
/**
|
|
2822
2901
|
* Convert 'flat' to 'nested' format.
|
|
2823
2902
|
*
|
|
@@ -3008,7 +3087,7 @@ function decompressSourceData(source) {
|
|
|
3008
3087
|
/*!
|
|
3009
3088
|
* Wunderbaum - ext-grid
|
|
3010
3089
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3011
|
-
* v0.
|
|
3090
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
3012
3091
|
*/
|
|
3013
3092
|
class GridExtension extends WunderbaumExtension {
|
|
3014
3093
|
constructor(tree) {
|
|
@@ -3068,7 +3147,7 @@ class GridExtension extends WunderbaumExtension {
|
|
|
3068
3147
|
super.init();
|
|
3069
3148
|
}
|
|
3070
3149
|
/**
|
|
3071
|
-
*
|
|
3150
|
+
* Handles drag and sragstop events for column resizing.
|
|
3072
3151
|
*/
|
|
3073
3152
|
handleDrag(e) {
|
|
3074
3153
|
const custom = e.customData;
|
|
@@ -3099,7 +3178,7 @@ class GridExtension extends WunderbaumExtension {
|
|
|
3099
3178
|
/*!
|
|
3100
3179
|
* Wunderbaum - deferred
|
|
3101
3180
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3102
|
-
* v0.
|
|
3181
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
3103
3182
|
*/
|
|
3104
3183
|
/**
|
|
3105
3184
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -3152,7 +3231,7 @@ class Deferred {
|
|
|
3152
3231
|
/*!
|
|
3153
3232
|
* Wunderbaum - wunderbaum_node
|
|
3154
3233
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3155
|
-
* v0.
|
|
3234
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
3156
3235
|
*/
|
|
3157
3236
|
/** WunderbaumNode properties that can be passed with source data.
|
|
3158
3237
|
* (Any other source properties will be stored as `node.data.PROP`.)
|
|
@@ -3203,7 +3282,7 @@ NODE_DICT_PROPS.delete("unselectable");
|
|
|
3203
3282
|
*/
|
|
3204
3283
|
class WunderbaumNode {
|
|
3205
3284
|
constructor(tree, parent, data) {
|
|
3206
|
-
var _a
|
|
3285
|
+
var _a;
|
|
3207
3286
|
/** Reference key. Unlike {@link key}, a `refKey` may occur multiple
|
|
3208
3287
|
* times within a tree (in this case we have 'clone nodes').
|
|
3209
3288
|
* @see Use {@link setKey} to modify.
|
|
@@ -3233,8 +3312,8 @@ class WunderbaumNode {
|
|
|
3233
3312
|
assert(!data.children, "'children' not allowed here");
|
|
3234
3313
|
this.tree = tree;
|
|
3235
3314
|
this.parent = parent;
|
|
3236
|
-
this.key =
|
|
3237
|
-
this.title = "" + ((
|
|
3315
|
+
this.key = tree._calculateKey(data, parent);
|
|
3316
|
+
this.title = "" + ((_a = data.title) !== null && _a !== void 0 ? _a : "<" + this.key + ">");
|
|
3238
3317
|
this.expanded = !!data.expanded;
|
|
3239
3318
|
this.lazy = !!data.lazy;
|
|
3240
3319
|
// We set the following node properties only if a matching data value is
|
|
@@ -3251,7 +3330,9 @@ class WunderbaumNode {
|
|
|
3251
3330
|
: 0;
|
|
3252
3331
|
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
3253
3332
|
// Selection
|
|
3254
|
-
data.checkbox != null
|
|
3333
|
+
data.checkbox != null
|
|
3334
|
+
? (this.checkbox = intToBool(data.checkbox))
|
|
3335
|
+
: 0;
|
|
3255
3336
|
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
3256
3337
|
data.selected != null ? (this.selected = !!data.selected) : 0;
|
|
3257
3338
|
data.unselectable != null ? (this.unselectable = !!data.unselectable) : 0;
|
|
@@ -3355,8 +3436,14 @@ class WunderbaumNode {
|
|
|
3355
3436
|
const forceExpand = applyMinExpanLevel && _level < tree.options.minExpandLevel;
|
|
3356
3437
|
for (const child of nodeData) {
|
|
3357
3438
|
const subChildren = child.children;
|
|
3439
|
+
// Remove children property from source data because it should not be
|
|
3440
|
+
// passed to the constructor of WunderbaumNode:
|
|
3358
3441
|
delete child.children;
|
|
3359
3442
|
const n = new WunderbaumNode(tree, this, child);
|
|
3443
|
+
// Set `children` property again, so it can be used in `reload()`
|
|
3444
|
+
if (subChildren != null) {
|
|
3445
|
+
child.children = subChildren;
|
|
3446
|
+
}
|
|
3360
3447
|
if (forceExpand && !n.isUnloaded()) {
|
|
3361
3448
|
n.expanded = true;
|
|
3362
3449
|
}
|
|
@@ -3772,15 +3859,12 @@ class WunderbaumNode {
|
|
|
3772
3859
|
}
|
|
3773
3860
|
return l;
|
|
3774
3861
|
}
|
|
3775
|
-
/** Return a string representing the
|
|
3862
|
+
/** Return a string representing the hierarchical node path, e.g. "a/b/c".
|
|
3776
3863
|
* @param includeSelf
|
|
3777
3864
|
* @param part property name or callback
|
|
3778
3865
|
* @param separator
|
|
3779
3866
|
*/
|
|
3780
3867
|
getPath(includeSelf = true, part = "title", separator = "/") {
|
|
3781
|
-
// includeSelf = includeSelf !== false;
|
|
3782
|
-
// part = part || "title";
|
|
3783
|
-
// separator = separator || "/";
|
|
3784
3868
|
let val;
|
|
3785
3869
|
const path = [];
|
|
3786
3870
|
const isFunc = typeof part === "function";
|
|
@@ -3795,7 +3879,7 @@ class WunderbaumNode {
|
|
|
3795
3879
|
}, includeSelf);
|
|
3796
3880
|
return path.join(separator);
|
|
3797
3881
|
}
|
|
3798
|
-
/** Return the
|
|
3882
|
+
/** Return the preceding node (under the same parent) or null. */
|
|
3799
3883
|
getPrevSibling() {
|
|
3800
3884
|
const ac = this.parent.children;
|
|
3801
3885
|
const idx = ac.indexOf(this);
|
|
@@ -3824,7 +3908,7 @@ class WunderbaumNode {
|
|
|
3824
3908
|
hasClass(className) {
|
|
3825
3909
|
return this.classes ? this.classes.has(className) : false;
|
|
3826
3910
|
}
|
|
3827
|
-
/** Return true if node
|
|
3911
|
+
/** Return true if node is the currently focused node. @since 0.9.0 */
|
|
3828
3912
|
hasFocus() {
|
|
3829
3913
|
return this.tree.focusNode === this;
|
|
3830
3914
|
}
|
|
@@ -3879,7 +3963,7 @@ class WunderbaumNode {
|
|
|
3879
3963
|
* an expand operation is currently possible.
|
|
3880
3964
|
*/
|
|
3881
3965
|
isExpandable(andCollapsed = false) {
|
|
3882
|
-
// `false` is never expandable (
|
|
3966
|
+
// `false` is never expandable (unofficial)
|
|
3883
3967
|
if ((andCollapsed && this.expanded) || this.children === false) {
|
|
3884
3968
|
return false;
|
|
3885
3969
|
}
|
|
@@ -3942,11 +4026,11 @@ class WunderbaumNode {
|
|
|
3942
4026
|
isPartsel() {
|
|
3943
4027
|
return !this.selected && !!this._partsel;
|
|
3944
4028
|
}
|
|
3945
|
-
/** Return true if this node has DOM
|
|
4029
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
3946
4030
|
isRadio() {
|
|
3947
4031
|
return !!this.parent.radiogroup || this.getOption("checkbox") === "radio";
|
|
3948
4032
|
}
|
|
3949
|
-
/** Return true if this node has DOM
|
|
4033
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
3950
4034
|
isRendered() {
|
|
3951
4035
|
return !!this._rowElem;
|
|
3952
4036
|
}
|
|
@@ -4701,9 +4785,9 @@ class WunderbaumNode {
|
|
|
4701
4785
|
const typeInfo = this.type ? tree.types[this.type] : null;
|
|
4702
4786
|
const rowDiv = this._rowElem;
|
|
4703
4787
|
// Row markup already exists
|
|
4704
|
-
const
|
|
4705
|
-
const
|
|
4706
|
-
const
|
|
4788
|
+
const nodeSpan = rowDiv.querySelector("span.wb-node");
|
|
4789
|
+
const expanderElem = nodeSpan.querySelector("i.wb-expander");
|
|
4790
|
+
const checkboxElem = nodeSpan.querySelector("i.wb-checkbox");
|
|
4707
4791
|
const rowClasses = ["wb-row"];
|
|
4708
4792
|
this.expanded ? rowClasses.push("wb-expanded") : 0;
|
|
4709
4793
|
this.lazy ? rowClasses.push("wb-lazy") : 0;
|
|
@@ -4728,7 +4812,7 @@ class WunderbaumNode {
|
|
|
4728
4812
|
if (typeInfo && typeInfo.classes) {
|
|
4729
4813
|
rowDiv.classList.add(...typeInfo.classes);
|
|
4730
4814
|
}
|
|
4731
|
-
if (
|
|
4815
|
+
if (expanderElem) {
|
|
4732
4816
|
let image = null;
|
|
4733
4817
|
if (this._isLoading) {
|
|
4734
4818
|
image = iconMap.loading;
|
|
@@ -4745,16 +4829,20 @@ class WunderbaumNode {
|
|
|
4745
4829
|
image = iconMap.expanderLazy;
|
|
4746
4830
|
}
|
|
4747
4831
|
if (image == null) {
|
|
4748
|
-
|
|
4832
|
+
expanderElem.className = "wb-expander";
|
|
4833
|
+
expanderElem.classList.add("wb-indent");
|
|
4834
|
+
}
|
|
4835
|
+
else if (TEST_HTML.test(image)) {
|
|
4836
|
+
expanderElem.replaceWith(elemFromHtml(image));
|
|
4749
4837
|
}
|
|
4750
|
-
else if (
|
|
4751
|
-
|
|
4838
|
+
else if (TEST_FILE_PATH.test(image)) {
|
|
4839
|
+
expanderElem.style.backgroundImage = `url('${image}')`;
|
|
4752
4840
|
}
|
|
4753
4841
|
else {
|
|
4754
|
-
|
|
4842
|
+
expanderElem.className = "wb-expander " + image;
|
|
4755
4843
|
}
|
|
4756
4844
|
}
|
|
4757
|
-
if (
|
|
4845
|
+
if (checkboxElem) {
|
|
4758
4846
|
let cbclass = "wb-checkbox ";
|
|
4759
4847
|
if (this.isRadio()) {
|
|
4760
4848
|
cbclass += "wb-radio ";
|
|
@@ -4778,7 +4866,7 @@ class WunderbaumNode {
|
|
|
4778
4866
|
cbclass += iconMap.checkUnchecked;
|
|
4779
4867
|
}
|
|
4780
4868
|
}
|
|
4781
|
-
|
|
4869
|
+
checkboxElem.className = cbclass;
|
|
4782
4870
|
}
|
|
4783
4871
|
// Fix active cell in cell-nav mode
|
|
4784
4872
|
if (!opts.isNew) {
|
|
@@ -4788,9 +4876,9 @@ class WunderbaumNode {
|
|
|
4788
4876
|
colSpan.classList.remove("wb-error", "wb-invalid");
|
|
4789
4877
|
}
|
|
4790
4878
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
4791
|
-
const iconSpan =
|
|
4879
|
+
const iconSpan = nodeSpan.querySelector("i.wb-icon");
|
|
4792
4880
|
if (iconSpan) {
|
|
4793
|
-
this._createIcon(
|
|
4881
|
+
this._createIcon(nodeSpan, iconSpan, !expanderElem);
|
|
4794
4882
|
}
|
|
4795
4883
|
}
|
|
4796
4884
|
// Adjust column width
|
|
@@ -5095,6 +5183,32 @@ class WunderbaumNode {
|
|
|
5095
5183
|
setKey(key, refKey) {
|
|
5096
5184
|
throw new Error("Not yet implemented");
|
|
5097
5185
|
}
|
|
5186
|
+
// /**
|
|
5187
|
+
// * Calculate a *stable*, unique key for this node from its refKey (or title).
|
|
5188
|
+
// * We also add information from the parent, because a refKey may occur multiple
|
|
5189
|
+
// * times in a tree.
|
|
5190
|
+
// */
|
|
5191
|
+
// calcUniqueKey() {
|
|
5192
|
+
// // Assuming that the parent's key was calculated the same way, we implicitly
|
|
5193
|
+
// // involve the whole refKey-path:
|
|
5194
|
+
// const s = this.key + (this.refKey || this.title);
|
|
5195
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
5196
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
5197
|
+
// const h1 = util.murmurHash3(s, true);
|
|
5198
|
+
// return "id_" + h1 + util.murmurHash3(h1 + s, true);
|
|
5199
|
+
// // const l = [];
|
|
5200
|
+
// // // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
5201
|
+
// // let node: WunderbaumNode = this;
|
|
5202
|
+
// // while (node.parent) {
|
|
5203
|
+
// // l.unshift(node.refKey || node.key);
|
|
5204
|
+
// // node = node.parent;
|
|
5205
|
+
// // }
|
|
5206
|
+
// // const path = l.join("/");
|
|
5207
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
5208
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
5209
|
+
// // const h1 = util.murmurHash3(path, true);
|
|
5210
|
+
// // return "id_" + h1 + util.murmurHash3(h1 + path, true);
|
|
5211
|
+
// }
|
|
5098
5212
|
/**
|
|
5099
5213
|
* Trigger a repaint, typically after a status or data change.
|
|
5100
5214
|
*
|
|
@@ -5126,6 +5240,23 @@ class WunderbaumNode {
|
|
|
5126
5240
|
});
|
|
5127
5241
|
return nodeList;
|
|
5128
5242
|
}
|
|
5243
|
+
/**
|
|
5244
|
+
* Return an array of refKey values.
|
|
5245
|
+
*
|
|
5246
|
+
* RefKeys are unique identifiers for a node data, and are used to identify
|
|
5247
|
+
* clones.
|
|
5248
|
+
* If more than one node has the same refKey, it is only returned once.
|
|
5249
|
+
* @param selected if true, only return refKeys of selected nodes.
|
|
5250
|
+
*/
|
|
5251
|
+
getRefKeys(selected = false) {
|
|
5252
|
+
const refKeys = new Set();
|
|
5253
|
+
this.visit((node) => {
|
|
5254
|
+
if (node.refKey != null && (!selected || node.selected)) {
|
|
5255
|
+
refKeys.add(node.refKey);
|
|
5256
|
+
}
|
|
5257
|
+
});
|
|
5258
|
+
return Array.from(refKeys);
|
|
5259
|
+
}
|
|
5129
5260
|
/** Toggle the check/uncheck state. */
|
|
5130
5261
|
toggleSelected(options) {
|
|
5131
5262
|
let flag = this.isSelected();
|
|
@@ -5305,9 +5436,11 @@ class WunderbaumNode {
|
|
|
5305
5436
|
if (selectMode === "hier") {
|
|
5306
5437
|
this.fixSelection3AfterClick();
|
|
5307
5438
|
}
|
|
5308
|
-
else if (selectMode === "single") {
|
|
5439
|
+
else if (selectMode === "single" && flag) {
|
|
5309
5440
|
tree.visit((n) => {
|
|
5310
|
-
n
|
|
5441
|
+
if (n !== this) {
|
|
5442
|
+
n.selected = false;
|
|
5443
|
+
}
|
|
5311
5444
|
});
|
|
5312
5445
|
}
|
|
5313
5446
|
}
|
|
@@ -5409,30 +5542,16 @@ class WunderbaumNode {
|
|
|
5409
5542
|
this.tooltip = tooltip;
|
|
5410
5543
|
this.update();
|
|
5411
5544
|
}
|
|
5412
|
-
_sortChildren(cmp, deep) {
|
|
5413
|
-
const cl = this.children;
|
|
5414
|
-
if (!cl) {
|
|
5415
|
-
return;
|
|
5416
|
-
}
|
|
5417
|
-
cl.sort(cmp);
|
|
5418
|
-
if (deep) {
|
|
5419
|
-
for (let i = 0, l = cl.length; i < l; i++) {
|
|
5420
|
-
if (cl[i].children) {
|
|
5421
|
-
cl[i]._sortChildren(cmp, deep);
|
|
5422
|
-
}
|
|
5423
|
-
}
|
|
5424
|
-
}
|
|
5425
|
-
}
|
|
5426
5545
|
/**
|
|
5427
5546
|
* Sort child list by title or custom criteria.
|
|
5428
5547
|
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
5429
5548
|
* (defaults to sorting by title).
|
|
5430
5549
|
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
5550
|
+
* @deprecated use {@link sort}
|
|
5431
5551
|
*/
|
|
5432
5552
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
5433
|
-
this.
|
|
5434
|
-
this.
|
|
5435
|
-
// this.triggerModify("sort"); // TODO
|
|
5553
|
+
this.tree.logDeprecate("node.sortChildren()", { since: "0.14.0" });
|
|
5554
|
+
return this.sort({ cmp: cmp ? cmp : undefined, deep: deep });
|
|
5436
5555
|
}
|
|
5437
5556
|
/**
|
|
5438
5557
|
* Renumber nodes `_nativeIndex`. This is useful to allow to restore the
|
|
@@ -5454,74 +5573,142 @@ class WunderbaumNode {
|
|
|
5454
5573
|
/**
|
|
5455
5574
|
* Convenience method to implement column sorting.
|
|
5456
5575
|
* @since 0.11.0
|
|
5576
|
+
* @deprecated use {@link sort}
|
|
5457
5577
|
*/
|
|
5458
5578
|
sortByProperty(options) {
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5579
|
+
this.tree.logDeprecate("node.sortByProperty()", { since: "0.14.0" });
|
|
5580
|
+
return this.sort(options);
|
|
5581
|
+
}
|
|
5582
|
+
/**
|
|
5583
|
+
* Implement column sorting.
|
|
5584
|
+
* @since 0.14.0
|
|
5585
|
+
*/
|
|
5586
|
+
sort(options) {
|
|
5587
|
+
const tree = this.tree;
|
|
5588
|
+
let { propName = undefined, deep = true, key = undefined, order = undefined, caseInsensitive = true, cmp = undefined,
|
|
5589
|
+
// Support click on column sort header:
|
|
5590
|
+
updateColInfo = false, nativeOrderPropName = "_nativeIndex", colId = undefined, } = options;
|
|
5591
|
+
propName !== null && propName !== void 0 ? propName : (propName = colId);
|
|
5592
|
+
if (propName === "*") {
|
|
5593
|
+
propName = "title";
|
|
5594
|
+
}
|
|
5595
|
+
const isFolder = tree.options.sortFoldersFirst === true
|
|
5596
|
+
? (node) => node.hasChildren() !== false || node.type === NODE_TYPE_FOLDER
|
|
5597
|
+
: tree.options.sortFoldersFirst;
|
|
5463
5598
|
if (updateColInfo) {
|
|
5464
|
-
colDef = this.tree["_columnsById"][options.colId];
|
|
5599
|
+
const colDef = this.tree["_columnsById"][options.colId];
|
|
5465
5600
|
assert(colDef, `Invalid colId specified: ${options.colId}`);
|
|
5466
|
-
order =
|
|
5467
|
-
(_a = options.order) !== null && _a !== void 0 ? _a : rotate(colDef.sortOrder, ["asc", "desc", undefined]);
|
|
5601
|
+
order !== null && order !== void 0 ? order : (order = rotate(colDef.sortOrder, ["asc", "desc", undefined]));
|
|
5468
5602
|
for (const col of this.tree.columns) {
|
|
5469
5603
|
col.sortOrder = col === colDef ? order : undefined;
|
|
5470
5604
|
}
|
|
5605
|
+
if (order === undefined) {
|
|
5606
|
+
propName = nativeOrderPropName;
|
|
5607
|
+
order = "asc";
|
|
5608
|
+
}
|
|
5471
5609
|
this.tree.update(ChangeType.colStructure);
|
|
5472
5610
|
}
|
|
5473
5611
|
else {
|
|
5474
|
-
|
|
5612
|
+
propName !== null && propName !== void 0 ? propName : (propName = "title");
|
|
5613
|
+
order !== null && order !== void 0 ? order : (order = "asc");
|
|
5614
|
+
}
|
|
5615
|
+
this.logDebug(`sort(), propName=${propName}, ${order}`, options);
|
|
5616
|
+
assert(propName || cmp || key, "No `propName` or `key` specified");
|
|
5617
|
+
// Define a key callback from the parameters we have
|
|
5618
|
+
if (key == null && cmp == null) {
|
|
5619
|
+
key = (node) => {
|
|
5620
|
+
let val;
|
|
5621
|
+
if (NODE_DICT_PROPS.has(propName)) {
|
|
5622
|
+
val = node[propName];
|
|
5623
|
+
}
|
|
5624
|
+
else {
|
|
5625
|
+
val = node.data[propName];
|
|
5626
|
+
}
|
|
5627
|
+
if (caseInsensitive && typeof val === "string") {
|
|
5628
|
+
val = val.toLowerCase();
|
|
5629
|
+
}
|
|
5630
|
+
return val;
|
|
5631
|
+
};
|
|
5475
5632
|
}
|
|
5476
|
-
|
|
5477
|
-
if (
|
|
5478
|
-
|
|
5633
|
+
// Define a compare callback that uses the key callback
|
|
5634
|
+
if (cmp) {
|
|
5635
|
+
assert(!key, "`key` and `cmp` are mutually exclusive");
|
|
5636
|
+
tree.logDeprecate("SortOptions.cmp", {
|
|
5637
|
+
since: "0.14.0",
|
|
5638
|
+
hint: "use the `key` callback instead",
|
|
5639
|
+
});
|
|
5479
5640
|
}
|
|
5480
|
-
|
|
5481
|
-
propName
|
|
5482
|
-
|
|
5641
|
+
else {
|
|
5642
|
+
if (options.propName || options.caseInsensitive) {
|
|
5643
|
+
tree.logWarn("sort(): ignoring propName, caseInsensitive");
|
|
5644
|
+
}
|
|
5645
|
+
cmp = (a, b) => {
|
|
5646
|
+
if (isFolder) {
|
|
5647
|
+
const isFolderA = isFolder(a);
|
|
5648
|
+
if (isFolderA !== isFolder(b)) {
|
|
5649
|
+
return isFolderA ? -1 : 1;
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
let x = key(a);
|
|
5653
|
+
let y = key(b);
|
|
5654
|
+
// Assure we have reasonable comparisons with null values:
|
|
5655
|
+
if (x == null) {
|
|
5656
|
+
x = typeof y === "string" ? "" : 0;
|
|
5657
|
+
}
|
|
5658
|
+
else if (typeof x === "boolean") {
|
|
5659
|
+
x = x ? 1 : 0;
|
|
5660
|
+
}
|
|
5661
|
+
if (y == null) {
|
|
5662
|
+
y = typeof x === "string" ? "" : 0;
|
|
5663
|
+
}
|
|
5664
|
+
else if (typeof y === "boolean") {
|
|
5665
|
+
y = y ? 1 : 0;
|
|
5666
|
+
}
|
|
5667
|
+
if (order === "desc") {
|
|
5668
|
+
return x === y ? 0 : x > y ? -1 : 1;
|
|
5669
|
+
}
|
|
5670
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
5671
|
+
};
|
|
5483
5672
|
}
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
let av, bv;
|
|
5488
|
-
if (NODE_DICT_PROPS.has(propName)) {
|
|
5489
|
-
av = a[propName];
|
|
5490
|
-
bv = b[propName];
|
|
5491
|
-
}
|
|
5492
|
-
else {
|
|
5493
|
-
av = a.data[propName];
|
|
5494
|
-
bv = b.data[propName];
|
|
5495
|
-
}
|
|
5496
|
-
if (av == null && bv == null) {
|
|
5497
|
-
return 0;
|
|
5498
|
-
}
|
|
5499
|
-
if (av == null) {
|
|
5500
|
-
av = typeof bv === "string" ? "" : 0;
|
|
5501
|
-
}
|
|
5502
|
-
else if (typeof av === "boolean") {
|
|
5503
|
-
av = av ? 1 : 0;
|
|
5504
|
-
}
|
|
5505
|
-
if (bv == null) {
|
|
5506
|
-
bv = typeof av === "string" ? "" : 0;
|
|
5507
|
-
}
|
|
5508
|
-
else if (typeof bv === "boolean") {
|
|
5509
|
-
bv = bv ? 1 : 0;
|
|
5673
|
+
function _sortChildren(cl) {
|
|
5674
|
+
if (!cl) {
|
|
5675
|
+
return;
|
|
5510
5676
|
}
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5677
|
+
cl.sort(cmp);
|
|
5678
|
+
if (deep) {
|
|
5679
|
+
for (let i = 0, l = cl.length; i < l; i++) {
|
|
5680
|
+
if (cl[i].children) {
|
|
5681
|
+
_sortChildren(cl[i].children);
|
|
5682
|
+
}
|
|
5517
5683
|
}
|
|
5518
5684
|
}
|
|
5519
|
-
|
|
5520
|
-
|
|
5685
|
+
}
|
|
5686
|
+
if (this.children) {
|
|
5687
|
+
_sortChildren(this.children);
|
|
5688
|
+
}
|
|
5689
|
+
this.tree.update(ChangeType.structure);
|
|
5690
|
+
// this.triggerModify("sort"); // TODO
|
|
5691
|
+
}
|
|
5692
|
+
/**
|
|
5693
|
+
* Re-apply current sorting if any (use after lazy load).
|
|
5694
|
+
* Example:
|
|
5695
|
+
* ```js
|
|
5696
|
+
* load: function (e) {
|
|
5697
|
+
* // Whe loading a lazy branch, apply current sort order if any
|
|
5698
|
+
* e.node.resort();
|
|
5699
|
+
* },
|
|
5700
|
+
* ```
|
|
5701
|
+
* @since 0.14.0
|
|
5702
|
+
*/
|
|
5703
|
+
resort(options = {}) {
|
|
5704
|
+
for (const colDef of this.tree.columns) {
|
|
5705
|
+
if (colDef.sortOrder) {
|
|
5706
|
+
options.colId = colDef.id;
|
|
5707
|
+
options.order = colDef.sortOrder;
|
|
5708
|
+
this.sort(options);
|
|
5709
|
+
break;
|
|
5521
5710
|
}
|
|
5522
|
-
|
|
5523
|
-
};
|
|
5524
|
-
return this.sortChildren(cmp, deep);
|
|
5711
|
+
}
|
|
5525
5712
|
}
|
|
5526
5713
|
/**
|
|
5527
5714
|
* Trigger `modifyChild` event on a parent to signal that a child was modified.
|
|
@@ -5558,7 +5745,8 @@ class WunderbaumNode {
|
|
|
5558
5745
|
* @param {function} callback the callback function.
|
|
5559
5746
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
5560
5747
|
* its children only.
|
|
5561
|
-
* @see
|
|
5748
|
+
* @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
|
|
5749
|
+
* @see {@link Wunderbaum.visit}.
|
|
5562
5750
|
*/
|
|
5563
5751
|
visit(callback, includeSelf = false) {
|
|
5564
5752
|
let res = true;
|
|
@@ -5631,7 +5819,7 @@ WunderbaumNode.sequence = 0;
|
|
|
5631
5819
|
/*!
|
|
5632
5820
|
* Wunderbaum - ext-edit
|
|
5633
5821
|
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
5634
|
-
* v0.
|
|
5822
|
+
* v0.14.1, Sun, 22 Mar 2026 05:52:05 GMT (https://github.com/mar10/wunderbaum)
|
|
5635
5823
|
*/
|
|
5636
5824
|
// const START_MARKER = "\uFFF7";
|
|
5637
5825
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5866,7 +6054,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
5866
6054
|
newValue = newValue.trim();
|
|
5867
6055
|
}
|
|
5868
6056
|
if (!node) {
|
|
5869
|
-
this.tree.logDebug("stopEditTitle: not in edit mode.");
|
|
6057
|
+
// this.tree.logDebug("stopEditTitle: not in edit mode.");
|
|
5870
6058
|
return;
|
|
5871
6059
|
}
|
|
5872
6060
|
node.logDebug(`stopEditTitle(${apply})`, options, focusElem, newValue);
|
|
@@ -5966,8 +6154,8 @@ class EditExtension extends WunderbaumExtension {
|
|
|
5966
6154
|
* https://github.com/mar10/wunderbaum
|
|
5967
6155
|
*
|
|
5968
6156
|
* Released under the MIT license.
|
|
5969
|
-
* @version v0.
|
|
5970
|
-
* @date
|
|
6157
|
+
* @version v0.14.1
|
|
6158
|
+
* @date Sun, 22 Mar 2026 05:52:05 GMT
|
|
5971
6159
|
*/
|
|
5972
6160
|
// import "./wunderbaum.scss";
|
|
5973
6161
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -6016,17 +6204,17 @@ class Wunderbaum {
|
|
|
6016
6204
|
this._disableUpdateIgnoreCount = 0;
|
|
6017
6205
|
this._activeNode = null;
|
|
6018
6206
|
this._focusNode = null;
|
|
6207
|
+
this._initialSource = null;
|
|
6019
6208
|
/** Shared properties, referenced by `node.type`. */
|
|
6020
6209
|
this.types = {};
|
|
6021
6210
|
/** List of column definitions. */
|
|
6022
|
-
this.columns = [];
|
|
6211
|
+
this.columns = [];
|
|
6023
6212
|
this._columnsById = {};
|
|
6024
6213
|
// Modification Status
|
|
6025
6214
|
this.pendingChangeTypes = new Set();
|
|
6026
6215
|
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
6027
6216
|
this._util = util;
|
|
6028
6217
|
// --- SELECT ---
|
|
6029
|
-
// /** @internal */
|
|
6030
6218
|
// public selectRangeAnchor: WunderbaumNode | null = null;
|
|
6031
6219
|
// --- BREADCRUMB ---
|
|
6032
6220
|
/** Filter options (used as defaults for calls to {@link Wunderbaum.filterNodes} ) */
|
|
@@ -6045,37 +6233,44 @@ class Wunderbaum {
|
|
|
6045
6233
|
this.lastQuicksearchTerm = "";
|
|
6046
6234
|
// --- EDIT ---
|
|
6047
6235
|
this.lastClickTime = 0;
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6236
|
+
// Set default options and merge with user options
|
|
6237
|
+
const initOptions = Object.assign({
|
|
6238
|
+
id: undefined,
|
|
6239
|
+
source: [], // URL for GET/PUT, Ajax options, or callback
|
|
6240
|
+
element: unsafeCast(null),
|
|
6052
6241
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
6053
6242
|
header: null, // Show/hide header (pass bool or string)
|
|
6054
|
-
// headerHeightPx: ROW_HEIGHT,
|
|
6055
6243
|
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
6056
6244
|
iconMap: "bootstrap",
|
|
6057
|
-
columns: null,
|
|
6058
|
-
types:
|
|
6059
|
-
// escapeTitles: true,
|
|
6245
|
+
columns: [], //util.unsafeCast<ColumnDefinitionList>(null),
|
|
6246
|
+
types: {},
|
|
6060
6247
|
enabled: true,
|
|
6061
6248
|
fixedCol: false,
|
|
6062
6249
|
showSpinner: false,
|
|
6063
6250
|
checkbox: false,
|
|
6064
6251
|
minExpandLevel: 0,
|
|
6065
6252
|
emptyChildListExpandable: false,
|
|
6066
|
-
// updateThrottleWait: 200,
|
|
6067
6253
|
skeleton: false,
|
|
6254
|
+
autoCollapse: false,
|
|
6255
|
+
adjustHeight: true,
|
|
6068
6256
|
connectTopBreadcrumb: null,
|
|
6257
|
+
columnsFilterable: false,
|
|
6258
|
+
columnsMenu: false,
|
|
6259
|
+
columnsResizable: false,
|
|
6260
|
+
columnsSortable: false,
|
|
6069
6261
|
selectMode: "multi", // SelectModeType
|
|
6262
|
+
scrollIntoViewOnExpandClick: true,
|
|
6263
|
+
// --- Extensions (actually set by exensions on init)
|
|
6264
|
+
dnd: unsafeCast(null),
|
|
6265
|
+
edit: unsafeCast(null),
|
|
6266
|
+
filter: unsafeCast(null),
|
|
6070
6267
|
// --- KeyNav ---
|
|
6071
|
-
navigationModeOption: null,
|
|
6268
|
+
navigationModeOption: unsafeCast(null),
|
|
6072
6269
|
quicksearch: true,
|
|
6073
6270
|
// --- Events ---
|
|
6074
|
-
iconBadge: null,
|
|
6075
|
-
change: null,
|
|
6076
|
-
//
|
|
6077
|
-
error: null,
|
|
6078
|
-
receive: null,
|
|
6271
|
+
// iconBadge: null,
|
|
6272
|
+
// change: null,
|
|
6273
|
+
// ...
|
|
6079
6274
|
// --- Strings ---
|
|
6080
6275
|
strings: {
|
|
6081
6276
|
loadError: "Error",
|
|
@@ -6086,7 +6281,9 @@ class Wunderbaum {
|
|
|
6086
6281
|
noMatch: "No results",
|
|
6087
6282
|
matchIndex: "${match} of ${matches}",
|
|
6088
6283
|
},
|
|
6089
|
-
}, options)
|
|
6284
|
+
}, options);
|
|
6285
|
+
const opts = initOptions;
|
|
6286
|
+
this.options = opts;
|
|
6090
6287
|
const readyDeferred = new Deferred();
|
|
6091
6288
|
this.ready = readyDeferred.promise();
|
|
6092
6289
|
let readyOk = false;
|
|
@@ -6113,7 +6310,8 @@ class Wunderbaum {
|
|
|
6113
6310
|
this._callEvent("init", { error: err });
|
|
6114
6311
|
}
|
|
6115
6312
|
});
|
|
6116
|
-
this.id =
|
|
6313
|
+
this.id = initOptions.id || "wb_" + ++Wunderbaum.sequence;
|
|
6314
|
+
delete initOptions.id;
|
|
6117
6315
|
this.root = new WbSystemRoot(this);
|
|
6118
6316
|
this._registerExtension(new KeynavExtension(this));
|
|
6119
6317
|
this._registerExtension(new EditExtension(this));
|
|
@@ -6123,19 +6321,20 @@ class Wunderbaum {
|
|
|
6123
6321
|
this._registerExtension(new LoggerExtension(this));
|
|
6124
6322
|
this._updateViewportThrottled = adaptiveThrottle(this._updateViewportImmediately.bind(this), {});
|
|
6125
6323
|
// --- Evaluate options
|
|
6126
|
-
this.columns =
|
|
6127
|
-
delete
|
|
6324
|
+
this.columns = initOptions.columns || [];
|
|
6325
|
+
delete initOptions.columns;
|
|
6128
6326
|
if (!this.columns || !this.columns.length) {
|
|
6129
6327
|
const title = typeof opts.header === "string" ? opts.header : this.id;
|
|
6130
6328
|
this.columns = [{ id: "*", title: title, width: "*" }];
|
|
6131
6329
|
}
|
|
6132
|
-
if (
|
|
6133
|
-
this.setTypes(
|
|
6330
|
+
if (initOptions.types) {
|
|
6331
|
+
this.setTypes(initOptions.types, true);
|
|
6134
6332
|
}
|
|
6135
|
-
delete
|
|
6333
|
+
delete initOptions.types;
|
|
6136
6334
|
// --- Create Markup
|
|
6137
|
-
this.element = elemFromSelector(
|
|
6138
|
-
assert(!!this.element, `Invalid 'element' option: ${
|
|
6335
|
+
this.element = elemFromSelector(initOptions.element);
|
|
6336
|
+
assert(!!this.element, `Invalid 'element' option: ${initOptions.element}`);
|
|
6337
|
+
delete initOptions.element;
|
|
6139
6338
|
this.element.classList.add("wunderbaum");
|
|
6140
6339
|
if (!this.element.getAttribute("tabindex")) {
|
|
6141
6340
|
this.element.tabIndex = 0;
|
|
@@ -6211,11 +6410,11 @@ class Wunderbaum {
|
|
|
6211
6410
|
}
|
|
6212
6411
|
});
|
|
6213
6412
|
// --- Load initial data
|
|
6214
|
-
if (
|
|
6413
|
+
if (initOptions.source) {
|
|
6215
6414
|
if (opts.showSpinner) {
|
|
6216
6415
|
this.nodeListElement.innerHTML = `<progress class='spinner'>${opts.strings.loading}</progress>`;
|
|
6217
6416
|
}
|
|
6218
|
-
this.load(
|
|
6417
|
+
this.load(initOptions.source)
|
|
6219
6418
|
.then(() => {
|
|
6220
6419
|
// The source may have defined columns, so we may adjust the nav mode
|
|
6221
6420
|
if (opts.navigationModeOption == null) {
|
|
@@ -6248,15 +6447,18 @@ class Wunderbaum {
|
|
|
6248
6447
|
// has a wrong value at start???
|
|
6249
6448
|
this.update(ChangeType.any);
|
|
6250
6449
|
// --- Bind listeners
|
|
6251
|
-
this.
|
|
6252
|
-
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
6253
|
-
this.update(ChangeType.scroll);
|
|
6254
|
-
});
|
|
6450
|
+
this._registerEventHandlers();
|
|
6255
6451
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
6256
6452
|
// this.log("ResizeObserver: Size changed", entries);
|
|
6257
6453
|
this.update(ChangeType.resize);
|
|
6258
6454
|
});
|
|
6259
6455
|
this.resizeObserver.observe(this.element);
|
|
6456
|
+
}
|
|
6457
|
+
_registerEventHandlers() {
|
|
6458
|
+
this.element.addEventListener("scroll", (e) => {
|
|
6459
|
+
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
6460
|
+
this.update(ChangeType.scroll);
|
|
6461
|
+
});
|
|
6260
6462
|
onEvent(this.element, "click", ".wb-button,.wb-col-icon", (e) => {
|
|
6261
6463
|
var _a, _b;
|
|
6262
6464
|
const info = Wunderbaum.getEventInfo(e);
|
|
@@ -6272,9 +6474,6 @@ class Wunderbaum {
|
|
|
6272
6474
|
const node = info.node;
|
|
6273
6475
|
const mouseEvent = e;
|
|
6274
6476
|
// this.log("click", info);
|
|
6275
|
-
// if (this._selectRange(info) === false) {
|
|
6276
|
-
// return;
|
|
6277
|
-
// }
|
|
6278
6477
|
if (this._callEvent("click", { event: e, node: node, info: info }) === false) {
|
|
6279
6478
|
this.lastClickTime = Date.now();
|
|
6280
6479
|
return false;
|
|
@@ -6293,20 +6492,22 @@ class Wunderbaum {
|
|
|
6293
6492
|
(!slowClickDelay || Date.now() - this.lastClickTime < slowClickDelay)) {
|
|
6294
6493
|
node.startEditTitle();
|
|
6295
6494
|
}
|
|
6296
|
-
if (info.colIdx >= 0) {
|
|
6297
|
-
node.setActive(true, { colIdx: info.colIdx, event: e });
|
|
6298
|
-
}
|
|
6299
|
-
else {
|
|
6300
|
-
node.setActive(true, { event: e });
|
|
6301
|
-
}
|
|
6302
6495
|
if (info.region === NodeRegion.expander) {
|
|
6303
6496
|
node.setExpanded(!node.isExpanded(), {
|
|
6304
|
-
scrollIntoView: options.scrollIntoViewOnExpandClick !== false,
|
|
6497
|
+
scrollIntoView: this.options.scrollIntoViewOnExpandClick !== false,
|
|
6305
6498
|
});
|
|
6306
6499
|
}
|
|
6307
6500
|
else if (info.region === NodeRegion.checkbox) {
|
|
6308
6501
|
node.toggleSelected();
|
|
6309
6502
|
}
|
|
6503
|
+
else {
|
|
6504
|
+
if (info.colIdx >= 0) {
|
|
6505
|
+
node.setActive(true, { colIdx: info.colIdx, event: e });
|
|
6506
|
+
}
|
|
6507
|
+
else {
|
|
6508
|
+
node.setActive(true, { event: e });
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6310
6511
|
}
|
|
6311
6512
|
this.lastClickTime = Date.now();
|
|
6312
6513
|
});
|
|
@@ -6342,7 +6543,7 @@ class Wunderbaum {
|
|
|
6342
6543
|
const targetNode = Wunderbaum.getNode(e);
|
|
6343
6544
|
this._callEvent("focus", { flag: flag, event: e });
|
|
6344
6545
|
if (flag && this.isRowNav() && !this.isEditingTitle()) {
|
|
6345
|
-
if (
|
|
6546
|
+
if (this.options.navigationModeOption === NavModeEnum.row) {
|
|
6346
6547
|
targetNode === null || targetNode === void 0 ? void 0 : targetNode.setActive();
|
|
6347
6548
|
}
|
|
6348
6549
|
else {
|
|
@@ -6409,11 +6610,12 @@ class Wunderbaum {
|
|
|
6409
6610
|
}
|
|
6410
6611
|
/**
|
|
6411
6612
|
* Return the icon-function -> icon-definition mapping.
|
|
6613
|
+
* @deprecated Use {@link Wunderbaum.iconMaps}
|
|
6412
6614
|
*/
|
|
6413
6615
|
get iconMap() {
|
|
6414
6616
|
const map = this.options.iconMap;
|
|
6415
6617
|
if (typeof map === "string") {
|
|
6416
|
-
return
|
|
6618
|
+
return defaultIconMaps[map];
|
|
6417
6619
|
}
|
|
6418
6620
|
return map;
|
|
6419
6621
|
}
|
|
@@ -6466,7 +6668,38 @@ class Wunderbaum {
|
|
|
6466
6668
|
ext.init();
|
|
6467
6669
|
}
|
|
6468
6670
|
}
|
|
6469
|
-
/**
|
|
6671
|
+
/**
|
|
6672
|
+
* Calculate a *stable*, unique key for a node from its refKey (or title).
|
|
6673
|
+
* We also add information from the parent, because a refKey may occur multiple
|
|
6674
|
+
* times in a tree (but not as child of the same parent).
|
|
6675
|
+
* @internal
|
|
6676
|
+
*/
|
|
6677
|
+
_calculateKey(data, parent) {
|
|
6678
|
+
if (data.key) {
|
|
6679
|
+
// Always use an explicitly passed key
|
|
6680
|
+
return data.key;
|
|
6681
|
+
}
|
|
6682
|
+
// Auto-keys are optional, use a monotonic counter by default:
|
|
6683
|
+
if (!this.options.autoKeys) {
|
|
6684
|
+
return "" + ++WunderbaumNode.sequence;
|
|
6685
|
+
}
|
|
6686
|
+
// Add the parent's key to the hash. Assuming this was generated by the
|
|
6687
|
+
// same algorithm, this should incorporate the whole path:
|
|
6688
|
+
const s = (parent ? parent.key : "") + (data.refKey || data.title);
|
|
6689
|
+
// 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
6690
|
+
// https://security.stackexchange.com/q/209882/207588
|
|
6691
|
+
const h1 = murmurHash3(s, true);
|
|
6692
|
+
let key = "id_" + h1 + murmurHash3(h1 + s, true);
|
|
6693
|
+
// Check for collisions
|
|
6694
|
+
// (Most likely if the same title occurs multiple in the same parent).
|
|
6695
|
+
const existingNode = this.keyMap.get(key);
|
|
6696
|
+
if (existingNode) {
|
|
6697
|
+
key += "." + ++Wunderbaum.sequence;
|
|
6698
|
+
this.logWarn(`Node with existing key: '${existingNode}', using ${key}.`, data);
|
|
6699
|
+
}
|
|
6700
|
+
return key;
|
|
6701
|
+
}
|
|
6702
|
+
/** Add node to tree's bookkeeping data structures. @internal */
|
|
6470
6703
|
_registerNode(node) {
|
|
6471
6704
|
const key = node.key;
|
|
6472
6705
|
assert(key != null, `Missing key: '${node}'.`);
|
|
@@ -6483,7 +6716,7 @@ class Wunderbaum {
|
|
|
6483
6716
|
}
|
|
6484
6717
|
}
|
|
6485
6718
|
}
|
|
6486
|
-
/** Remove node from tree's bookkeeping data structures. */
|
|
6719
|
+
/** Remove node from tree's bookkeeping data structures. @internal */
|
|
6487
6720
|
_unregisterNode(node) {
|
|
6488
6721
|
// Remove refKey reference from map (if any)
|
|
6489
6722
|
const rk = node.refKey;
|
|
@@ -6602,6 +6835,16 @@ class Wunderbaum {
|
|
|
6602
6835
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6603
6836
|
return this._getNodeByRowIdx(bottomIdx);
|
|
6604
6837
|
}
|
|
6838
|
+
/** Return preceding visible node in the viewport. */
|
|
6839
|
+
_getPrevNodeInView(node, ofs = 1) {
|
|
6840
|
+
this.visitRows((n) => {
|
|
6841
|
+
node = n;
|
|
6842
|
+
if (ofs-- <= 0) {
|
|
6843
|
+
return false;
|
|
6844
|
+
}
|
|
6845
|
+
}, { reverse: true, start: node || this.getActiveNode() });
|
|
6846
|
+
return node;
|
|
6847
|
+
}
|
|
6605
6848
|
/** Return following visible node in the viewport. */
|
|
6606
6849
|
_getNextNodeInView(node, options) {
|
|
6607
6850
|
let ofs = (options === null || options === void 0 ? void 0 : options.ofs) || 1;
|
|
@@ -6853,22 +7096,39 @@ class Wunderbaum {
|
|
|
6853
7096
|
/** Run code, but defer rendering of viewport until done.
|
|
6854
7097
|
*
|
|
6855
7098
|
* ```js
|
|
6856
|
-
* tree.runWithDeferredUpdate(() => {
|
|
6857
|
-
* return
|
|
7099
|
+
* const res = tree.runWithDeferredUpdate(() => {
|
|
7100
|
+
* return someFunctionThatWouldUpdateManyNodes();
|
|
6858
7101
|
* });
|
|
6859
7102
|
* ```
|
|
6860
7103
|
*/
|
|
6861
|
-
runWithDeferredUpdate(func
|
|
7104
|
+
runWithDeferredUpdate(func) {
|
|
6862
7105
|
try {
|
|
6863
7106
|
this.enableUpdate(false);
|
|
6864
7107
|
const res = func();
|
|
6865
|
-
assert(!(res instanceof Promise), `Promise return not allowed: ${res}`);
|
|
7108
|
+
assert(!(res instanceof Promise), `Promise return not allowed (see 'runWithDeferredUpdateAsync()'): ${res}`);
|
|
6866
7109
|
return res;
|
|
6867
7110
|
}
|
|
6868
7111
|
finally {
|
|
6869
7112
|
this.enableUpdate(true);
|
|
6870
7113
|
}
|
|
6871
7114
|
}
|
|
7115
|
+
/** Run code, but defer rendering of viewport until done.
|
|
7116
|
+
*
|
|
7117
|
+
* ```js
|
|
7118
|
+
* const res = await tree.runWithDeferredUpdate(async () => {
|
|
7119
|
+
* return someAsyncFunctionThatWouldUpdateManyNodes();
|
|
7120
|
+
* });
|
|
7121
|
+
* ```
|
|
7122
|
+
*/
|
|
7123
|
+
async runWithDeferredUpdateAsync(func) {
|
|
7124
|
+
try {
|
|
7125
|
+
this.enableUpdate(false);
|
|
7126
|
+
return await func();
|
|
7127
|
+
}
|
|
7128
|
+
finally {
|
|
7129
|
+
this.enableUpdate(true);
|
|
7130
|
+
}
|
|
7131
|
+
}
|
|
6872
7132
|
/** Recursively expand all expandable nodes (triggers lazy load if needed). */
|
|
6873
7133
|
async expandAll(flag = true, options) {
|
|
6874
7134
|
await this.root.expandAll(flag, options);
|
|
@@ -6888,6 +7148,17 @@ class Wunderbaum {
|
|
|
6888
7148
|
getSelectedNodes(stopOnParents = false) {
|
|
6889
7149
|
return this.root.getSelectedNodes(stopOnParents);
|
|
6890
7150
|
}
|
|
7151
|
+
/**
|
|
7152
|
+
* Return an array of refKey values.
|
|
7153
|
+
*
|
|
7154
|
+
* RefKeys are unique identifiers for a node data, and are used to identify
|
|
7155
|
+
* clones.
|
|
7156
|
+
* If more than one node has the same refKey, it is only returned once.
|
|
7157
|
+
* @param selected if true, only return refKeys of selected nodes.
|
|
7158
|
+
*/
|
|
7159
|
+
getRefKeys(selected = false) {
|
|
7160
|
+
return this.root.getRefKeys(selected);
|
|
7161
|
+
}
|
|
6891
7162
|
/*
|
|
6892
7163
|
* Return an array of selected nodes.
|
|
6893
7164
|
*/
|
|
@@ -7169,6 +7440,18 @@ class Wunderbaum {
|
|
|
7169
7440
|
format(name_cb, connectors) {
|
|
7170
7441
|
return this.root.format(name_cb, connectors);
|
|
7171
7442
|
}
|
|
7443
|
+
/**
|
|
7444
|
+
* Always returns null (so a tree instance behaves as `tree.root`).
|
|
7445
|
+
*/
|
|
7446
|
+
get parent() {
|
|
7447
|
+
return null;
|
|
7448
|
+
}
|
|
7449
|
+
/**
|
|
7450
|
+
* Return a list of top-level nodes.
|
|
7451
|
+
*/
|
|
7452
|
+
get children() {
|
|
7453
|
+
return this.root.children || [];
|
|
7454
|
+
}
|
|
7172
7455
|
/**
|
|
7173
7456
|
* Return the active cell (`span.wb-col`) of the currently active node or null.
|
|
7174
7457
|
*/
|
|
@@ -7287,7 +7570,7 @@ class Wunderbaum {
|
|
|
7287
7570
|
}
|
|
7288
7571
|
/** Return true if any node title or grid cell is currently beeing edited.
|
|
7289
7572
|
*
|
|
7290
|
-
* See also {@link
|
|
7573
|
+
* See also {@link isEditingTitle}.
|
|
7291
7574
|
*/
|
|
7292
7575
|
isEditing() {
|
|
7293
7576
|
const focusElem = this.nodeListElement.querySelector("input:focus,select:focus");
|
|
@@ -7295,7 +7578,7 @@ class Wunderbaum {
|
|
|
7295
7578
|
}
|
|
7296
7579
|
/** Return true if any node is currently in edit-title mode.
|
|
7297
7580
|
*
|
|
7298
|
-
* See also {@link WunderbaumNode.isEditingTitle} and {@link
|
|
7581
|
+
* See also {@link WunderbaumNode.isEditingTitle} and {@link isEditing}.
|
|
7299
7582
|
*/
|
|
7300
7583
|
isEditingTitle() {
|
|
7301
7584
|
return this._callMethod("edit.isEditingTitle");
|
|
@@ -7315,7 +7598,7 @@ class Wunderbaum {
|
|
|
7315
7598
|
return res;
|
|
7316
7599
|
}
|
|
7317
7600
|
/** Write to `console.log` with tree name as prefix if opts.debugLevel >= 4.
|
|
7318
|
-
* @see {@link
|
|
7601
|
+
* @see {@link logDebug}
|
|
7319
7602
|
*/
|
|
7320
7603
|
log(...args) {
|
|
7321
7604
|
if (this.options.debugLevel >= 4) {
|
|
@@ -7324,7 +7607,7 @@ class Wunderbaum {
|
|
|
7324
7607
|
}
|
|
7325
7608
|
/** Write to `console.debug` with tree name as prefix if opts.debugLevel >= 4.
|
|
7326
7609
|
* and browser console level includes debug/verbose messages.
|
|
7327
|
-
* @see {@link
|
|
7610
|
+
* @see {@link log}
|
|
7328
7611
|
*/
|
|
7329
7612
|
logDebug(...args) {
|
|
7330
7613
|
if (this.options.debugLevel >= 4) {
|
|
@@ -7362,6 +7645,19 @@ class Wunderbaum {
|
|
|
7362
7645
|
console.warn(this.toString(), ...args); // eslint-disable-line no-console
|
|
7363
7646
|
}
|
|
7364
7647
|
}
|
|
7648
|
+
/** Emit a warning for deprecated methods. @internal */
|
|
7649
|
+
logDeprecate(method, options) {
|
|
7650
|
+
if (this.options.debugLevel >= 2) {
|
|
7651
|
+
let msg = `${this}: ${method} is deprecated`;
|
|
7652
|
+
if (options === null || options === void 0 ? void 0 : options.since) {
|
|
7653
|
+
msg += ` since ${options.since}`;
|
|
7654
|
+
}
|
|
7655
|
+
if (options === null || options === void 0 ? void 0 : options.hint) {
|
|
7656
|
+
msg += ` (${options.since})`;
|
|
7657
|
+
}
|
|
7658
|
+
console.warn(msg + "."); // eslint-disable-line no-console
|
|
7659
|
+
}
|
|
7660
|
+
}
|
|
7365
7661
|
/** Reset column widths to default. @since 0.10.0 */
|
|
7366
7662
|
resetColumns() {
|
|
7367
7663
|
this.columns.forEach((col) => {
|
|
@@ -7530,46 +7826,64 @@ class Wunderbaum {
|
|
|
7530
7826
|
this._focusNode = node;
|
|
7531
7827
|
}
|
|
7532
7828
|
/** Return the current selection/expansion/activation status. @experimental */
|
|
7533
|
-
getState(options) {
|
|
7829
|
+
getState(options = {}) {
|
|
7534
7830
|
var _a, _b;
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7831
|
+
const { activeKey = true, expandedKeys = false, selectedKeys = false, } = options;
|
|
7832
|
+
const expandSet = new Set();
|
|
7833
|
+
if (expandedKeys) {
|
|
7538
7834
|
for (const node of this) {
|
|
7539
|
-
if (node.
|
|
7540
|
-
|
|
7835
|
+
if (node.isExpanded() && node.hasChildren()) {
|
|
7836
|
+
expandSet.add(node.key);
|
|
7541
7837
|
}
|
|
7542
7838
|
}
|
|
7543
7839
|
}
|
|
7840
|
+
// Parents of active node are always expanded
|
|
7841
|
+
if (activeKey && this.activeNode) {
|
|
7842
|
+
this.activeNode.visitParents((n) => {
|
|
7843
|
+
if (n.parent) {
|
|
7844
|
+
expandSet.add(n.key);
|
|
7845
|
+
}
|
|
7846
|
+
}, false);
|
|
7847
|
+
}
|
|
7544
7848
|
const state = {
|
|
7849
|
+
expandedKeys: expandSet.size ? Array.from(expandSet) : undefined,
|
|
7545
7850
|
activeKey: (_b = (_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : null,
|
|
7546
7851
|
activeColIdx: this.activeColIdx,
|
|
7547
|
-
selectedKeys:
|
|
7548
|
-
?
|
|
7549
|
-
:
|
|
7550
|
-
expandedKeys: expandedKeys,
|
|
7852
|
+
selectedKeys: selectedKeys
|
|
7853
|
+
? this.getSelectedNodes().flatMap((n) => n.key)
|
|
7854
|
+
: undefined,
|
|
7551
7855
|
};
|
|
7552
7856
|
return state;
|
|
7553
7857
|
}
|
|
7554
7858
|
/** Apply selection/expansion/activation status. @experimental */
|
|
7555
|
-
setState(state, options) {
|
|
7556
|
-
|
|
7859
|
+
async setState(state, options = {}) {
|
|
7860
|
+
const { expandLazy = true } = options;
|
|
7861
|
+
return this.runWithDeferredUpdateAsync(async () => {
|
|
7557
7862
|
var _a, _b;
|
|
7558
|
-
if (state.
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7863
|
+
if (state.expandedKeys && state.expandedKeys.length) {
|
|
7864
|
+
if (expandLazy) {
|
|
7865
|
+
// Expand all keys recursively, even if they are not in the tree yet
|
|
7866
|
+
await this._loadLazyNodes(state.expandedKeys, {
|
|
7867
|
+
expand: true,
|
|
7868
|
+
noEvents: true,
|
|
7869
|
+
});
|
|
7562
7870
|
}
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7871
|
+
else {
|
|
7872
|
+
for (const key of state.expandedKeys) {
|
|
7873
|
+
(_a = this.findKey(key)) === null || _a === void 0 ? void 0 : _a.setExpanded(true);
|
|
7874
|
+
}
|
|
7567
7875
|
}
|
|
7568
7876
|
}
|
|
7569
7877
|
if (state.activeKey) {
|
|
7570
7878
|
this.setActiveNode(state.activeKey);
|
|
7571
7879
|
}
|
|
7572
|
-
if (state.
|
|
7880
|
+
if (state.selectedKeys) {
|
|
7881
|
+
this.selectAll(false);
|
|
7882
|
+
for (const key of state.selectedKeys) {
|
|
7883
|
+
(_b = this.findKey(key)) === null || _b === void 0 ? void 0 : _b.setSelected(true);
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
if (this.isCellNav() && state.activeColIdx != null) {
|
|
7573
7887
|
this.setColumn(state.activeColIdx);
|
|
7574
7888
|
}
|
|
7575
7889
|
});
|
|
@@ -7730,18 +8044,33 @@ class Wunderbaum {
|
|
|
7730
8044
|
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
7731
8045
|
* (defaults to sorting by title).
|
|
7732
8046
|
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
8047
|
+
* @deprecated use {@link sort}
|
|
7733
8048
|
*/
|
|
7734
8049
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
7735
|
-
this.
|
|
8050
|
+
this.logDeprecate("sortChildren()", { since: "0.14.0" });
|
|
8051
|
+
return this.sort({
|
|
8052
|
+
cmp: cmp ? cmp : undefined,
|
|
8053
|
+
deep: deep,
|
|
8054
|
+
propName: "title",
|
|
8055
|
+
});
|
|
7736
8056
|
}
|
|
7737
8057
|
/**
|
|
7738
8058
|
* Convenience method to implement column sorting.
|
|
7739
8059
|
* @see {@link WunderbaumNode.sortByProperty}.
|
|
7740
8060
|
* @since 0.11.0
|
|
8061
|
+
* @deprecated use {@link sort}
|
|
7741
8062
|
*/
|
|
7742
8063
|
sortByProperty(options) {
|
|
8064
|
+
this.logDeprecate("sortByProperty()", { since: "0.14.0" });
|
|
7743
8065
|
this.root.sortByProperty(options);
|
|
7744
8066
|
}
|
|
8067
|
+
/**
|
|
8068
|
+
* Sort nodes list by title or custom criteria.
|
|
8069
|
+
* @since 0.14.0
|
|
8070
|
+
*/
|
|
8071
|
+
sort(options) {
|
|
8072
|
+
this.root.sort(options);
|
|
8073
|
+
}
|
|
7745
8074
|
/** Convert tree to an array of plain objects.
|
|
7746
8075
|
*
|
|
7747
8076
|
* @param callback is called for every node, in order to allow
|
|
@@ -7993,12 +8322,10 @@ class Wunderbaum {
|
|
|
7993
8322
|
iconElem = document.createElement("i");
|
|
7994
8323
|
iconElem.className = "wb-icon";
|
|
7995
8324
|
}
|
|
7996
|
-
else if (
|
|
7997
|
-
// HTML
|
|
8325
|
+
else if (TEST_HTML.test(icon)) {
|
|
7998
8326
|
iconElem = elemFromHtml(icon);
|
|
7999
8327
|
}
|
|
8000
|
-
else if (
|
|
8001
|
-
// Image URL
|
|
8328
|
+
else if (TEST_FILE_PATH.test(icon)) {
|
|
8002
8329
|
iconElem = elemFromHtml(`<i class="wb-icon" style="background-image: url('${icon}');">`);
|
|
8003
8330
|
}
|
|
8004
8331
|
else {
|
|
@@ -8236,7 +8563,8 @@ class Wunderbaum {
|
|
|
8236
8563
|
}
|
|
8237
8564
|
/**
|
|
8238
8565
|
* Call `callback(node)` for all nodes in hierarchical order (depth-first, pre-order).
|
|
8239
|
-
* @see
|
|
8566
|
+
* @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
|
|
8567
|
+
* @see {@link WunderbaumNode.visit}.
|
|
8240
8568
|
*
|
|
8241
8569
|
* @param {function} callback the callback function.
|
|
8242
8570
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
@@ -8379,11 +8707,71 @@ class Wunderbaum {
|
|
|
8379
8707
|
*
|
|
8380
8708
|
* Previous data is cleared. Note that also column- and type defintions may
|
|
8381
8709
|
* be passed with the `source` object.
|
|
8710
|
+
* @see {@link Wunderbaum.reload} for a shortcut to reload the last ajax request
|
|
8711
|
+
* and restore the previous state.
|
|
8382
8712
|
*/
|
|
8383
|
-
load(source) {
|
|
8713
|
+
async load(source) {
|
|
8384
8714
|
this.clear();
|
|
8715
|
+
this._initialSource = source;
|
|
8385
8716
|
return this.root.load(source);
|
|
8386
8717
|
}
|
|
8718
|
+
/** Reload the tree and optionally restore state.
|
|
8719
|
+
* Source defaults to last ajax url if any.
|
|
8720
|
+
* Restoring the active node requires stable keys
|
|
8721
|
+
* @see {@link WunderbaumOptions.autoKeys}
|
|
8722
|
+
* @see {@link Wunderbaum.load}
|
|
8723
|
+
* @experimental
|
|
8724
|
+
*/
|
|
8725
|
+
async reload(options = {}) {
|
|
8726
|
+
const { source = this._initialSource, reactivate = true } = options;
|
|
8727
|
+
if (!source) {
|
|
8728
|
+
this.logWarn("No previous ajax source to reload.");
|
|
8729
|
+
return;
|
|
8730
|
+
}
|
|
8731
|
+
if (!reactivate) {
|
|
8732
|
+
return this.load(source);
|
|
8733
|
+
}
|
|
8734
|
+
const state = this.getState();
|
|
8735
|
+
await this.load(source);
|
|
8736
|
+
return this.setState(state);
|
|
8737
|
+
}
|
|
8738
|
+
/**
|
|
8739
|
+
* Make sure that all nodes in the given keyList are accessible.
|
|
8740
|
+
* This may include loading lazy parent nodes.
|
|
8741
|
+
* Recursively load (and optionally expand) all requested node paths.
|
|
8742
|
+
*/
|
|
8743
|
+
async _loadLazyNodes(keyList, options = {}) {
|
|
8744
|
+
const { expand = true } = options;
|
|
8745
|
+
const keySet = new Set(keyList);
|
|
8746
|
+
// Make sure that all parent nodes are loaded (and expand if requested)
|
|
8747
|
+
while (keySet.size > 0) {
|
|
8748
|
+
const pendingNodes = [];
|
|
8749
|
+
const curSet = new Set(keySet);
|
|
8750
|
+
for (const key of curSet) {
|
|
8751
|
+
const node = this.findKey(key);
|
|
8752
|
+
if (!node) {
|
|
8753
|
+
continue; // key not yet found (need to load lazy parent?)
|
|
8754
|
+
}
|
|
8755
|
+
keySet.delete(key);
|
|
8756
|
+
if (expand) {
|
|
8757
|
+
pendingNodes.push(node.setExpanded(true));
|
|
8758
|
+
}
|
|
8759
|
+
else if (node.isUnloaded()) {
|
|
8760
|
+
pendingNodes.push(node.loadLazy());
|
|
8761
|
+
}
|
|
8762
|
+
if (node._rowElem) {
|
|
8763
|
+
node._render(); // show spinner even is update is suppressed
|
|
8764
|
+
}
|
|
8765
|
+
}
|
|
8766
|
+
if (pendingNodes.length === 0) {
|
|
8767
|
+
// will not load any more nodes, so if if there are still keys
|
|
8768
|
+
// left in the set, we will never find them
|
|
8769
|
+
this.logWarn(`Could not expand ${keySet.size} nodes:`, keySet);
|
|
8770
|
+
break;
|
|
8771
|
+
}
|
|
8772
|
+
await Promise.allSettled(pendingNodes);
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8387
8775
|
/**
|
|
8388
8776
|
* Disable render requests during operations that would trigger many updates.
|
|
8389
8777
|
*
|
|
@@ -8481,8 +8869,20 @@ class Wunderbaum {
|
|
|
8481
8869
|
}
|
|
8482
8870
|
Wunderbaum.sequence = 0;
|
|
8483
8871
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8484
|
-
Wunderbaum.version = "v0.
|
|
8872
|
+
Wunderbaum.version = "v0.14.1"; // Set to semver by 'grunt release'
|
|
8485
8873
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8486
8874
|
Wunderbaum.util = util;
|
|
8875
|
+
/** A map of default iconMaps.
|
|
8876
|
+
* May be used as default, when passing partial icon definition maps:
|
|
8877
|
+
* ```js
|
|
8878
|
+
* const tree = new mar10.Wunderbaum({
|
|
8879
|
+
* ...
|
|
8880
|
+
* iconMap: Object.assign(Wunderbaum.iconMaps.bootstrap, {
|
|
8881
|
+
* folder: "bi bi-archive",
|
|
8882
|
+
* }),
|
|
8883
|
+
* });
|
|
8884
|
+
* ```
|
|
8885
|
+
*/
|
|
8886
|
+
Wunderbaum.iconMaps = defaultIconMaps;
|
|
8487
8887
|
|
|
8488
8888
|
export { Wunderbaum };
|