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/src/wb_node.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
ApplyCommandType,
|
|
14
14
|
ChangeType,
|
|
15
15
|
CheckboxOption,
|
|
16
|
-
ColumnDefinition,
|
|
17
16
|
ColumnEventInfoMap,
|
|
18
17
|
ExpandAllOptions,
|
|
19
18
|
IconOption,
|
|
@@ -37,7 +36,7 @@ import {
|
|
|
37
36
|
SetStatusOptions,
|
|
38
37
|
SortByPropertyOptions,
|
|
39
38
|
SortCallback,
|
|
40
|
-
|
|
39
|
+
SortOptions,
|
|
41
40
|
SourceType,
|
|
42
41
|
TooltipOption,
|
|
43
42
|
TristateType,
|
|
@@ -49,9 +48,11 @@ import {
|
|
|
49
48
|
ICON_WIDTH,
|
|
50
49
|
KEY_TO_NAVIGATION_MAP,
|
|
51
50
|
makeNodeTitleMatcher,
|
|
51
|
+
NODE_TYPE_FOLDER,
|
|
52
52
|
nodeTitleSorter,
|
|
53
53
|
RESERVED_TREE_SOURCE_KEYS,
|
|
54
|
-
|
|
54
|
+
TEST_FILE_PATH,
|
|
55
|
+
TEST_HTML,
|
|
55
56
|
TITLE_SPAN_PAD_Y,
|
|
56
57
|
} from "./common";
|
|
57
58
|
import { Deferred } from "./deferred";
|
|
@@ -186,13 +187,13 @@ export class WunderbaumNode {
|
|
|
186
187
|
_rowIdx: number | undefined = 0;
|
|
187
188
|
_rowElem: HTMLDivElement | undefined = undefined;
|
|
188
189
|
|
|
189
|
-
constructor(tree: Wunderbaum, parent: WunderbaumNode, data:
|
|
190
|
+
constructor(tree: Wunderbaum, parent: WunderbaumNode, data: WbNodeData) {
|
|
190
191
|
util.assert(!parent || parent.tree === tree, `Invalid parent: ${parent}`);
|
|
191
192
|
util.assert(!data.children, "'children' not allowed here");
|
|
192
193
|
|
|
193
194
|
this.tree = tree;
|
|
194
195
|
this.parent = parent;
|
|
195
|
-
this.key =
|
|
196
|
+
this.key = tree._calculateKey(data, parent);
|
|
196
197
|
this.title = "" + (data.title ?? "<" + this.key + ">");
|
|
197
198
|
this.expanded = !!data.expanded;
|
|
198
199
|
this.lazy = !!data.lazy;
|
|
@@ -212,7 +213,9 @@ export class WunderbaumNode {
|
|
|
212
213
|
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
213
214
|
|
|
214
215
|
// Selection
|
|
215
|
-
data.checkbox != null
|
|
216
|
+
data.checkbox != null
|
|
217
|
+
? (this.checkbox = util.intToBool(data.checkbox) as CheckboxOption)
|
|
218
|
+
: 0;
|
|
216
219
|
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
217
220
|
data.selected != null ? (this.selected = !!data.selected) : 0;
|
|
218
221
|
data.unselectable != null ? (this.unselectable = !!data.unselectable) : 0;
|
|
@@ -332,12 +335,19 @@ export class WunderbaumNode {
|
|
|
332
335
|
nodeData = [<WbNodeData>nodeData];
|
|
333
336
|
}
|
|
334
337
|
const forceExpand =
|
|
335
|
-
applyMinExpanLevel && _level < tree.options.minExpandLevel
|
|
338
|
+
applyMinExpanLevel && _level < tree.options.minExpandLevel;
|
|
336
339
|
for (const child of <WbNodeData[]>nodeData) {
|
|
337
340
|
const subChildren = child.children;
|
|
341
|
+
// Remove children property from source data because it should not be
|
|
342
|
+
// passed to the constructor of WunderbaumNode:
|
|
338
343
|
delete child.children;
|
|
339
344
|
|
|
340
345
|
const n = new WunderbaumNode(tree, this, child);
|
|
346
|
+
|
|
347
|
+
// Set `children` property again, so it can be used in `reload()`
|
|
348
|
+
if (subChildren != null) {
|
|
349
|
+
child.children = subChildren;
|
|
350
|
+
}
|
|
341
351
|
if (forceExpand && !n.isUnloaded()) {
|
|
342
352
|
n.expanded = true;
|
|
343
353
|
}
|
|
@@ -804,7 +814,7 @@ export class WunderbaumNode {
|
|
|
804
814
|
}
|
|
805
815
|
return l;
|
|
806
816
|
}
|
|
807
|
-
/** Return a string representing the
|
|
817
|
+
/** Return a string representing the hierarchical node path, e.g. "a/b/c".
|
|
808
818
|
* @param includeSelf
|
|
809
819
|
* @param part property name or callback
|
|
810
820
|
* @param separator
|
|
@@ -814,10 +824,6 @@ export class WunderbaumNode {
|
|
|
814
824
|
part: keyof WunderbaumNode | NodeAnyCallback = "title",
|
|
815
825
|
separator: string = "/"
|
|
816
826
|
) {
|
|
817
|
-
// includeSelf = includeSelf !== false;
|
|
818
|
-
// part = part || "title";
|
|
819
|
-
// separator = separator || "/";
|
|
820
|
-
|
|
821
827
|
let val;
|
|
822
828
|
const path: string[] = [];
|
|
823
829
|
const isFunc = typeof part === "function";
|
|
@@ -834,7 +840,7 @@ export class WunderbaumNode {
|
|
|
834
840
|
return path.join(separator);
|
|
835
841
|
}
|
|
836
842
|
|
|
837
|
-
/** Return the
|
|
843
|
+
/** Return the preceding node (under the same parent) or null. */
|
|
838
844
|
getPrevSibling(): WunderbaumNode | null {
|
|
839
845
|
const ac = this.parent.children!;
|
|
840
846
|
const idx = ac.indexOf(this);
|
|
@@ -866,7 +872,7 @@ export class WunderbaumNode {
|
|
|
866
872
|
return this.classes ? this.classes.has(className) : false;
|
|
867
873
|
}
|
|
868
874
|
|
|
869
|
-
/** Return true if node
|
|
875
|
+
/** Return true if node is the currently focused node. @since 0.9.0 */
|
|
870
876
|
hasFocus(): boolean {
|
|
871
877
|
return this.tree.focusNode === this;
|
|
872
878
|
}
|
|
@@ -927,7 +933,7 @@ export class WunderbaumNode {
|
|
|
927
933
|
* an expand operation is currently possible.
|
|
928
934
|
*/
|
|
929
935
|
isExpandable(andCollapsed = false): boolean {
|
|
930
|
-
// `false` is never expandable (
|
|
936
|
+
// `false` is never expandable (unofficial)
|
|
931
937
|
if ((andCollapsed && this.expanded) || <any>this.children === false) {
|
|
932
938
|
return false;
|
|
933
939
|
}
|
|
@@ -1002,12 +1008,12 @@ export class WunderbaumNode {
|
|
|
1002
1008
|
return !this.selected && !!this._partsel;
|
|
1003
1009
|
}
|
|
1004
1010
|
|
|
1005
|
-
/** Return true if this node has DOM
|
|
1011
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
1006
1012
|
isRadio(): boolean {
|
|
1007
1013
|
return !!this.parent.radiogroup || this.getOption("checkbox") === "radio";
|
|
1008
1014
|
}
|
|
1009
1015
|
|
|
1010
|
-
/** Return true if this node has DOM
|
|
1016
|
+
/** Return true if this node has DOM representation, i.e. is displayed in the viewport. */
|
|
1011
1017
|
isRendered(): boolean {
|
|
1012
1018
|
return !!this._rowElem;
|
|
1013
1019
|
}
|
|
@@ -1645,7 +1651,7 @@ export class WunderbaumNode {
|
|
|
1645
1651
|
protected _render_markup(opts: RenderOptions) {
|
|
1646
1652
|
const tree = this.tree;
|
|
1647
1653
|
const treeOptions = tree.options;
|
|
1648
|
-
const rowHeight = treeOptions.rowHeightPx
|
|
1654
|
+
const rowHeight = treeOptions.rowHeightPx;
|
|
1649
1655
|
const checkbox = this.getOption("checkbox");
|
|
1650
1656
|
const columns = tree.columns;
|
|
1651
1657
|
const level = this.getLevel();
|
|
@@ -1866,11 +1872,11 @@ export class WunderbaumNode {
|
|
|
1866
1872
|
const rowDiv = this._rowElem!;
|
|
1867
1873
|
|
|
1868
1874
|
// Row markup already exists
|
|
1869
|
-
const
|
|
1870
|
-
const
|
|
1875
|
+
const nodeSpan = rowDiv.querySelector("span.wb-node") as HTMLSpanElement;
|
|
1876
|
+
const expanderElem = nodeSpan.querySelector(
|
|
1871
1877
|
"i.wb-expander"
|
|
1872
1878
|
) as HTMLLIElement;
|
|
1873
|
-
const
|
|
1879
|
+
const checkboxElem = nodeSpan.querySelector(
|
|
1874
1880
|
"i.wb-checkbox"
|
|
1875
1881
|
) as HTMLLIElement;
|
|
1876
1882
|
|
|
@@ -1903,7 +1909,7 @@ export class WunderbaumNode {
|
|
|
1903
1909
|
rowDiv.classList.add(...typeInfo.classes);
|
|
1904
1910
|
}
|
|
1905
1911
|
|
|
1906
|
-
if (
|
|
1912
|
+
if (expanderElem) {
|
|
1907
1913
|
let image = null;
|
|
1908
1914
|
if (this._isLoading) {
|
|
1909
1915
|
image = iconMap.loading;
|
|
@@ -1918,14 +1924,17 @@ export class WunderbaumNode {
|
|
|
1918
1924
|
}
|
|
1919
1925
|
|
|
1920
1926
|
if (image == null) {
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1927
|
+
expanderElem.className = "wb-expander";
|
|
1928
|
+
expanderElem.classList.add("wb-indent");
|
|
1929
|
+
} else if (TEST_HTML.test(image)) {
|
|
1930
|
+
expanderElem.replaceWith(util.elemFromHtml(image));
|
|
1931
|
+
} else if (TEST_FILE_PATH.test(image)) {
|
|
1932
|
+
expanderElem.style.backgroundImage = `url('${image}')`;
|
|
1924
1933
|
} else {
|
|
1925
|
-
|
|
1934
|
+
expanderElem.className = "wb-expander " + image;
|
|
1926
1935
|
}
|
|
1927
1936
|
}
|
|
1928
|
-
if (
|
|
1937
|
+
if (checkboxElem) {
|
|
1929
1938
|
let cbclass = "wb-checkbox ";
|
|
1930
1939
|
if (this.isRadio()) {
|
|
1931
1940
|
cbclass += "wb-radio ";
|
|
@@ -1945,7 +1954,7 @@ export class WunderbaumNode {
|
|
|
1945
1954
|
cbclass += iconMap.checkUnchecked;
|
|
1946
1955
|
}
|
|
1947
1956
|
}
|
|
1948
|
-
|
|
1957
|
+
checkboxElem.className = cbclass;
|
|
1949
1958
|
}
|
|
1950
1959
|
// Fix active cell in cell-nav mode
|
|
1951
1960
|
if (!opts.isNew) {
|
|
@@ -1955,9 +1964,9 @@ export class WunderbaumNode {
|
|
|
1955
1964
|
colSpan.classList.remove("wb-error", "wb-invalid");
|
|
1956
1965
|
}
|
|
1957
1966
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
1958
|
-
const iconSpan =
|
|
1967
|
+
const iconSpan = nodeSpan.querySelector("i.wb-icon") as HTMLElement;
|
|
1959
1968
|
if (iconSpan) {
|
|
1960
|
-
this._createIcon(
|
|
1969
|
+
this._createIcon(nodeSpan, iconSpan, !expanderElem);
|
|
1961
1970
|
}
|
|
1962
1971
|
}
|
|
1963
1972
|
// Adjust column width
|
|
@@ -2217,6 +2226,7 @@ export class WunderbaumNode {
|
|
|
2217
2226
|
async setExpanded(flag: boolean = true, options?: SetExpandedOptions) {
|
|
2218
2227
|
const { force, scrollIntoView, immediate, resetLazy } = options ?? {};
|
|
2219
2228
|
const sendEvents = !options?.noEvents; // Default: send events
|
|
2229
|
+
|
|
2220
2230
|
if (
|
|
2221
2231
|
!flag &&
|
|
2222
2232
|
this.isExpanded() &&
|
|
@@ -2283,6 +2293,32 @@ export class WunderbaumNode {
|
|
|
2283
2293
|
setKey(key: string | null, refKey: string | null) {
|
|
2284
2294
|
throw new Error("Not yet implemented");
|
|
2285
2295
|
}
|
|
2296
|
+
// /**
|
|
2297
|
+
// * Calculate a *stable*, unique key for this node from its refKey (or title).
|
|
2298
|
+
// * We also add information from the parent, because a refKey may occur multiple
|
|
2299
|
+
// * times in a tree.
|
|
2300
|
+
// */
|
|
2301
|
+
// calcUniqueKey() {
|
|
2302
|
+
// // Assuming that the parent's key was calculated the same way, we implicitly
|
|
2303
|
+
// // involve the whole refKey-path:
|
|
2304
|
+
// const s = this.key + (this.refKey || this.title);
|
|
2305
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
2306
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
2307
|
+
// const h1 = util.murmurHash3(s, true);
|
|
2308
|
+
// return "id_" + h1 + util.murmurHash3(h1 + s, true);
|
|
2309
|
+
// // const l = [];
|
|
2310
|
+
// // // eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2311
|
+
// // let node: WunderbaumNode = this;
|
|
2312
|
+
// // while (node.parent) {
|
|
2313
|
+
// // l.unshift(node.refKey || node.key);
|
|
2314
|
+
// // node = node.parent;
|
|
2315
|
+
// // }
|
|
2316
|
+
// // const path = l.join("/");
|
|
2317
|
+
// // 32-bit has a high probability of collisions, so we pump up to 64-bit
|
|
2318
|
+
// // https://security.stackexchange.com/q/209882/207588
|
|
2319
|
+
// // const h1 = util.murmurHash3(path, true);
|
|
2320
|
+
// // return "id_" + h1 + util.murmurHash3(h1 + path, true);
|
|
2321
|
+
// }
|
|
2286
2322
|
|
|
2287
2323
|
/**
|
|
2288
2324
|
* Trigger a repaint, typically after a status or data change.
|
|
@@ -2320,6 +2356,24 @@ export class WunderbaumNode {
|
|
|
2320
2356
|
return nodeList;
|
|
2321
2357
|
}
|
|
2322
2358
|
|
|
2359
|
+
/**
|
|
2360
|
+
* Return an array of refKey values.
|
|
2361
|
+
*
|
|
2362
|
+
* RefKeys are unique identifiers for a node data, and are used to identify
|
|
2363
|
+
* clones.
|
|
2364
|
+
* If more than one node has the same refKey, it is only returned once.
|
|
2365
|
+
* @param selected if true, only return refKeys of selected nodes.
|
|
2366
|
+
*/
|
|
2367
|
+
getRefKeys(selected = false): string[] {
|
|
2368
|
+
const refKeys = new Set<string>();
|
|
2369
|
+
this.visit((node) => {
|
|
2370
|
+
if (node.refKey != null && (!selected || node.selected)) {
|
|
2371
|
+
refKeys.add(node.refKey);
|
|
2372
|
+
}
|
|
2373
|
+
});
|
|
2374
|
+
return Array.from(refKeys);
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2323
2377
|
/** Toggle the check/uncheck state. */
|
|
2324
2378
|
toggleSelected(options?: SetSelectedOptions): TristateType {
|
|
2325
2379
|
let flag = this.isSelected();
|
|
@@ -2521,9 +2575,11 @@ export class WunderbaumNode {
|
|
|
2521
2575
|
this.selected = flag;
|
|
2522
2576
|
if (selectMode === "hier") {
|
|
2523
2577
|
this.fixSelection3AfterClick();
|
|
2524
|
-
} else if (selectMode === "single") {
|
|
2578
|
+
} else if (selectMode === "single" && flag) {
|
|
2525
2579
|
tree.visit((n) => {
|
|
2526
|
-
n
|
|
2580
|
+
if (n !== this) {
|
|
2581
|
+
n.selected = false;
|
|
2582
|
+
}
|
|
2527
2583
|
});
|
|
2528
2584
|
}
|
|
2529
2585
|
}
|
|
@@ -2592,7 +2648,7 @@ export class WunderbaumNode {
|
|
|
2592
2648
|
_setStatusNode({
|
|
2593
2649
|
statusNodeType: status,
|
|
2594
2650
|
title:
|
|
2595
|
-
tree.options.strings
|
|
2651
|
+
tree.options.strings.loading +
|
|
2596
2652
|
(message ? " (" + message + ")" : ""),
|
|
2597
2653
|
checkbox: false,
|
|
2598
2654
|
colspan: true,
|
|
@@ -2605,7 +2661,7 @@ export class WunderbaumNode {
|
|
|
2605
2661
|
_setStatusNode({
|
|
2606
2662
|
statusNodeType: status,
|
|
2607
2663
|
title:
|
|
2608
|
-
tree.options.strings
|
|
2664
|
+
tree.options.strings.loadError +
|
|
2609
2665
|
(message ? " (" + message + ")" : ""),
|
|
2610
2666
|
checkbox: false,
|
|
2611
2667
|
colspan: true,
|
|
@@ -2618,7 +2674,7 @@ export class WunderbaumNode {
|
|
|
2618
2674
|
case "noData":
|
|
2619
2675
|
_setStatusNode({
|
|
2620
2676
|
statusNodeType: status,
|
|
2621
|
-
title: message || tree.options.strings
|
|
2677
|
+
title: message || tree.options.strings.noData,
|
|
2622
2678
|
checkbox: false,
|
|
2623
2679
|
colspan: true,
|
|
2624
2680
|
tooltip: details,
|
|
@@ -2646,35 +2702,19 @@ export class WunderbaumNode {
|
|
|
2646
2702
|
this.update();
|
|
2647
2703
|
}
|
|
2648
2704
|
|
|
2649
|
-
_sortChildren(cmp: SortCallback, deep: boolean): void {
|
|
2650
|
-
const cl = this.children;
|
|
2651
|
-
|
|
2652
|
-
if (!cl) {
|
|
2653
|
-
return;
|
|
2654
|
-
}
|
|
2655
|
-
cl.sort(cmp);
|
|
2656
|
-
if (deep) {
|
|
2657
|
-
for (let i = 0, l = cl.length; i < l; i++) {
|
|
2658
|
-
if (cl[i].children) {
|
|
2659
|
-
cl[i]._sortChildren(cmp, deep);
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
|
-
}
|
|
2664
|
-
|
|
2665
2705
|
/**
|
|
2666
2706
|
* Sort child list by title or custom criteria.
|
|
2667
2707
|
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
2668
2708
|
* (defaults to sorting by title).
|
|
2669
2709
|
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
2710
|
+
* @deprecated use {@link sort}
|
|
2670
2711
|
*/
|
|
2671
2712
|
sortChildren(
|
|
2672
2713
|
cmp: SortCallback | null = nodeTitleSorter,
|
|
2673
2714
|
deep: boolean = false
|
|
2674
2715
|
): void {
|
|
2675
|
-
this.
|
|
2676
|
-
this.
|
|
2677
|
-
// this.triggerModify("sort"); // TODO
|
|
2716
|
+
this.tree.logDeprecate("node.sortChildren()", { since: "0.14.0" });
|
|
2717
|
+
return this.sort({ cmp: cmp ? cmp : undefined, deep: deep });
|
|
2678
2718
|
}
|
|
2679
2719
|
|
|
2680
2720
|
/**
|
|
@@ -2699,82 +2739,159 @@ export class WunderbaumNode {
|
|
|
2699
2739
|
/**
|
|
2700
2740
|
* Convenience method to implement column sorting.
|
|
2701
2741
|
* @since 0.11.0
|
|
2742
|
+
* @deprecated use {@link sort}
|
|
2702
2743
|
*/
|
|
2703
2744
|
sortByProperty(options: SortByPropertyOptions) {
|
|
2704
|
-
|
|
2705
|
-
|
|
2745
|
+
this.tree.logDeprecate("node.sortByProperty()", { since: "0.14.0" });
|
|
2746
|
+
return this.sort(options);
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
/**
|
|
2750
|
+
* Implement column sorting.
|
|
2751
|
+
* @since 0.14.0
|
|
2752
|
+
*/
|
|
2753
|
+
sort(options: SortOptions) {
|
|
2754
|
+
const tree = this.tree;
|
|
2755
|
+
let {
|
|
2756
|
+
propName = undefined,
|
|
2706
2757
|
deep = true,
|
|
2707
|
-
|
|
2758
|
+
key = undefined,
|
|
2759
|
+
order = undefined,
|
|
2760
|
+
caseInsensitive = true,
|
|
2761
|
+
cmp = undefined,
|
|
2762
|
+
// Support click on column sort header:
|
|
2708
2763
|
updateColInfo = false,
|
|
2764
|
+
nativeOrderPropName = "_nativeIndex",
|
|
2765
|
+
colId = undefined,
|
|
2709
2766
|
} = options;
|
|
2710
2767
|
|
|
2711
|
-
|
|
2712
|
-
|
|
2768
|
+
propName ??= colId;
|
|
2769
|
+
if (propName === "*") {
|
|
2770
|
+
propName = "title";
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
const isFolder =
|
|
2774
|
+
tree.options.sortFoldersFirst === true
|
|
2775
|
+
? (node: WunderbaumNode) =>
|
|
2776
|
+
node.hasChildren() !== false || node.type === NODE_TYPE_FOLDER
|
|
2777
|
+
: tree.options.sortFoldersFirst;
|
|
2713
2778
|
|
|
2714
2779
|
if (updateColInfo) {
|
|
2715
|
-
colDef = this.tree["_columnsById"][options.colId!];
|
|
2780
|
+
const colDef = this.tree["_columnsById"][options.colId!];
|
|
2716
2781
|
util.assert(colDef, `Invalid colId specified: ${options.colId}`);
|
|
2717
|
-
order
|
|
2718
|
-
options.order ??
|
|
2719
|
-
util.rotate(colDef!.sortOrder, ["asc", "desc", undefined]);
|
|
2782
|
+
order ??= util.rotate(colDef.sortOrder, ["asc", "desc", undefined]);
|
|
2720
2783
|
|
|
2721
2784
|
for (const col of this.tree.columns) {
|
|
2722
2785
|
col.sortOrder = col === colDef ? order : undefined;
|
|
2723
2786
|
}
|
|
2724
|
-
|
|
2787
|
+
if (order === undefined) {
|
|
2788
|
+
propName = nativeOrderPropName;
|
|
2789
|
+
order = "asc";
|
|
2790
|
+
}
|
|
2725
2791
|
this.tree.update(ChangeType.colStructure);
|
|
2726
2792
|
} else {
|
|
2727
|
-
|
|
2793
|
+
propName ??= "title";
|
|
2794
|
+
order ??= "asc";
|
|
2728
2795
|
}
|
|
2729
2796
|
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
propName = "title";
|
|
2733
|
-
}
|
|
2734
|
-
if (order == null) {
|
|
2735
|
-
propName = nativeOrderPropName;
|
|
2736
|
-
order = "asc";
|
|
2737
|
-
}
|
|
2738
|
-
this.logDebug(`sortByProperty(), propName=${propName}, ${order}`, options);
|
|
2739
|
-
util.assert(propName, "No property name specified");
|
|
2797
|
+
this.logDebug(`sort(), propName=${propName}, ${order}`, options);
|
|
2798
|
+
util.assert(propName || cmp || key, "No `propName` or `key` specified");
|
|
2740
2799
|
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2800
|
+
// Define a key callback from the parameters we have
|
|
2801
|
+
if (key == null && cmp == null) {
|
|
2802
|
+
key = (node) => {
|
|
2803
|
+
let val;
|
|
2804
|
+
if (NODE_DICT_PROPS.has(<string>propName)) {
|
|
2805
|
+
val = node[propName as keyof WunderbaumNode];
|
|
2806
|
+
} else {
|
|
2807
|
+
val = node.data[propName!];
|
|
2808
|
+
}
|
|
2809
|
+
if (caseInsensitive && typeof val === "string") {
|
|
2810
|
+
val = val.toLowerCase();
|
|
2811
|
+
}
|
|
2812
|
+
return val;
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
2815
|
+
// Define a compare callback that uses the key callback
|
|
2816
|
+
if (cmp) {
|
|
2817
|
+
util.assert(!key, "`key` and `cmp` are mutually exclusive");
|
|
2818
|
+
tree.logDeprecate("SortOptions.cmp", {
|
|
2819
|
+
since: "0.14.0",
|
|
2820
|
+
hint: "use the `key` callback instead",
|
|
2821
|
+
});
|
|
2822
|
+
} else {
|
|
2823
|
+
if (options.propName || options.caseInsensitive) {
|
|
2824
|
+
tree.logWarn("sort(): ignoring propName, caseInsensitive");
|
|
2762
2825
|
}
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2826
|
+
|
|
2827
|
+
cmp = (a, b) => {
|
|
2828
|
+
if (isFolder) {
|
|
2829
|
+
const isFolderA = isFolder(a);
|
|
2830
|
+
if (isFolderA !== isFolder(b)) {
|
|
2831
|
+
return isFolderA ? -1 : 1;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
let x = key!(a);
|
|
2835
|
+
let y = key!(b);
|
|
2836
|
+
// Assure we have reasonable comparisons with null values:
|
|
2837
|
+
if (x == null) {
|
|
2838
|
+
x = typeof y === "string" ? "" : 0;
|
|
2839
|
+
} else if (typeof x === "boolean") {
|
|
2840
|
+
x = x ? 1 : 0;
|
|
2841
|
+
}
|
|
2842
|
+
if (y == null) {
|
|
2843
|
+
y = typeof x === "string" ? "" : 0;
|
|
2844
|
+
} else if (typeof y === "boolean") {
|
|
2845
|
+
y = y ? 1 : 0;
|
|
2766
2846
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2847
|
+
|
|
2848
|
+
if (order === "desc") {
|
|
2849
|
+
return x === y ? 0 : x > y ? -1 : 1;
|
|
2769
2850
|
}
|
|
2851
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
function _sortChildren(cl: WunderbaumNode[]): void {
|
|
2856
|
+
if (!cl) {
|
|
2857
|
+
return;
|
|
2770
2858
|
}
|
|
2771
|
-
|
|
2772
|
-
|
|
2859
|
+
cl.sort(cmp);
|
|
2860
|
+
if (deep) {
|
|
2861
|
+
for (let i = 0, l = cl.length; i < l; i++) {
|
|
2862
|
+
if (cl[i].children) {
|
|
2863
|
+
_sortChildren(cl[i].children!);
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2773
2866
|
}
|
|
2774
|
-
|
|
2775
|
-
|
|
2867
|
+
}
|
|
2868
|
+
if (this.children) {
|
|
2869
|
+
_sortChildren(this.children);
|
|
2870
|
+
}
|
|
2871
|
+
this.tree.update(ChangeType.structure);
|
|
2872
|
+
// this.triggerModify("sort"); // TODO
|
|
2873
|
+
}
|
|
2776
2874
|
|
|
2777
|
-
|
|
2875
|
+
/**
|
|
2876
|
+
* Re-apply current sorting if any (use after lazy load).
|
|
2877
|
+
* Example:
|
|
2878
|
+
* ```js
|
|
2879
|
+
* load: function (e) {
|
|
2880
|
+
* // Whe loading a lazy branch, apply current sort order if any
|
|
2881
|
+
* e.node.resort();
|
|
2882
|
+
* },
|
|
2883
|
+
* ```
|
|
2884
|
+
* @since 0.14.0
|
|
2885
|
+
*/
|
|
2886
|
+
resort(options: SortOptions = {}): void {
|
|
2887
|
+
for (const colDef of this.tree.columns) {
|
|
2888
|
+
if (colDef.sortOrder) {
|
|
2889
|
+
options.colId = colDef.id;
|
|
2890
|
+
options.order = colDef.sortOrder;
|
|
2891
|
+
this.sort(options);
|
|
2892
|
+
break;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2778
2895
|
}
|
|
2779
2896
|
|
|
2780
2897
|
/**
|
|
@@ -2821,7 +2938,8 @@ export class WunderbaumNode {
|
|
|
2821
2938
|
* @param {function} callback the callback function.
|
|
2822
2939
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
2823
2940
|
* its children only.
|
|
2824
|
-
* @see
|
|
2941
|
+
* @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
|
|
2942
|
+
* @see {@link Wunderbaum.visit}.
|
|
2825
2943
|
*/
|
|
2826
2944
|
visit(
|
|
2827
2945
|
callback: NodeVisitCallback,
|