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/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
- SortOrderType,
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
- TEST_IMG,
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: any) {
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 = "" + (data.key ?? ++WunderbaumNode.sequence);
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 ? util.intToBool(data.checkbox) : 0;
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 hierachical node path, e.g. "a/b/c".
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 preceeding node (under the same parent) or null. */
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 ist the currently focused node. @since 0.9.0 */
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 (unoffical)
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 representaion, i.e. is displayed in the viewport. */
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 representaion, i.e. is displayed in the viewport. */
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 nodeElem = rowDiv.querySelector("span.wb-node") as HTMLSpanElement;
1870
- const expanderSpan = nodeElem.querySelector(
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 checkboxSpan = nodeElem.querySelector(
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 (expanderSpan) {
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
- expanderSpan.classList.add("wb-indent");
1922
- } else if (TEST_IMG.test(image)) {
1923
- expanderSpan.style.backgroundImage = `url('${image}')`;
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
- expanderSpan.className = "wb-expander " + image;
1934
+ expanderElem.className = "wb-expander " + image;
1926
1935
  }
1927
1936
  }
1928
- if (checkboxSpan) {
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
- checkboxSpan.className = cbclass;
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 = nodeElem.querySelector("i.wb-icon") as HTMLElement;
1967
+ const iconSpan = nodeSpan.querySelector("i.wb-icon") as HTMLElement;
1959
1968
  if (iconSpan) {
1960
- this._createIcon(nodeElem, iconSpan, !expanderSpan);
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.selected = false;
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!.loading +
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!.loadError +
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!.noData,
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._sortChildren(cmp || nodeTitleSorter, deep);
2676
- this.tree.update(ChangeType.structure);
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
- const {
2705
- caseInsensitive = true,
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
- nativeOrderPropName = "_nativeIndex",
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
- let order: SortOrderType;
2712
- let colDef: ColumnDefinition | null;
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
- order = options.order ?? "asc";
2793
+ propName ??= "title";
2794
+ order ??= "asc";
2728
2795
  }
2729
2796
 
2730
- let propName = options.propName ?? (options.colId || "");
2731
- if (propName === "*") {
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
- const cmp = (a: WunderbaumNode, b: WunderbaumNode) => {
2742
- let av, bv;
2743
- if (NODE_DICT_PROPS.has(<string>propName)) {
2744
- av = a[propName as keyof WunderbaumNode];
2745
- bv = b[propName as keyof WunderbaumNode];
2746
- } else {
2747
- av = a.data[propName];
2748
- bv = b.data[propName];
2749
- }
2750
- if (av == null && bv == null) {
2751
- return 0;
2752
- }
2753
- if (av == null) {
2754
- av = typeof bv === "string" ? "" : 0;
2755
- } else if (typeof av === "boolean") {
2756
- av = av ? 1 : 0;
2757
- }
2758
- if (bv == null) {
2759
- bv = typeof av === "string" ? "" : 0;
2760
- } else if (typeof bv === "boolean") {
2761
- bv = bv ? 1 : 0;
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
- if (caseInsensitive) {
2764
- if (typeof av === "string") {
2765
- av = av.toLowerCase();
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
- if (typeof bv === "string") {
2768
- bv = bv.toLowerCase();
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
- if (order === "desc") {
2772
- return av === bv ? 0 : av > bv ? -1 : 1;
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
- return av === bv ? 0 : av > bv ? 1 : -1;
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
- return this.sortChildren(cmp, deep);
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 {@link IterableIterator<WunderbaumNode>}, {@link Wunderbaum.visit}.
2941
+ * @see `wb_node.WunderbaumNode.IterableIterator<WunderbaumNode>`
2942
+ * @see {@link Wunderbaum.visit}.
2825
2943
  */
2826
2944
  visit(
2827
2945
  callback: NodeVisitCallback,