@syncfusion/ej2-navigations 34.2.2 → 34.2.4

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.
Files changed (79) hide show
  1. package/dist/ej2-navigations.min.js +2 -2
  2. package/dist/ej2-navigations.umd.min.js +2 -2
  3. package/dist/ej2-navigations.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-navigations.es2015.js +310 -563
  5. package/dist/es6/ej2-navigations.es2015.js.map +1 -1
  6. package/dist/es6/ej2-navigations.es5.js +312 -563
  7. package/dist/es6/ej2-navigations.es5.js.map +1 -1
  8. package/dist/global/ej2-navigations.min.js +2 -2
  9. package/dist/global/ej2-navigations.min.js.map +1 -1
  10. package/dist/global/index.d.ts +1 -1
  11. package/package.json +3 -3
  12. package/src/common/menu-base.js +14 -6
  13. package/src/treeview/treeview-model.d.ts +1 -0
  14. package/src/treeview/treeview.d.ts +7 -210
  15. package/src/treeview/treeview.js +298 -557
  16. package/styles/bds-lite.css +3 -0
  17. package/styles/bds.css +3 -0
  18. package/styles/bootstrap-dark-lite.css +3 -0
  19. package/styles/bootstrap-dark.css +3 -0
  20. package/styles/bootstrap-lite.css +3 -0
  21. package/styles/bootstrap.css +3 -0
  22. package/styles/bootstrap4-lite.css +3 -0
  23. package/styles/bootstrap4.css +3 -0
  24. package/styles/bootstrap5-dark-lite.css +3 -0
  25. package/styles/bootstrap5-dark.css +3 -0
  26. package/styles/bootstrap5-lite.css +3 -0
  27. package/styles/bootstrap5.3-lite.css +3 -0
  28. package/styles/bootstrap5.3.css +3 -0
  29. package/styles/bootstrap5.css +3 -0
  30. package/styles/fabric-dark-lite.css +3 -0
  31. package/styles/fabric-dark.css +3 -0
  32. package/styles/fabric-lite.css +3 -0
  33. package/styles/fabric.css +3 -0
  34. package/styles/fluent-dark-lite.css +3 -0
  35. package/styles/fluent-dark.css +3 -0
  36. package/styles/fluent-lite.css +3 -0
  37. package/styles/fluent.css +3 -0
  38. package/styles/fluent2-lite.css +3 -0
  39. package/styles/fluent2.css +3 -0
  40. package/styles/highcontrast-light-lite.css +3 -0
  41. package/styles/highcontrast-light.css +3 -0
  42. package/styles/highcontrast-lite.css +3 -0
  43. package/styles/highcontrast.css +3 -0
  44. package/styles/material-dark-lite.css +3 -0
  45. package/styles/material-dark.css +3 -0
  46. package/styles/material-lite.css +3 -0
  47. package/styles/material.css +3 -0
  48. package/styles/material3-dark-lite.css +3 -0
  49. package/styles/material3-dark.css +3 -0
  50. package/styles/material3-lite.css +3 -0
  51. package/styles/material3.css +3 -0
  52. package/styles/tailwind-dark-lite.css +3 -0
  53. package/styles/tailwind-dark.css +3 -0
  54. package/styles/tailwind-lite.css +3 -0
  55. package/styles/tailwind.css +3 -0
  56. package/styles/tailwind3-lite.css +3 -0
  57. package/styles/tailwind3.css +3 -0
  58. package/styles/toolbar/_layout.scss +4 -0
  59. package/styles/toolbar/bds.css +3 -0
  60. package/styles/toolbar/bootstrap-dark.css +3 -0
  61. package/styles/toolbar/bootstrap.css +3 -0
  62. package/styles/toolbar/bootstrap4.css +3 -0
  63. package/styles/toolbar/bootstrap5-dark.css +3 -0
  64. package/styles/toolbar/bootstrap5.3.css +3 -0
  65. package/styles/toolbar/bootstrap5.css +3 -0
  66. package/styles/toolbar/fabric-dark.css +3 -0
  67. package/styles/toolbar/fabric.css +3 -0
  68. package/styles/toolbar/fluent-dark.css +3 -0
  69. package/styles/toolbar/fluent.css +3 -0
  70. package/styles/toolbar/fluent2.css +3 -0
  71. package/styles/toolbar/highcontrast-light.css +3 -0
  72. package/styles/toolbar/highcontrast.css +3 -0
  73. package/styles/toolbar/material-dark.css +3 -0
  74. package/styles/toolbar/material.css +3 -0
  75. package/styles/toolbar/material3-dark.css +3 -0
  76. package/styles/toolbar/material3.css +3 -0
  77. package/styles/toolbar/tailwind-dark.css +3 -0
  78. package/styles/toolbar/tailwind.css +3 -0
  79. package/styles/toolbar/tailwind3.css +3 -0
@@ -228,54 +228,25 @@ var TreeView = /** @class */ (function (_super) {
228
228
  _this.isDropIn = false;
229
229
  _this.OldCheckedData = [];
230
230
  _this.isHiddenItem = false;
231
- // ── Virtualization state ──────────────────────────────────────────────────────────────
232
- /** Flat ordered list of all currently-visible (non-collapsed) tree nodes. */
231
+ // Virtualization state
233
232
  _this.virtualFlatNodes = [];
234
- /** Maps node id → index in virtualFlatNodes for O(1) lookups. */
235
233
  _this.virtualNodeMap = new Map();
236
- /** Set of expanded node IDs for O(1) lookup performance during flattening (avoids array indexOf). */
237
234
  _this.virtualExpandedNodesSet = new Set();
238
- /** Set of checked node IDs for O(1) lookup performance during flattening. */
239
235
  _this.virtualCheckedNodesSet = new Set();
240
- /** Set of indeterminate node IDs — persists across flattenVisibleNodes() rebuilds. */
241
236
  _this.virtualIndeterminateNodesSet = new Set();
242
- /** Set of selected node IDs for O(1) lookup performance during flattening. */
243
237
  _this.virtualSelectedNodesSet = new Set();
244
- /** Row height in pixels, measured from the first rendered item or set to 40 as a default. */
245
238
  _this.virtualItemHeight = 40;
246
- /** Height of the scrollable container in pixels. */
247
239
  _this.virtualViewportHeight = 0;
248
- /** scrollTop captured during the previous scroll handler invocation. */
249
240
  _this.virtualPrevScrollTop = 0;
250
- /** Index of the first rendered row in the current window. */
251
241
  _this.virtualStartIndex = 0;
252
- /** Index one past the last rendered row in the current window. */
253
242
  _this.virtualEndIndex = 0;
254
- /** Extra rows rendered above and below the visible window to smooth scrolling. */
255
243
  _this.virtualOverscanCount = 3;
256
- /** RequestAnimationFrame handle used for throttling scroll events. */
257
244
  _this.virtualScrollRAF = null;
258
- /** The `.e-mask-parent` overlay element shown during fast scroll. */
259
245
  _this.virtualMaskElement = null;
260
- /** Bound scroll handler stored for later removeEventListener. */
261
246
  _this.virtualScrollBound = null;
262
- /** Skeleton component instances inside the mask overlay. */
263
247
  _this.skeletonInstances = [];
264
- /** Set to true whenever the flat node list must be rebuilt on next render. */
265
248
  _this.virtualTreeDirty = false;
266
- /** The data-uid of the currently keyboard-focused node in virtual mode (persists across re-renders). */
267
249
  _this.virtualFocusedId = null;
268
- /**
269
- * Set to true by the document-level `keydown` handler when the user presses
270
- * the Tab key. The browser will auto-scroll the focused element into view
271
- * immediately after focus, which fires a `scroll` event on the TreeView
272
- * container. We want to skip the re-render that the virtual scroll handler
273
- * would otherwise trigger, because the element being focused is already
274
- * rendered (it is the single `<li tabindex="0">` of the current window) and
275
- * re-rendering would tear down the DOM element that is currently holding
276
- * focus. The flag is consumed (reset to `false`) inside `onVirtualScroll`
277
- * after the auto-scroll has been handled.
278
- */
279
250
  _this.virtualSkipNextScrollRender = false;
280
251
  return _this;
281
252
  }
@@ -578,16 +549,6 @@ var TreeView = /** @class */ (function (_super) {
578
549
  }
579
550
  };
580
551
  TreeView.prototype.renderItems = function (isSorted) {
581
- // In virtual mode (with a fixed height) create only the container <ul> —
582
- // initVirtualization will populate the items directly, avoiding the O(n)
583
- // ListBase.createList cost.
584
- // updateCheckedStateFromDS / updateParentCheckState are intentionally skipped here:
585
- // they call getCheckedNodeDetails which traverses the DOM (no DOM exists yet in
586
- // virtual mode) and would throw on empty recursion results. The initial checked
587
- // state is derived from the raw data inside flattenNodeRecursive instead.
588
- //
589
- // When enableVirtualization is true but height is NOT provided, we fall through
590
- // to the normal (non-virtualized) rendering path so the tree is still visible.
591
552
  if (this.enableVirtualization && !isNOU(this.height)) {
592
553
  this.ulElement = this.createElement('ul', {
593
554
  className: PARENTITEM + ' ' + 'e-ul',
@@ -1674,7 +1635,6 @@ var TreeView = /** @class */ (function (_super) {
1674
1635
  this.checkDisabledState(id);
1675
1636
  }
1676
1637
  if (this.enableVirtualization) {
1677
- // Clear any indeterminate state — node is now fully checked
1678
1638
  this.virtualIndeterminateNodesSet.delete(id);
1679
1639
  var actualIndex = this.virtualNodeMap.get(id);
1680
1640
  if (!isNOU(actualIndex) && actualIndex > -1) {
@@ -1695,19 +1655,16 @@ var TreeView = /** @class */ (function (_super) {
1695
1655
  var actualIndex = this.virtualNodeMap.get(id);
1696
1656
  if (!isNOU(actualIndex) && actualIndex > -1) {
1697
1657
  if (isIndeterminate) {
1698
- // Parent is transitioning to indeterminate — preserve children states
1699
- // and mark this node as 'indeterminate' so scroll/collapse re-renders correctly.
1700
1658
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = 'indeterminate';
1701
- // Also record it in the persistent indeterminate set so flattenVisibleNodes()
1702
- // can restore this state after a full rebuild (e.g. on collapse).
1703
1659
  this.virtualIndeterminateNodesSet.add(id);
1704
1660
  }
1705
1661
  else {
1706
- // Node is being fully unchecked — clear indeterminate tracking too
1707
1662
  this.virtualIndeterminateNodesSet.delete(id);
1708
1663
  this.virtualCheckedNodesSet.delete(id);
1709
1664
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = false;
1710
- this.treeData[parseInt(id.toString(), 10) - 1].isChecked = false;
1665
+ if (this.treeData && this.fields && this.fields.isChecked) {
1666
+ this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
1667
+ }
1711
1668
  if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
1712
1669
  this.setVirtualChildCheckState(id, false);
1713
1670
  }
@@ -1752,8 +1709,6 @@ var TreeView = /** @class */ (function (_super) {
1752
1709
  }
1753
1710
  }
1754
1711
  }
1755
- // Collapsed children are not present in virtualFlatNodes, so walk the raw data
1756
- // to keep this.treeData and checkedNodes in sync for hidden descendants too.
1757
1712
  var collapsedChildren = this.getChildNodes(this.treeData, parentId);
1758
1713
  if (collapsedChildren) {
1759
1714
  for (var i = 0; i < collapsedChildren.length; i++) {
@@ -1899,7 +1854,6 @@ var TreeView = /** @class */ (function (_super) {
1899
1854
  }
1900
1855
  this.isLoaded = true;
1901
1856
  this.isNodeDropped = false;
1902
- // Initialise (or re-initialise) virtual mode after data binding has settled
1903
1857
  if (this.enableVirtualization && !isNOU(this.height)) {
1904
1858
  this.initVirtualization();
1905
1859
  }
@@ -2039,9 +1993,6 @@ var TreeView = /** @class */ (function (_super) {
2039
1993
  };
2040
1994
  TreeView.prototype.expandNode = function (currLi, icon, loaded, isKeyboardNav) {
2041
1995
  var _this = this;
2042
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
2043
- // Only engage when height is also set; without a fixed height the tree falls
2044
- // back to normal rendering and the standard expand path must be used.
2045
1996
  if (this.enableVirtualization && !isNOU(this.height)) {
2046
1997
  var id = currLi ? currLi.getAttribute('data-uid') : null;
2047
1998
  if (id) {
@@ -2170,9 +2121,6 @@ var TreeView = /** @class */ (function (_super) {
2170
2121
  };
2171
2122
  TreeView.prototype.nodeCollapseAction = function (currLi, icon, colArgs, isKeyboardNav) {
2172
2123
  var _this = this;
2173
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
2174
- // Only engage when height is also set; without a fixed height the tree falls
2175
- // back to normal rendering and the standard collapse path must be used.
2176
2124
  if (this.enableVirtualization && !isNOU(this.height)) {
2177
2125
  var id = currLi ? currLi.getAttribute('data-uid') : null;
2178
2126
  if (id) {
@@ -2643,7 +2591,6 @@ var TreeView = /** @class */ (function (_super) {
2643
2591
  li.setAttribute('tabindex', '0');
2644
2592
  EventHandler.add(li, 'blur', this.focusOut, this);
2645
2593
  this.updateIdAttr(focusedNode, li);
2646
- // Track focused id for virtual mode re-render restoration
2647
2594
  if (this.enableVirtualization) {
2648
2595
  this.virtualFocusedId = li.getAttribute('data-uid');
2649
2596
  }
@@ -2656,6 +2603,13 @@ var TreeView = /** @class */ (function (_super) {
2656
2603
  if (!isNOU(id) && this.selectedNodes.indexOf(id) === -1) {
2657
2604
  this.selectedNodes.push(id);
2658
2605
  }
2606
+ if (this.enableVirtualization && !isNOU(this.height)) {
2607
+ this.virtualSelectedNodesSet.add(id);
2608
+ var actualIndex = this.virtualNodeMap.get(id);
2609
+ if (!isNOU(actualIndex) && actualIndex > -1) {
2610
+ this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
2611
+ }
2612
+ }
2659
2613
  };
2660
2614
  TreeView.prototype.removeSelect = function (liEle) {
2661
2615
  if (this.allowMultiSelection) {
@@ -2669,6 +2623,14 @@ var TreeView = /** @class */ (function (_super) {
2669
2623
  if (index > -1) {
2670
2624
  this.selectedNodes.splice(index, 1);
2671
2625
  }
2626
+ if (this.enableVirtualization && !isNOU(this.height)) {
2627
+ var id = liEle.getAttribute('data-uid');
2628
+ this.virtualSelectedNodesSet.delete(id);
2629
+ var actualIndex = this.virtualNodeMap.get(id);
2630
+ if (!isNOU(actualIndex) && actualIndex > -1) {
2631
+ this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
2632
+ }
2633
+ }
2672
2634
  };
2673
2635
  TreeView.prototype.removeSelectAll = function () {
2674
2636
  var selectedLI = this.element.querySelectorAll('.' + ACTIVE);
@@ -2683,6 +2645,15 @@ var TreeView = /** @class */ (function (_super) {
2683
2645
  }
2684
2646
  removeClass(selectedLI, ACTIVE);
2685
2647
  this.setProperties({ selectedNodes: [] }, true);
2648
+ if (this.enableVirtualization && !isNOU(this.height)) {
2649
+ this.virtualSelectedNodesSet.clear();
2650
+ for (var i = 0; i < this.virtualFlatNodes.length; i++) {
2651
+ var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
2652
+ if (node.selected) {
2653
+ node.selected = false;
2654
+ }
2655
+ }
2656
+ }
2686
2657
  };
2687
2658
  TreeView.prototype.getSelectEvent = function (currLi, action, e) {
2688
2659
  var nodeData = this.getNodeData(currLi);
@@ -2751,12 +2722,6 @@ var TreeView = /** @class */ (function (_super) {
2751
2722
  }
2752
2723
  };
2753
2724
  TreeView.prototype.nodeExpandAction = function (currLi, icon, expandChild, callback, isFromExpandAll, isKeyboardNav) {
2754
- // In virtual mode (with a fixed height) the flat <li> never has a child <ul>
2755
- // in the DOM, so the PARENTITEM check below would always fail and fall into
2756
- // renderChildNodes(), which corrupts the virtual DOM.
2757
- // Delegate directly to expandNode() which has the handleVirtualExpand fast-path.
2758
- // When height is not provided, fall through to normal expand logic below since
2759
- // the tree is rendered with a full nested DOM structure.
2760
2725
  if (this.enableVirtualization && !isNOU(this.height)) {
2761
2726
  this.expandNode(currLi, icon, undefined, isKeyboardNav);
2762
2727
  return;
@@ -2874,9 +2839,6 @@ var TreeView = /** @class */ (function (_super) {
2874
2839
  });
2875
2840
  };
2876
2841
  TreeView.prototype.navigateToFocus = function (isUp) {
2877
- // In virtual mode (with a fixed height) the element itself is the scroll
2878
- // container; use ensureVirtualNodeVisible. When height is not provided,
2879
- // the tree uses normal rendering, so fall through to the standard scroll logic.
2880
2842
  if (this.enableVirtualization && !isNOU(this.height)) {
2881
2843
  var focusedLi = this.getFocusedNode();
2882
2844
  if (focusedLi) {
@@ -3308,7 +3270,6 @@ var TreeView = /** @class */ (function (_super) {
3308
3270
  };
3309
3271
  TreeView.prototype.focusNextNode = function (li, isTowards) {
3310
3272
  if (this.enableVirtualization && !isNOU(this.height)) {
3311
- // Determine the id of the node one step in the requested direction
3312
3273
  var currentId = li ? li.getAttribute('data-uid') : null;
3313
3274
  var currentIdx = -1;
3314
3275
  if (!isNOU(currentId)) {
@@ -3320,26 +3281,14 @@ var TreeView = /** @class */ (function (_super) {
3320
3281
  return;
3321
3282
  }
3322
3283
  var targetId = this.virtualFlatNodes[parseInt(targetIdx.toString(), 10)].id;
3323
- // ── Fix 1: set virtualFocusedId BEFORE calling ensureVirtualNodeVisible ──
3324
- // renderVirtualItems() is called synchronously inside ensureVirtualNodeVisible
3325
- // when a re-render is needed. If virtualFocusedId is still the old value at
3326
- // that point, focus restoration falls back to the first item. Setting it here
3327
- // ensures renderVirtualItems always restores focus to the correct target node.
3328
3284
  this.virtualFocusedId = targetId;
3329
- // ── Fix 2: clear focus classes from ALL nested list items, not just top-level ones ──
3330
- // With hierarchical virtual DOM, the currently focused <li> may be nested inside
3331
- // a child <ul>. Using ':scope > li' only cleans up direct children and leaves
3332
- // stale e-focused / e-node-focus classes on nested nodes across re-renders.
3333
3285
  var allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
3334
3286
  for (var k = 0; k < allLi.length; k++) {
3335
3287
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
3336
3288
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED);
3337
3289
  allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
3338
3290
  }
3339
- // Ensure the target <li> is in the DOM window (may trigger a re-render).
3340
- // virtualFocusedId is already set above so renderVirtualItems restores focus correctly.
3341
3291
  this.ensureVirtualNodeVisible(targetId);
3342
- // Re-query both nodes after the potential re-render so setFocus gets live DOM elements
3343
3292
  var prevNode = currentId ? this.getElement(currentId) : li;
3344
3293
  var nextNode_1 = this.getElement(targetId);
3345
3294
  if (!isNOU(nextNode_1)) {
@@ -3440,18 +3389,6 @@ var TreeView = /** @class */ (function (_super) {
3440
3389
  if (!focusedElement) {
3441
3390
  return;
3442
3391
  }
3443
- // Virtual-mode guard: when the browser moves focus into the TreeView (e.g. via
3444
- // the Tab key) it always lands on the single `<li tabindex="0">`. In that
3445
- // case the element is already the focusable one, FOCUS is already applied
3446
- // and the only thing left to do is the keyboard focus styling. Skip the
3447
- // redundant setAttribute/EventHandler.add work so we never re-render the
3448
- // virtual window as a side effect of focusing the existing `tabindex="0"`
3449
- // item.
3450
- // ── Fix: Use document.activeElement to detect browser-focused node ──
3451
- // After renderVirtualItems() restores focus, the browser-focused element is
3452
- // the authoritative source, not getFocusedNode(). If they don't match, it means
3453
- // renderVirtualItems already correctly set up the element, so we must not
3454
- // override its setup by calling setFocus again.
3455
3392
  var actuallyFocusedElement = document.activeElement;
3456
3393
  var isAlreadyFocusedInVirtualMode = this.enableVirtualization
3457
3394
  && actuallyFocusedElement === focusedElement
@@ -3706,12 +3643,6 @@ var TreeView = /** @class */ (function (_super) {
3706
3643
  else {
3707
3644
  txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
3708
3645
  }
3709
- // ── Virtual mode: sync the flat-node snapshot so re-renders show updated text ──
3710
- // virtualFlatNodes is rebuilt from this.treeData (via flattenVisibleNodes) only on
3711
- // expand/collapse/scroll. Between those events the snapshot is the single source of
3712
- // truth used by createVirtualListItem. setValue() above updates this.editData (which
3713
- // IS the treeData object reference), but virtualFlatNodes[idx].text still holds the
3714
- // old value. Update it here so the next renderVirtualItems() call shows the new text.
3715
3646
  if (this.enableVirtualization) {
3716
3647
  var id = liEle.getAttribute('data-uid');
3717
3648
  var idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
@@ -4159,11 +4090,49 @@ var TreeView = /** @class */ (function (_super) {
4159
4090
  this.fields.dataSource = this.treeData;
4160
4091
  }
4161
4092
  this.triggerEvent('nodeDropped', nodeData);
4093
+ if (this.enableVirtualization && !isNOU(this.height)) {
4094
+ this.setRootData();
4095
+ this.rebuildVirtualCheckCache();
4096
+ this.virtualFlatNodes = this.flattenVisibleNodes();
4097
+ this.rebuildVirtualNodeMap();
4098
+ var scrollTopSnapshot = this.element.scrollTop;
4099
+ var win = this.getVirtualWindowAt(scrollTopSnapshot);
4100
+ this.virtualStartIndex = win.startIndex;
4101
+ this.virtualEndIndex = win.endIndex;
4102
+ this.virtualSkipNextScrollRender = true;
4103
+ this.renderVirtualItems();
4104
+ }
4162
4105
  };
4163
4106
  TreeView.prototype.appendNode = function (dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
4164
4107
  var checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
4165
4108
  var collapse = closest(e.target, '.' + COLLAPSIBLE);
4166
4109
  var expand = closest(e.target, '.' + EXPANDABLE);
4110
+ if (this.enableVirtualization && !isNOU(this.height)) {
4111
+ var routesToSiblingBranch = !dragLi.classList.contains('e-disable') && !checkWrapper &&
4112
+ ((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) ||
4113
+ (expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) ||
4114
+ (!expand && !collapse));
4115
+ if (routesToSiblingBranch) {
4116
+ if (dropTarget.nodeName === 'LI') {
4117
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4118
+ }
4119
+ else if (dropTarget.firstElementChild && dropTarget.classList.contains(ROOT)) {
4120
+ if (dropTarget.firstElementChild.nodeName === 'UL') {
4121
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4122
+ }
4123
+ }
4124
+ else if ((dropTarget.classList.contains('e-icon-collapsible')) || (dropTarget.classList.contains('e-icon-expandable'))) {
4125
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4126
+ }
4127
+ else {
4128
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
4129
+ }
4130
+ }
4131
+ else {
4132
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
4133
+ }
4134
+ return;
4135
+ }
4167
4136
  if (!dragLi.classList.contains('e-disable') && !checkWrapper && ((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) || (expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) || (!expand && !collapse))) {
4168
4137
  if (dropTarget.nodeName === 'LI') {
4169
4138
  this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
@@ -4187,6 +4156,62 @@ var TreeView = /** @class */ (function (_super) {
4187
4156
  this.ensureIndeterminate();
4188
4157
  }
4189
4158
  };
4159
+ TreeView.prototype.virtualMoveAsSibling = function (dragLi, dropLi, e, dragObj) {
4160
+ var dropUl = closest(dropLi, '.' + PARENTITEM);
4161
+ var pre;
4162
+ if (e.target.offsetHeight > 0 && e.event.offsetY > e.target.offsetHeight - 2) {
4163
+ pre = false;
4164
+ }
4165
+ else if (e.event.offsetY < 2) {
4166
+ pre = true;
4167
+ }
4168
+ else if (e.target.classList.contains('e-icon-expandable') || (e.target.classList.contains('e-icon-collapsible'))) {
4169
+ if ((e.event.offsetY < 5) || (e.event.offsetX < 3)) {
4170
+ pre = true;
4171
+ }
4172
+ else if ((e.event.offsetY > 15) || (e.event.offsetX > 17)) {
4173
+ pre = false;
4174
+ }
4175
+ }
4176
+ this.moveData(dragLi, dropLi, dropUl, pre, dragObj);
4177
+ };
4178
+ TreeView.prototype.virtualMoveAsChild = function (dragLi, dropLi, dragObj, offsetY, dropTarget) {
4179
+ var pos = offsetY;
4180
+ var isSiblingMarker = dropTarget.classList.contains('e-sibling');
4181
+ var refLi = (pos < 7 && !isSiblingMarker) || (isSiblingMarker &&
4182
+ dropLi.lastChild && !dropLi.lastChild.classList.contains('e-sibling'))
4183
+ ? dropLi : null;
4184
+ var dropTargetHt = dropTarget;
4185
+ var isAfter = (dropTargetHt.offsetHeight > 0 && pos > dropTargetHt.offsetHeight - 10);
4186
+ if (refLi) {
4187
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
4188
+ this.moveData(dragLi, dropLi, dropParentUl, true, dragObj);
4189
+ }
4190
+ else if (isAfter) {
4191
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
4192
+ this.moveData(dragLi, dropLi, dropParentUl, false, dragObj);
4193
+ }
4194
+ else {
4195
+ var dropIcon = select('div.' + EXPANDABLE + ', div.' + COLLAPSIBLE, dropLi);
4196
+ var dropIsCollapsedParent = dropIcon && dropIcon.classList.contains(EXPANDABLE);
4197
+ var dropIsLeaf = isNOU(dropIcon);
4198
+ if (this.preventExpand !== true && (dropIsCollapsedParent || dropIsLeaf)) {
4199
+ var dropLiId = dropLi.getAttribute('data-uid');
4200
+ if (dropLiId && this.expandedNodes.indexOf(dropLiId) === -1) {
4201
+ this.expandedNodes.push(dropLiId);
4202
+ }
4203
+ if (this.virtualExpandedNodesSet && dropLiId) {
4204
+ this.virtualExpandedNodesSet.add(dropLiId);
4205
+ }
4206
+ var dropRecord = this.findNodeInTreeData(dropLiId);
4207
+ if (dropRecord && this.fields && this.fields.expanded) {
4208
+ setValue(this.fields.expanded, true, dropRecord);
4209
+ }
4210
+ }
4211
+ var dropUl = select('.' + PARENTITEM, dropLi) || dropLi;
4212
+ this.moveData(dragLi, null, dropUl, true, dragObj);
4213
+ }
4214
+ };
4190
4215
  TreeView.prototype.dropAsSiblingNode = function (dragLi, dropLi, e, dragObj) {
4191
4216
  var dropUl = closest(dropLi, '.' + PARENTITEM);
4192
4217
  var dragParentUl = closest(dragLi, '.' + PARENTITEM);
@@ -4274,7 +4299,7 @@ var TreeView = /** @class */ (function (_super) {
4274
4299
  delete removedData[0][this.fields.parentID];
4275
4300
  }
4276
4301
  else {
4277
- var currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
4302
+ var currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
4278
4303
  setValue(this.fields.parentID, currPid, removedData[0]);
4279
4304
  }
4280
4305
  this.treeData.splice(pos, 0, removedData[0]);
@@ -4416,7 +4441,7 @@ var TreeView = /** @class */ (function (_super) {
4416
4441
  var draggedNodeData = obj ? obj.dragData : null;
4417
4442
  var newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
4418
4443
  var dragLiParent = obj.dragLi.parentElement;
4419
- var dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
4444
+ var dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
4420
4445
  var targetParent = null;
4421
4446
  var indexValue = null;
4422
4447
  var iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
@@ -4425,7 +4450,7 @@ var TreeView = /** @class */ (function (_super) {
4425
4450
  var index = node ? closest(node, '.e-list-parent') : null;
4426
4451
  var i = 0;
4427
4452
  var position = null;
4428
- dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
4453
+ dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
4429
4454
  dragParent = (drop === true) ? this.dragParent : dragParent;
4430
4455
  if (cloneEle) {
4431
4456
  while (i < 4) {
@@ -5099,26 +5124,10 @@ var TreeView = /** @class */ (function (_super) {
5099
5124
  TreeView.prototype.keyDownHandler = function (e) {
5100
5125
  if (e.key === 'Tab' || e.keyCode === 9) {
5101
5126
  this.isKeyboardFocus = true;
5102
- // Mark the upcoming scroll event (if any) as Tab-induced so the
5103
- // virtual scroll handler skips its re-render path. The browser
5104
- // calls `Element.scrollIntoView` semantics on focus, which scrolls
5105
- // a `<li tabindex="0">` in the middle of the tree into view and
5106
- // therefore fires `scroll` on the container. Without this guard
5107
- // the scroll handler would tear down the focused `<li>` and move
5108
- // focus elsewhere.
5109
5127
  if (this.enableVirtualization) {
5110
5128
  this.virtualSkipNextScrollRender = true;
5111
5129
  }
5112
5130
  }
5113
- // ── Fix: Also skip scroll render for arrow key navigation ──────────────────
5114
- // Arrow Down/Up calls focusNextNode() which calls ensureVirtualNodeVisible()
5115
- // to scroll the target node into view. Without this guard, the scroll event
5116
- // would trigger onVirtualScroll() which re-renders the window and resets the
5117
- // focus that focusNextNode() just set. This manifests as tabindex resetting
5118
- // to the first item instead of staying on the arrow-navigated target.
5119
- // Arrow Right/Left (expand/collapse) also calls renderVirtualItems() which
5120
- // calls li.focus() in branch 2; that can trigger a scroll on an edge node,
5121
- // so we consume it here to prevent a stale RAF re-render.
5122
5131
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
5123
5132
  || e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
5124
5133
  if (this.enableVirtualization) {
@@ -5480,10 +5489,6 @@ var TreeView = /** @class */ (function (_super) {
5480
5489
  if (this.dataType === 1) {
5481
5490
  this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
5482
5491
  }
5483
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
5484
- // after deletion so the removed nodes are correctly reflected in the hierarchical
5485
- // virtual DOM. When height is not provided, the tree uses normal rendering and
5486
- // the DOM is already updated by removeNode() above.
5487
5492
  if (this.enableVirtualization && !isNOU(this.height)) {
5488
5493
  this.virtualFlatNodes = this.flattenVisibleNodes();
5489
5494
  this.rebuildVirtualNodeMap();
@@ -5552,16 +5557,6 @@ var TreeView = /** @class */ (function (_super) {
5552
5557
  }
5553
5558
  return parentsID;
5554
5559
  };
5555
- // ═══════════════════════════════════════════════════════════════════════════════
5556
- // VIRTUALIZATION — private helpers
5557
- // ═══════════════════════════════════════════════════════════════════════════════
5558
- /**
5559
- * Parses the `height` property (string like '400px' or number) to a pixel value.
5560
- * Returns 0 if the value cannot be determined.
5561
- *
5562
- * @returns {number} Pixel height.
5563
- * @private
5564
- */
5565
5560
  TreeView.prototype.parseHeightToPixels = function () {
5566
5561
  var h = this.height;
5567
5562
  if (isNOU(h)) {
@@ -5573,13 +5568,6 @@ var TreeView = /** @class */ (function (_super) {
5573
5568
  var parsed = parseFloat(h);
5574
5569
  return isNaN(parsed) ? 0 : parsed;
5575
5570
  };
5576
- /**
5577
- * Measures the height of the first rendered list item so subsequent calculations
5578
- * use real layout values. Falls back to the stored `virtualItemHeight` default (40).
5579
- *
5580
- * @returns {number} Row height in pixels.
5581
- * @private
5582
- */
5583
5571
  TreeView.prototype.measureVirtualItemHeight = function () {
5584
5572
  var firstLi = select('.' + LISTITEM, this.element);
5585
5573
  if (firstLi && firstLi.offsetHeight > 0) {
@@ -5587,13 +5575,6 @@ var TreeView = /** @class */ (function (_super) {
5587
5575
  }
5588
5576
  return this.virtualItemHeight;
5589
5577
  };
5590
- /**
5591
- * Rebuilds the cached Sets for checked/selected nodes to enable O(1) lookup
5592
- * during the flatten operation instead of O(n) array indexOf calls.
5593
- *
5594
- * @returns {void}
5595
- * @private
5596
- */
5597
5578
  TreeView.prototype.rebuildVirtualCheckCache = function () {
5598
5579
  this.virtualExpandedNodesSet.clear();
5599
5580
  this.virtualCheckedNodesSet.clear();
@@ -5614,20 +5595,10 @@ var TreeView = /** @class */ (function (_super) {
5614
5595
  }
5615
5596
  }
5616
5597
  };
5617
- /**
5618
- * Recursively walks the treeData (or groupedData for flat lists) and builds
5619
- * `virtualFlatNodes` — an ordered list of every node that is currently visible
5620
- * (i.e. its ancestors are all expanded). Uses cached Sets for O(1) state lookups.
5621
- *
5622
- * @returns {VirtualNodeData[]} Ordered flat list of visible nodes.
5623
- * @private
5624
- */
5625
5598
  TreeView.prototype.flattenVisibleNodes = function () {
5626
- // Build cached Sets for fast lookups
5627
5599
  this.rebuildVirtualCheckCache();
5628
5600
  var result = [];
5629
5601
  if (this.dataType === 1) {
5630
- // Flat data with parentID field — use getChildGroup to find root nodes
5631
5602
  var rootNodes = [];
5632
5603
  if (this.groupedData) {
5633
5604
  rootNodes = this.getChildGroup(this.groupedData, null, true);
@@ -5635,39 +5606,29 @@ var TreeView = /** @class */ (function (_super) {
5635
5606
  if (!rootNodes || rootNodes.length === 0) {
5636
5607
  rootNodes = this.rootData;
5637
5608
  }
5638
- // Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
5639
5609
  rootNodes = this.getSortedData(rootNodes);
5640
5610
  this.flattenNodeRecursive(rootNodes, 1, null, result);
5641
5611
  }
5642
5612
  else {
5643
- // Hierarchical data
5644
5613
  var sorted = this.getSortedData(this.rootData);
5645
5614
  this.flattenNodeRecursive(sorted, 1, null, result);
5646
5615
  }
5647
5616
  return result;
5648
5617
  };
5649
- /**
5650
- * Recursive helper for `flattenVisibleNodes`.
5651
- *
5652
- * @param {Object[]} nodes - Array of raw data objects at this level.
5653
- * @param {number} level - 1-based depth level.
5654
- * @param {string | null} parentId - The parent node id, or null for root.
5655
- * @param {VirtualNodeData[]} result - Accumulator array.
5656
- * @returns {void}
5657
- * @private
5658
- */
5659
5618
  TreeView.prototype.flattenNodeRecursive = function (nodes, level, parentId, result) {
5660
5619
  if (!nodes || nodes.length === 0) {
5661
5620
  return;
5662
5621
  }
5663
- // Cache field names and settings outside the loop to avoid repeated conversions
5664
- var childFieldName = this.fields.child.toString();
5665
- var expandedFieldName = this.fields.expanded;
5666
- var isCheckedFieldName = this.fields.isChecked;
5667
- var iconCssFieldName = this.fields.iconCss;
5668
- var imageUrlFieldName = this.fields.imageUrl;
5669
- var idFieldName = this.fields.id;
5670
- var textFieldName = this.fields.text;
5622
+ var levelMapper = level === 1 ? this.fields : this.getChildFields(this.fields, level, 1);
5623
+ var childFieldName = levelMapper.child && levelMapper.child.toString();
5624
+ var expandedFieldName = levelMapper.expanded;
5625
+ var isCheckedFieldName = levelMapper.isChecked;
5626
+ var iconCssFieldName = levelMapper.iconCss;
5627
+ var imageUrlFieldName = levelMapper.imageUrl;
5628
+ var idFieldName = levelMapper.id;
5629
+ var textFieldName = levelMapper.text;
5630
+ var selectedFieldName = levelMapper.selected;
5631
+ var hasChildrenFieldName = levelMapper.hasChildren;
5671
5632
  var needsSorting = this.sortOrder !== 'None';
5672
5633
  var isDataTypeFlat = this.dataType === 1;
5673
5634
  var enableVirtualization = this.enableVirtualization;
@@ -5675,70 +5636,93 @@ var TreeView = /** @class */ (function (_super) {
5675
5636
  var raw = nodes[i];
5676
5637
  var id = isNOU(raw[idFieldName]) ? '' : raw[idFieldName].toString();
5677
5638
  var text = isNOU(raw[textFieldName]) ? '' : raw[textFieldName].toString();
5678
- // Use expandedNodes as the single source of truth.
5679
5639
  var inExpandedNodes = this.virtualExpandedNodesSet.has(id);
5680
- var rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
5681
- var isExpanded = inExpandedNodes || rawExpanded;
5640
+ var rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
5641
+ var expanded = inExpandedNodes || rawExpanded;
5682
5642
  if (rawExpanded && id) {
5683
5643
  this.expandedNodes.push(id);
5684
5644
  this.virtualExpandedNodesSet.add(id);
5685
5645
  }
5686
- // Inline hasChildRecords check to avoid function call (optimization)
5687
5646
  var hasChildren = false;
5688
- if (isDataTypeFlat) {
5647
+ var explicitHasChildren = hasChildrenFieldName ? getValue(hasChildrenFieldName, raw) : null;
5648
+ if (levelMapper.dataSource instanceof DataManager && !isNOU(explicitHasChildren) && explicitHasChildren) {
5649
+ hasChildren = true;
5650
+ }
5651
+ else if (isDataTypeFlat) {
5689
5652
  if (this.groupedData) {
5690
5653
  var groupedChild = this.getChildGroup(this.groupedData, id, false);
5691
5654
  hasChildren = !isNOU(groupedChild) && groupedChild.length > 0;
5692
5655
  }
5693
5656
  }
5694
5657
  else {
5695
- var childrenObj = getValue(childFieldName, raw);
5658
+ var childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
5696
5659
  hasChildren = !isNOU(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
5697
5660
  }
5698
- // Virtual-mode only: honour fields.isChecked === true on initial render
5699
- if (enableVirtualization && id && isCheckedFieldName && !this.virtualCheckedNodesSet.has(id)) {
5700
- var rawChecked = getValue(isCheckedFieldName, raw);
5701
- if (!isNOU(rawChecked) && rawChecked.toString() === 'true') {
5661
+ var parentChecked = null;
5662
+ if (levelMapper.dataSource instanceof DataManager && level > 1 && this.autoCheck && parentId) {
5663
+ if (this.virtualCheckedNodesSet.has(parentId) ||
5664
+ (this.checkedNodes && this.checkedNodes.indexOf(parentId) !== -1)) {
5665
+ parentChecked = true;
5666
+ }
5667
+ else {
5668
+ parentChecked = false;
5669
+ }
5670
+ }
5671
+ var rawChecked = isCheckedFieldName && getValue(isCheckedFieldName, raw);
5672
+ if (enableVirtualization && id && !this.virtualCheckedNodesSet.has(id)) {
5673
+ if ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'true') ||
5674
+ (levelMapper.dataSource instanceof DataManager && parentChecked === true)) {
5702
5675
  this.virtualCheckedNodesSet.add(id);
5703
5676
  if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
5704
5677
  this.checkedNodes.push(id);
5705
5678
  }
5706
5679
  }
5707
5680
  }
5708
- // Use cached Sets for O(1) lookup
5709
- var isSelected = this.virtualSelectedNodesSet.has(id);
5710
- // Resolve checkbox state: indeterminate takes precedence over the checked set,
5711
- // because a node can be in the indeterminate state without being in checkedNodes.
5681
+ else if (enableVirtualization && id && this.virtualCheckedNodesSet.has(id) && levelMapper.dataSource instanceof DataManager) {
5682
+ if (level > 1 && !this.virtualIndeterminateNodesSet.has(parentId) && this.autoCheck &&
5683
+ ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'false') || parentChecked === false)) {
5684
+ this.virtualCheckedNodesSet.delete(id);
5685
+ if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
5686
+ this.checkedNodes.splice(this.checkedNodes.indexOf(id), 1);
5687
+ }
5688
+ }
5689
+ }
5690
+ if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
5691
+ var rawSelected = getValue(selectedFieldName, raw);
5692
+ if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
5693
+ this.virtualSelectedNodesSet.add(id);
5694
+ if (this.selectedNodes && this.selectedNodes.indexOf(id) === -1) {
5695
+ this.selectedNodes.push(id);
5696
+ }
5697
+ }
5698
+ }
5699
+ var selected = this.virtualSelectedNodesSet.has(id);
5712
5700
  var isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
5713
5701
  ? 'indeterminate'
5714
5702
  : this.virtualCheckedNodesSet.has(id);
5715
- var isDisabled = false; // TODO: Check if any per-node disabled field exists
5716
5703
  var iconCssVal = raw[iconCssFieldName];
5717
5704
  var iconCss = isNOU(iconCssVal) ? '' : iconCssVal.toString();
5718
5705
  var imageUrlVal = raw[imageUrlFieldName];
5719
5706
  var imageUrl = isNOU(imageUrlVal) ? '' : imageUrlVal.toString();
5720
5707
  var node = {
5721
- id: id, text: text, level: level, parentId: parentId, hasChildren: hasChildren, isExpanded: isExpanded,
5722
- isSelected: isSelected, isChecked: isChecked, isDisabled: isDisabled, iconCss: iconCss, imageUrl: imageUrl, rawData: raw
5708
+ id: id, text: text, level: level, parentId: parentId, hasChildren: hasChildren, expanded: expanded,
5709
+ selected: selected, isChecked: isChecked, iconCss: iconCss, imageUrl: imageUrl
5723
5710
  };
5724
5711
  result.push(node);
5725
- if (isExpanded && hasChildren) {
5726
- // Determine children list
5712
+ if (expanded && hasChildren) {
5727
5713
  var children = [];
5728
5714
  if (isDataTypeFlat) {
5729
5715
  if (this.groupedData) {
5730
5716
  children = this.getChildGroup(this.groupedData, id, false);
5731
- // Apply sortOrder to flat-data children (same as hierarchical path)
5732
5717
  if (needsSorting) {
5733
5718
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
5734
5719
  }
5735
5720
  }
5736
5721
  }
5737
5722
  else {
5738
- var rawChildren = getValue(childFieldName, raw);
5723
+ var rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
5739
5724
  if (!isNOU(rawChildren) && Array.isArray(rawChildren)) {
5740
5725
  children = rawChildren;
5741
- // Only sort if needed
5742
5726
  if (needsSorting) {
5743
5727
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
5744
5728
  }
@@ -5748,35 +5732,6 @@ var TreeView = /** @class */ (function (_super) {
5748
5732
  }
5749
5733
  }
5750
5734
  };
5751
- /**
5752
- * Checks whether a raw datasource node has child records.
5753
- *
5754
- * @param {Object} raw - Raw datasource object.
5755
- * @returns {boolean} True if children exist.
5756
- * @private
5757
- */
5758
- TreeView.prototype.hasChildRecords = function (raw) {
5759
- if (this.dataType === 1) {
5760
- if (!this.groupedData) {
5761
- return false;
5762
- }
5763
- var rawId = raw[this.fields.id];
5764
- if (isNOU(rawId)) {
5765
- return false;
5766
- }
5767
- var groupedChild = this.getChildGroup(this.groupedData, rawId.toString(), false);
5768
- return !isNOU(groupedChild) && groupedChild.length > 0;
5769
- }
5770
- var childField = this.fields.child.toString();
5771
- var children = getValue(childField, raw);
5772
- return !isNOU(children) && Array.isArray(children) && children.length > 0;
5773
- };
5774
- /**
5775
- * Rebuilds `virtualNodeMap` (id → index) from the current `virtualFlatNodes`.
5776
- *
5777
- * @returns {void}
5778
- * @private
5779
- */
5780
5735
  TreeView.prototype.rebuildVirtualNodeMap = function () {
5781
5736
  this.virtualNodeMap.clear();
5782
5737
  for (var i = 0; i < this.virtualFlatNodes.length; i++) {
@@ -5809,49 +5764,12 @@ var TreeView = /** @class */ (function (_super) {
5809
5764
  var paddingBottom = Math.max(0, (total - end) * itemH);
5810
5765
  return { paddingTop: paddingTop, paddingBottom: paddingBottom };
5811
5766
  };
5812
- /**
5813
- * Renders the virtual window into a **nested** `<ul>/<li>` hierarchy identical to
5814
- * the non-virtualized DOM structure.
5815
- *
5816
- * ## Scroll math
5817
- * The flat-index ↔ pixel mapping stays unchanged: every entry in
5818
- * `virtualFlatNodes` represents exactly one `itemHeight` row.
5819
- * `paddingTop = startIndex * itemHeight` and
5820
- * `paddingBottom = (total - endIndex) * itemHeight` keep the scrollbar
5821
- * thumb in the correct position.
5822
- *
5823
- * ## Ancestor injection
5824
- * When `startIndex` points at a child node whose ancestor(s) have already
5825
- * scrolled above the viewport (their flat indices are < startIndex), the
5826
- * ancestors would normally be missing from the DOM, forcing the children to
5827
- * fall back to the root `<ul>` as flat siblings — producing wrong HTML.
5828
- *
5829
- * To fix this, before rendering the window we walk the ancestor chain of the
5830
- * first visible node and collect every ancestor that lies **outside** the
5831
- * window. Those ancestors are injected as real `<li>` containers at the
5832
- * very top of the root `<ul>`. To compensate for their presence, `paddingTop`
5833
- * is reduced by `injectedCount * itemHeight` so the total scroll height
5834
- * remains the same.
5835
- *
5836
- * Once the ancestors are in the DOM they are registered in `renderedParentMap`
5837
- * so the normal window-rendering loop can nest children inside them exactly
5838
- * as it does when both parent and child are in the same window.
5839
- *
5840
- * @returns {void}
5841
- * @private
5842
- */
5843
5767
  TreeView.prototype.renderVirtualItems = function () {
5844
5768
  if (!this.ulElement) {
5845
5769
  return;
5846
5770
  }
5847
5771
  var ul = this.ulElement;
5848
- // ── Snapshot focused ID BEFORE clearing the DOM ───────────────────────────
5849
- // Removing a focused <li> from the DOM triggers a synchronous `blur` event,
5850
- // which calls focusOut() and sets virtualFocusedId = null. Capturing the id
5851
- // here (before any removeChild) ensures focus is correctly restored to the
5852
- // same node after re-render even when keyboard navigation was in progress.
5853
5772
  var focusedUid = this.virtualFocusedId || null;
5854
- // ── Clear previous render (direct children only) ─────────────────────────
5855
5773
  var existingItems = ul.querySelectorAll(':scope > li');
5856
5774
  for (var i = existingItems.length - 1; i >= 0; i--) {
5857
5775
  ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
@@ -5862,26 +5780,22 @@ var TreeView = /** @class */ (function (_super) {
5862
5780
  if (this.virtualStartIndex < this.virtualFlatNodes.length) {
5863
5781
  var firstNode = this.virtualFlatNodes[this.virtualStartIndex];
5864
5782
  if (firstNode.parentId) {
5865
- // Build the ancestor chain from root down to the immediate parent
5866
5783
  var ancestors = [];
5867
5784
  var searchId = firstNode.parentId;
5868
5785
  while (searchId) {
5869
5786
  var ancestorIdx = this.virtualNodeMap.get(searchId);
5870
5787
  if (!isNOU(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
5871
5788
  var ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
5872
- ancestors.unshift(ancestor); // prepend so root comes first
5789
+ ancestors.unshift(ancestor);
5873
5790
  searchId = ancestor.parentId;
5874
5791
  }
5875
5792
  else {
5876
5793
  break;
5877
5794
  }
5878
5795
  }
5879
- // Inject each ancestor as a real <li> into the root <ul>
5880
- // and register it in renderedParentMap for child-nesting below.
5881
5796
  for (var a = 0; a < ancestors.length; a++) {
5882
5797
  var ancestor = ancestors[parseInt(a.toString(), 10)];
5883
5798
  var ancestorLi = this.createVirtualListItem(ancestor, false);
5884
- // Find the correct container: root or a previously injected ancestor's <ul>
5885
5799
  var targetUl = ul;
5886
5800
  if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
5887
5801
  var pe = renderedParentMap.get(ancestor.parentId);
@@ -5900,26 +5814,20 @@ var TreeView = /** @class */ (function (_super) {
5900
5814
  }
5901
5815
  }
5902
5816
  }
5903
- // ── Scroll-space padding (adjusted for injected ancestors) ────────────────
5904
5817
  var total = this.virtualFlatNodes.length;
5905
5818
  var start = this.virtualStartIndex;
5906
5819
  var end = this.virtualEndIndex;
5907
5820
  var rawPaddingTop = start * itemH;
5908
- // Subtract the height of injected ancestors so the scroll position is
5909
- // exact: those ancestor rows are now real DOM elements, not hidden space.
5910
5821
  var adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
5911
5822
  ul.style.paddingTop = adjustedPaddingTop + 'px';
5912
5823
  ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
5913
- // ── Render window items (nested via renderedParentMap) ────────────────────
5914
5824
  var liListBuf = [];
5915
5825
  for (var i = start; i < end; i++) {
5916
5826
  var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
5917
5827
  var li = this.createVirtualListItem(node, i === start);
5918
- // Find the correct parent container
5919
5828
  var targetUl = ul;
5920
5829
  if (node.parentId && renderedParentMap.has(node.parentId)) {
5921
5830
  var parentEntry = renderedParentMap.get(node.parentId);
5922
- // Lazily create the child <ul> the first time a child needs it
5923
5831
  if (!parentEntry.childUl) {
5924
5832
  parentEntry.childUl = this.createElement('ul', {
5925
5833
  className: PARENTITEM + ' e-ul',
@@ -5931,30 +5839,11 @@ var TreeView = /** @class */ (function (_super) {
5931
5839
  }
5932
5840
  targetUl.appendChild(li);
5933
5841
  liListBuf.push(li);
5934
- // Register this node so its children in the same window can nest under it
5935
- if (node.isExpanded && node.hasChildren) {
5842
+ if (node.expanded && node.hasChildren) {
5936
5843
  renderedParentMap.set(node.id, { li: li, childUl: null });
5937
5844
  }
5938
5845
  }
5939
- // ── Restore focus / tabindex ──────────────────────────────────────────────
5940
- // In virtual mode only the li that carries the 'e-active' class should be
5941
- // focusable (tabindex='0'). All other rendered li elements must be set
5942
- // to tabindex='-1' so the tab order remains unambiguous as the window
5943
- // changes during scrolling.
5944
5846
  var focusRestored = false;
5945
- // 1. Prefer the currently active node if it is in the rendered window.
5946
- for (var k = 0; k < liListBuf.length; k++) {
5947
- var li = liListBuf[parseInt(k.toString(), 10)];
5948
- if (li.classList.contains(ACTIVE)) {
5949
- li.setAttribute('tabindex', '0');
5950
- // ── Fix: ensure FOCUS class set on active node too ──
5951
- // This ensures focusIn() guard condition is met: tabindex='0' && FOCUS class
5952
- addClass([li], FOCUS);
5953
- focusRestored = true;
5954
- break;
5955
- }
5956
- }
5957
- // 2. Otherwise fall back to the previously focused node (if it is in window).
5958
5847
  if (!focusRestored && focusedUid) {
5959
5848
  for (var k = 0; k < liListBuf.length; k++) {
5960
5849
  var li = liListBuf[parseInt(k.toString(), 10)];
@@ -5962,22 +5851,6 @@ var TreeView = /** @class */ (function (_super) {
5962
5851
  li.setAttribute('tabindex', '0');
5963
5852
  addClass([li], FOCUS);
5964
5853
  addClass([li], FOCUSED);
5965
- // ── Fix: restore actual browser focus so keyboard events keep working ──
5966
- // After renderVirtualItems() replaces all <li> elements the old element
5967
- // is gone from the DOM and document.activeElement falls back to
5968
- // document.body. Setting tabindex + classes is not enough — the new
5969
- // element also needs a programmatic .focus() call so subsequent keyboard
5970
- // events (Arrow keys, Home, End, etc.) are delivered to the TreeView.
5971
- // Re-attach the blur→focusOut handler so virtualFocusedId is cleared
5972
- // correctly when the user tabs away.
5973
- // ── Use preventScroll:true ────────────────────────────────────────────
5974
- // Without preventScroll the browser scrolls the container to bring this
5975
- // node into view during the re-render, changing scrollTop AFTER the
5976
- // correct paddingTop has been applied. This is the root cause of the
5977
- // KBN expand position-jump bug: the render is correct but the browser
5978
- // silently scrolls the viewport, making the parent node appear to jump.
5979
- // ensureVirtualNodeVisible() handles any legitimate scroll-into-view
5980
- // needs before renderVirtualItems() is called, so scroll here is wrong.
5981
5854
  EventHandler.add(li, 'blur', this.focusOut, this);
5982
5855
  li.focus({ preventScroll: true });
5983
5856
  focusRestored = true;
@@ -5985,53 +5858,36 @@ var TreeView = /** @class */ (function (_super) {
5985
5858
  }
5986
5859
  }
5987
5860
  }
5988
- // 3. Finally default to the first item in the window.
5989
5861
  if (!focusRestored && liListBuf.length > 0) {
5990
5862
  var firstLi = liListBuf[0];
5991
5863
  firstLi.setAttribute('tabindex', '0');
5992
5864
  }
5993
- // ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
5994
- // This prevents stale tabindex="0" on multiple nodes which breaks Tab order
5995
5865
  for (var k = 0; k < liListBuf.length; k++) {
5996
5866
  var li = liListBuf[parseInt(k.toString(), 10)];
5997
5867
  if (li.getAttribute('tabindex') !== '0') {
5998
5868
  li.setAttribute('tabindex', '-1');
5999
5869
  }
6000
5870
  }
6001
- // Keep liList in sync so keyboard-navigation helpers stay functional.
6002
5871
  this.liList = liListBuf;
6003
5872
  };
6004
- /**
6005
- * Creates a single `<li>` element for the virtual window representing `node`.
6006
- *
6007
- * @param {VirtualNodeData} node - The node data.
6008
- * @param {boolean} _isFirst - True if this is the first rendered item (unused here but kept for potential override).
6009
- * @returns {HTMLElement} The constructed `<li>` element.
6010
- * @private
6011
- */
6012
5873
  TreeView.prototype.createVirtualListItem = function (node, _isFirst) {
6013
5874
  var li = this.createElement('li', { className: LISTITEM });
6014
- // Level class for CSS indentation
6015
5875
  li.classList.add('e-level-' + node.level);
6016
5876
  li.setAttribute('data-uid', node.id);
6017
5877
  li.setAttribute('aria-level', node.level.toString());
6018
5878
  li.setAttribute('role', 'treeitem');
6019
5879
  li.setAttribute('tabindex', '-1');
6020
- // Full-row overlay (required for fullRowSelect mode)
6021
5880
  if (this.fullRowSelect) {
6022
5881
  var fullRow = this.createElement('div', { className: FULLROW });
6023
5882
  li.appendChild(fullRow);
6024
5883
  }
6025
- // Text/icon wrapper
6026
5884
  var textWrap = this.createElement('div', { className: TEXTWRAP });
6027
- // Expand/collapse icon
6028
5885
  if (node.hasChildren) {
6029
5886
  var iconSpan = this.createElement('div', {
6030
- className: ICON + ' ' + (node.isExpanded ? COLLAPSIBLE : EXPANDABLE)
5887
+ className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
6031
5888
  });
6032
5889
  textWrap.appendChild(iconSpan);
6033
5890
  }
6034
- // Checkbox
6035
5891
  if (this.showCheckBox) {
6036
5892
  var checkWrap = createCheckBox(this.createElement, false, {
6037
5893
  checked: node.isChecked === true,
@@ -6045,29 +5901,32 @@ var TreeView = /** @class */ (function (_super) {
6045
5901
  }
6046
5902
  textWrap.appendChild(checkWrap);
6047
5903
  }
6048
- // Icon
6049
5904
  if (node.iconCss) {
6050
5905
  var iconEl = this.createElement('span', {
6051
5906
  className: ICON + ' ' + node.iconCss
6052
5907
  });
6053
5908
  textWrap.appendChild(iconEl);
6054
5909
  }
6055
- // Image
6056
5910
  if (node.imageUrl) {
6057
5911
  var img = document.createElement('img');
6058
5912
  img.src = node.imageUrl;
6059
5913
  img.classList.add(IMAGE);
6060
5914
  textWrap.appendChild(img);
6061
5915
  }
6062
- // Text anchor
6063
5916
  var anchor = this.createElement('span', {
6064
5917
  className: 'e-list-text',
6065
5918
  innerHTML: node.text
6066
5919
  });
6067
5920
  textWrap.appendChild(anchor);
6068
5921
  li.appendChild(textWrap);
6069
- // State classes
6070
- if (node.isExpanded) {
5922
+ if (!isNOU(this.nodeTemplateFn)) {
5923
+ var rawData = this.findNodeInTreeData(node.id);
5924
+ if (!isNOU(rawData)) {
5925
+ anchor.innerHTML = '';
5926
+ this.renderNodeTemplate(rawData, anchor, node.id);
5927
+ }
5928
+ }
5929
+ if (node.expanded) {
6071
5930
  li.classList.add(EXPANDED);
6072
5931
  li.setAttribute('aria-expanded', 'true');
6073
5932
  }
@@ -6075,91 +5934,50 @@ var TreeView = /** @class */ (function (_super) {
6075
5934
  li.classList.add(NODECOLLAPSED);
6076
5935
  li.setAttribute('aria-expanded', 'false');
6077
5936
  }
6078
- if (node.isSelected) {
5937
+ if (node.selected) {
6079
5938
  li.classList.add(ACTIVE);
6080
5939
  li.setAttribute('aria-selected', 'true');
6081
5940
  }
6082
- if (node.isDisabled) {
6083
- li.classList.add(DISABLED);
6084
- li.setAttribute('aria-disabled', 'true');
6085
- }
6086
5941
  if (node.hasChildren) {
6087
5942
  li.classList.add(CHILD);
6088
5943
  }
6089
- // Wire the focus event so `focusIn()` fires when the user Tabs into this
6090
- // item. The non-virtualized path registers this in `beforeNodeCreate()`,
6091
- // but virtual items never go through that callback, so we must register
6092
- // it here to keep focus styling (e.g. `e-focused`) consistent between
6093
- // virtual and non-virtual modes. `unWireEvents()` already removes this
6094
- // handler during teardown by iterating over the rendered `<li>`s.
6095
5944
  EventHandler.add(li, 'focus', this.focusIn, this);
6096
5945
  return li;
6097
5946
  };
6098
- /**
6099
- * Initialises the virtual-scrolling layout:
6100
- * 1. Adds the `e-virtualization` CSS class and applies a fixed `height` + `overflow-y`.
6101
- * 2. Flattens all visible nodes into `virtualFlatNodes`.
6102
- * 3. Measures item height and viewport height.
6103
- * 4. Computes the initial window and renders it.
6104
- * 5. Wires the scroll event handler.
6105
- *
6106
- * @returns {void}
6107
- * @private
6108
- */
6109
5947
  TreeView.prototype.initVirtualization = function () {
6110
5948
  addClass([this.element], VIRTUALIZATION);
6111
- // Apply container height
6112
5949
  var px = this.parseHeightToPixels();
6113
5950
  if (px > 0) {
6114
5951
  this.element.style.height = px + 'px';
6115
5952
  this.element.style.overflowY = 'auto';
6116
5953
  }
6117
- // Ensure the inner UL is ready
6118
5954
  if (!this.ulElement) {
6119
5955
  this.ulElement = select('.' + PARENTITEM, this.element);
6120
5956
  }
6121
5957
  if (!this.ulElement) {
6122
5958
  return;
6123
5959
  }
6124
- // Make the UL position-relative for the absolute spacers
6125
5960
  this.ulElement.style.position = 'relative';
6126
- // Rebuild flat list
6127
5961
  this.virtualFlatNodes = this.flattenVisibleNodes();
6128
5962
  this.rebuildVirtualNodeMap();
6129
- // Measure dimensions
6130
5963
  this.virtualViewportHeight = px > 0 ? px : this.element.offsetHeight;
6131
- // Render a single probe item to measure the real item height before the full render
6132
5964
  if (this.virtualFlatNodes.length > 0) {
6133
5965
  var probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
6134
5966
  this.ulElement.appendChild(probe);
6135
5967
  this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
6136
5968
  this.ulElement.removeChild(probe);
6137
5969
  }
6138
- // Compute window and render all items in the initial viewport
6139
5970
  var win = this.getVirtualWindow();
6140
5971
  this.virtualStartIndex = win.startIndex;
6141
5972
  this.virtualEndIndex = win.endIndex;
6142
5973
  this.renderVirtualItems();
6143
- // Wire scroll
6144
5974
  this.wireVirtualScrollEvents();
6145
5975
  };
6146
- /**
6147
- * Wires the scroll event listener on the TreeView element.
6148
- *
6149
- * @returns {void}
6150
- * @private
6151
- */
6152
5976
  TreeView.prototype.wireVirtualScrollEvents = function () {
6153
5977
  this.unwireVirtualScrollEvents();
6154
5978
  this.virtualScrollBound = this.onVirtualScroll.bind(this);
6155
5979
  this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
6156
5980
  };
6157
- /**
6158
- * Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
6159
- *
6160
- * @returns {void}
6161
- * @private
6162
- */
6163
5981
  TreeView.prototype.unwireVirtualScrollEvents = function () {
6164
5982
  if (this.virtualScrollBound) {
6165
5983
  this.element.removeEventListener('scroll', this.virtualScrollBound);
@@ -6170,29 +5988,13 @@ var TreeView = /** @class */ (function (_super) {
6170
5988
  this.virtualScrollRAF = null;
6171
5989
  }
6172
5990
  };
6173
- /**
6174
- * Scroll handler for the virtual TreeView. Throttled via `requestAnimationFrame`.
6175
- * Shows the loading mask during fast scrolls, re-renders the window, then hides the mask.
6176
- *
6177
- * @returns {void}
6178
- * @private
6179
- */
6180
5991
  TreeView.prototype.onVirtualScroll = function () {
6181
5992
  var _this = this;
6182
5993
  if (this.virtualScrollRAF !== null) {
6183
5994
  cancelAnimationFrame(this.virtualScrollRAF);
6184
5995
  }
6185
- // ── Tab-induced focus guard ────────────────────────────────────────────────
6186
- // When the user Tabs into a `<li tabindex="0">` that is in the middle of
6187
- // the treeview, the browser auto-scrolls the container so the focused
6188
- // element becomes visible. That fires a `scroll` event here, but the
6189
- // virtual window already contains the focused element — re-rendering
6190
- // would destroy the focused `<li>` and break focus. Detect this case
6191
- // via the flag set in `keyDownHandler` and skip the re-render entirely.
6192
5996
  if (this.virtualSkipNextScrollRender) {
6193
5997
  this.virtualSkipNextScrollRender = false;
6194
- // Update prev scrollTop so a subsequent user-driven scroll still
6195
- // calculates the correct delta.
6196
5998
  this.virtualPrevScrollTop = this.element.scrollTop;
6197
5999
  return;
6198
6000
  }
@@ -6209,30 +6011,16 @@ var TreeView = /** @class */ (function (_super) {
6209
6011
  _this.virtualStartIndex = win.startIndex;
6210
6012
  _this.virtualEndIndex = win.endIndex;
6211
6013
  _this.renderVirtualItems();
6212
- // Hide the skeleton mask after a short delay so it stays visible long enough
6213
- // for the user to perceive the loading indicator even on short scrolls.
6214
6014
  setTimeout(function () {
6215
6015
  _this.hideVirtualMask();
6216
- // Keep the (now hidden) mask's coords fresh for the next show.
6217
6016
  _this.updateMaskPosition();
6218
6017
  }, 200);
6219
6018
  });
6220
6019
  };
6221
- /**
6222
- * Creates the skeleton mask overlay used to indicate loading during fast scrolls.
6223
- * Skeleton instances are stored in `skeletonInstances` for later disposal.
6224
- *
6225
- * @returns {void}
6226
- * @private
6227
- */
6228
6020
  TreeView.prototype.createVirtualMask = function () {
6229
6021
  if (this.virtualMaskElement) {
6230
6022
  return;
6231
6023
  }
6232
- // Use position:fixed so the mask is anchored to the visible viewport (not to the
6233
- // scrolling container). Width/height/left/top are set/refreshed by
6234
- // updateMaskPosition() so the mask always covers the TreeView's visible area
6235
- // regardless of how the page itself is scrolled.
6236
6024
  var rect = this.element.getBoundingClientRect();
6237
6025
  var scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
6238
6026
  var maskParent = this.createElement('div', {
@@ -6245,20 +6033,6 @@ var TreeView = /** @class */ (function (_super) {
6245
6033
  className: VIRTUAL_MASK_ITEM,
6246
6034
  styles: "display:flex;align-items:center;height:" + this.virtualItemHeight + "px;padding:0 12px;gap:8px;"
6247
6035
  });
6248
- // Skeleton circle for icon placeholder
6249
- // const iconHolder: HTMLElement = this.createElement('span', {
6250
- // styles: `width:20px;height:20px;`
6251
- // });
6252
- // maskItem.appendChild(iconHolder);
6253
- // const iconSkel: Skeleton = new Skeleton({
6254
- // shape: 'Circle',
6255
- // width: '20px',
6256
- // height: '20px',
6257
- // cssClass: 'e-virtual-skel-icon'
6258
- // });
6259
- // iconSkel.appendTo(iconHolder);
6260
- // this.skeletonInstances.push(iconSkel);
6261
- // Skeleton text bar
6262
6036
  var textHolder = this.createElement('span');
6263
6037
  textHolder.style.flex = '1';
6264
6038
  textHolder.style.height = '14px';
@@ -6276,14 +6050,6 @@ var TreeView = /** @class */ (function (_super) {
6276
6050
  this.virtualMaskElement = maskParent;
6277
6051
  this.element.appendChild(maskParent);
6278
6052
  };
6279
- /**
6280
- * Refreshes the mask position so it always overlays the TreeView's viewport.
6281
- * Called on every show / scroll, since `position:fixed` is relative to the
6282
- * page viewport, not to the TreeView scroll container.
6283
- *
6284
- * @returns {void}
6285
- * @private
6286
- */
6287
6053
  TreeView.prototype.updateMaskPosition = function () {
6288
6054
  if (!this.virtualMaskElement) {
6289
6055
  return;
@@ -6295,12 +6061,6 @@ var TreeView = /** @class */ (function (_super) {
6295
6061
  this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
6296
6062
  this.virtualMaskElement.style.height = rect.height + 'px';
6297
6063
  };
6298
- /**
6299
- * Destroys all skeleton instances and removes the mask overlay from the DOM.
6300
- *
6301
- * @returns {void}
6302
- * @private
6303
- */
6304
6064
  TreeView.prototype.destroyVirtualMask = function () {
6305
6065
  for (var i = this.skeletonInstances.length - 1; i >= 0; i--) {
6306
6066
  this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
@@ -6313,52 +6073,26 @@ var TreeView = /** @class */ (function (_super) {
6313
6073
  this.virtualMaskElement = null;
6314
6074
  }
6315
6075
  };
6316
- /**
6317
- * Positions and shows the skeleton mask at the current `scrollTop` position.
6318
- *
6319
- * @param {number} _scrollTop - Current scrollTop of the container.
6320
- * @returns {void}
6321
- * @private
6322
- */
6323
6076
  TreeView.prototype.showVirtualMask = function (_scrollTop) {
6324
6077
  if (!this.virtualMaskElement) {
6325
6078
  this.createVirtualMask();
6326
6079
  }
6327
6080
  if (this.virtualMaskElement) {
6328
- // Refresh the fixed-position coords in case the TreeView was moved/resized
6329
- // since the last time the mask was shown.
6330
6081
  this.updateMaskPosition();
6331
6082
  this.virtualMaskElement.style.display = 'block';
6332
- // Visually hide the real list while the skeleton is shown
6333
6083
  if (this.ulElement) {
6334
6084
  this.ulElement.style.visibility = 'hidden';
6335
6085
  }
6336
6086
  }
6337
6087
  };
6338
- /**
6339
- * Hides the skeleton mask overlay after the virtual window has been rendered.
6340
- *
6341
- * @returns {void}
6342
- * @private
6343
- */
6344
6088
  TreeView.prototype.hideVirtualMask = function () {
6345
6089
  if (this.virtualMaskElement) {
6346
6090
  this.virtualMaskElement.style.display = 'none';
6347
- // Restore real list visibility
6348
6091
  if (this.ulElement) {
6349
6092
  this.ulElement.style.visibility = '';
6350
6093
  }
6351
6094
  }
6352
6095
  };
6353
- /**
6354
- * Ensures the node identified by `id` is scrolled into view inside the virtual container.
6355
- * If the node falls outside the rendered window, the container's `scrollTop` is adjusted so
6356
- * that the node becomes visible, the window is re-computed and `renderVirtualItems` is called.
6357
- *
6358
- * @param {string} id - The `data-uid` of the target node.
6359
- * @returns {void}
6360
- * @private
6361
- */
6362
6096
  TreeView.prototype.ensureVirtualNodeVisible = function (id) {
6363
6097
  var idx = this.virtualNodeMap.get(id);
6364
6098
  if (isNOU(idx)) {
@@ -6377,20 +6111,53 @@ var TreeView = /** @class */ (function (_super) {
6377
6111
  this.element.scrollTop = nodeBottom - vpH;
6378
6112
  }
6379
6113
  else {
6380
- return; // already visible, no re-render needed
6114
+ return;
6381
6115
  }
6382
- // Re-render the new window synchronously so the <li> is in the DOM
6383
6116
  var win = this.getVirtualWindow();
6384
6117
  this.virtualStartIndex = win.startIndex;
6385
6118
  this.virtualEndIndex = win.endIndex;
6386
6119
  this.renderVirtualItems();
6387
6120
  };
6121
+ TreeView.prototype.findNodeInTreeData = function (id) {
6122
+ if (!this.treeData) {
6123
+ return null;
6124
+ }
6125
+ var idFieldName = this.fields.id.toString();
6126
+ var childFieldName = this.fields.child.toString();
6127
+ var findRecursive = function (nodes) {
6128
+ if (!nodes || nodes.length === 0) {
6129
+ return null;
6130
+ }
6131
+ for (var i = 0; i < nodes.length; i++) {
6132
+ var node = nodes[i];
6133
+ if (node[idFieldName] && node[idFieldName].toString() === id) {
6134
+ return node;
6135
+ }
6136
+ var children = getValue(childFieldName, node);
6137
+ if (children && Array.isArray(children)) {
6138
+ var found = findRecursive(children);
6139
+ if (found) {
6140
+ return found;
6141
+ }
6142
+ }
6143
+ }
6144
+ return null;
6145
+ };
6146
+ return findRecursive(this.treeData);
6147
+ };
6148
+ TreeView.prototype.getNodeLevel = function (id) {
6149
+ if (!this.virtualFlatNodes) {
6150
+ return 1;
6151
+ }
6152
+ for (var i = 0; i < this.virtualFlatNodes.length; i++) {
6153
+ if (this.virtualFlatNodes[parseInt(i.toString(), 10)].id === id) {
6154
+ return this.virtualFlatNodes[parseInt(i.toString(), 10)].level;
6155
+ }
6156
+ }
6157
+ return 1;
6158
+ };
6388
6159
  TreeView.prototype.handleVirtualExpand = function (id, isKeyboardNav) {
6389
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
6390
- // A scroll RAF queued before the expand click would run asynchronously after
6391
- // this synchronous expand completes, re-rendering the window from a stale
6392
- // scrollTop and moving the parent node to the wrong position. Cancel it now
6393
- // so the expand render is authoritative.
6160
+ var _this = this;
6394
6161
  if (this.virtualScrollRAF !== null) {
6395
6162
  cancelAnimationFrame(this.virtualScrollRAF);
6396
6163
  this.virtualScrollRAF = null;
@@ -6398,64 +6165,85 @@ var TreeView = /** @class */ (function (_super) {
6398
6165
  if (this.expandedNodes.indexOf(id) === -1) {
6399
6166
  this.expandedNodes.push(id);
6400
6167
  }
6401
- // Also update virtualExpandedNodesSet to keep it in sync
6402
6168
  if (this.virtualExpandedNodesSet) {
6403
6169
  this.virtualExpandedNodesSet.add(id);
6404
6170
  }
6405
- if (this.rootData) {
6406
- var acutalRootData = this.rootData.find(function (item) { return item.id === parseInt(id, 10); });
6407
- acutalRootData.isExpanded = true;
6408
- }
6409
- // ── Fix: Pin focused ID to the expanding node for keyboard navigation ──
6410
- // When keyboard-triggered, the expanded node IS definitively the focused node
6411
- // (`id` comes from currLi.getAttribute('data-uid') in expandNode, where currLi
6412
- // is getFocusedNode()). Set unconditionally — the old conditional guard
6413
- // `!this.virtualFocusedId` could leave a stale or null value if a previous
6414
- // RAF render (from arrow-key scroll) hadn't yet completed, causing branch 3
6415
- // (first item) to fire in renderVirtualItems() instead of branch 2.
6416
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
6417
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
6418
- // mouse-based expand/collapse should never add the e-focused class.
6171
+ if (this.treeData) {
6172
+ var acutalRootData = this.findNodeInTreeData(id);
6173
+ if (acutalRootData && this.fields && this.fields.expanded) {
6174
+ setValue(this.fields.expanded, true, acutalRootData);
6175
+ }
6176
+ }
6419
6177
  if (isKeyboardNav) {
6420
6178
  this.virtualFocusedId = id;
6421
6179
  }
6422
6180
  else {
6423
6181
  this.virtualFocusedId = null;
6424
6182
  }
6183
+ if (this.fields.dataSource instanceof DataManager && !this.isOffline) {
6184
+ var parentNode = this.findNodeInTreeData(id);
6185
+ if (parentNode && !isNOU(parentNode)) {
6186
+ var childFieldName = this.fields.child.toString();
6187
+ var childrenObj = getValue(childFieldName, parentNode);
6188
+ var childrenNotLoaded = isNOU(childrenObj) || !Array.isArray(childrenObj) ||
6189
+ childrenObj.length === 0;
6190
+ if (childrenNotLoaded) {
6191
+ this.loadVirtualChildData(id, parentNode, function () {
6192
+ _this.completeVirtualExpand(id, isKeyboardNav);
6193
+ });
6194
+ return;
6195
+ }
6196
+ }
6197
+ }
6198
+ this.completeVirtualExpand(id, isKeyboardNav);
6199
+ };
6200
+ TreeView.prototype.loadVirtualChildData = function (parentId, parentNode, callback) {
6201
+ var _this = this;
6202
+ var level = this.getNodeLevel(parentId);
6203
+ var mapper = this.getChildFields(this.fields, level + 1, 1);
6204
+ if (isNOU(mapper) || isNOU(mapper.dataSource)) {
6205
+ if (callback) {
6206
+ callback();
6207
+ }
6208
+ return;
6209
+ }
6210
+ mapper.dataSource.executeQuery(this.getQuery(mapper, parentId)).then(function (e) {
6211
+ var childItems = e.result;
6212
+ if (!isNOU(childItems) && childItems.length > 0) {
6213
+ var childFieldName = _this.fields.child.toString();
6214
+ setValue(childFieldName, childItems, parentNode);
6215
+ if (_this.dataType === 2) {
6216
+ var nodeInTree = _this.findNodeInTreeData(parentId);
6217
+ if (nodeInTree) {
6218
+ setValue(childFieldName, childItems, nodeInTree);
6219
+ }
6220
+ }
6221
+ }
6222
+ if (callback) {
6223
+ callback();
6224
+ }
6225
+ }).catch(function (e) {
6226
+ _this.trigger('actionFailure', { error: e });
6227
+ if (callback) {
6228
+ callback();
6229
+ }
6230
+ });
6231
+ };
6232
+ TreeView.prototype.completeVirtualExpand = function (id, isKeyboardNav) {
6425
6233
  this.virtualFlatNodes = this.flattenVisibleNodes();
6426
6234
  this.rebuildVirtualNodeMap();
6427
- // ── Snapshot scrollTop before computing the window ────────────────────────
6428
- // Read scrollTop once here and pass it into getVirtualWindow so that any
6429
- // browser scroll-anchoring adjustment that happens when the padding values
6430
- // change cannot affect which window we render.
6431
6235
  var scrollTopSnapshot = this.element.scrollTop;
6432
6236
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
6433
6237
  this.virtualStartIndex = win.startIndex;
6434
6238
  this.virtualEndIndex = win.endIndex;
6435
- // ── Block ALL scroll-triggered re-renders during this expand ─────────────
6436
- // renderVirtualItems() calls li.focus() in branch 2 (keyboard focus restore).
6437
- // The post-render focus() call below can also trigger browser scroll. Both
6438
- // can fire the 'scroll' event and cause onVirtualScroll() to queue a RAF.
6439
- // We set the skip flag ONCE here; it is consumed by the first scroll event.
6440
- // To cover the second potential scroll (from the focus() call below) we use
6441
- // preventScroll:true on that call so it never moves the container at all.
6442
6239
  this.virtualSkipNextScrollRender = true;
6443
6240
  this.renderVirtualItems();
6444
- // renderVirtualItems() replaces all <li> elements so the old icon element that
6445
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
6446
- // so the node can be collapsed/expanded again without being blocked.
6447
6241
  var newLi = this.getElement(id);
6448
6242
  if (newLi) {
6449
6243
  var icon = select('.' + ICON, newLi);
6450
6244
  if (icon) {
6451
6245
  removeClass([icon], PROCESS);
6452
6246
  }
6453
- // ── Use preventScroll:true ────────────────────────────────────────────
6454
- // Without preventScroll the browser scrolls the container to bring this
6455
- // node into view, which fires a second 'scroll' event. The first scroll
6456
- // was already consumed by virtualSkipNextScrollRender (set above), so the
6457
- // second scroll would queue a new RAF that re-renders from the
6458
- // focus-adjusted scrollTop — moving the parent node to the wrong position.
6459
6247
  newLi.focus({ preventScroll: true });
6460
6248
  }
6461
6249
  if (this.isLoaded) {
@@ -6468,9 +6256,6 @@ var TreeView = /** @class */ (function (_super) {
6468
6256
  }
6469
6257
  };
6470
6258
  TreeView.prototype.handleVirtualCollapse = function (id, isKeyboardNav) {
6471
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
6472
- // Mirrors handleVirtualExpand: a queued RAF from a previous scroll event
6473
- // must not override the collapse render with a stale window.
6474
6259
  if (this.virtualScrollRAF !== null) {
6475
6260
  cancelAnimationFrame(this.virtualScrollRAF);
6476
6261
  this.virtualScrollRAF = null;
@@ -6479,21 +6264,15 @@ var TreeView = /** @class */ (function (_super) {
6479
6264
  if (idx !== -1) {
6480
6265
  this.expandedNodes.splice(idx, 1);
6481
6266
  }
6482
- // Also update virtualExpandedNodesSet to keep it in sync
6483
6267
  if (this.virtualExpandedNodesSet) {
6484
6268
  this.virtualExpandedNodesSet.delete(id);
6485
6269
  }
6486
- if (this.rootData) {
6487
- var acutalRootData = this.rootData.find(function (item) { return item.id === parseInt(id, 10); });
6488
- acutalRootData.isExpanded = false;
6489
- }
6490
- // ── Fix: Pin focused ID to the collapsing node for keyboard navigation ──
6491
- // Same reasoning as handleVirtualExpand: when keyboard-triggered, `id` is
6492
- // definitively the focused node. Set unconditionally to avoid the stale/null
6493
- // state that could cause the RAF re-render to fall through to branch 3.
6494
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
6495
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
6496
- // mouse-based expand/collapse should never add the e-focused class.
6270
+ if (this.treeData) {
6271
+ var acutalRootData = this.findNodeInTreeData(id);
6272
+ if (acutalRootData && this.fields && this.fields.expanded) {
6273
+ setValue(this.fields.expanded, false, acutalRootData);
6274
+ }
6275
+ }
6497
6276
  if (isKeyboardNav) {
6498
6277
  this.virtualFocusedId = id;
6499
6278
  }
@@ -6502,28 +6281,18 @@ var TreeView = /** @class */ (function (_super) {
6502
6281
  }
6503
6282
  this.virtualFlatNodes = this.flattenVisibleNodes();
6504
6283
  this.rebuildVirtualNodeMap();
6505
- // ── Snapshot scrollTop before computing the window ────────────────────────
6506
- // Same rationale as handleVirtualExpand: capture scrollTop before any focus()
6507
- // call or padding change can cause the browser to auto-scroll.
6508
6284
  var scrollTopSnapshot = this.element.scrollTop;
6509
6285
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
6510
6286
  this.virtualStartIndex = win.startIndex;
6511
6287
  this.virtualEndIndex = win.endIndex;
6512
- // ── Block ALL scroll-triggered re-renders during this collapse ────────────
6513
- // Same as handleVirtualExpand: use preventScroll:true on the focus() call
6514
- // below so the second potential scroll event is never fired.
6515
6288
  this.virtualSkipNextScrollRender = true;
6516
6289
  this.renderVirtualItems();
6517
- // renderVirtualItems() replaces all <li> elements so the old icon element that
6518
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
6519
- // so the node can be expanded/collapsed again without being blocked.
6520
6290
  var newLi = this.getElement(id);
6521
6291
  if (newLi) {
6522
6292
  var icon = select('.' + ICON, newLi);
6523
6293
  if (icon) {
6524
6294
  removeClass([icon], PROCESS);
6525
6295
  }
6526
- // ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
6527
6296
  newLi.focus({ preventScroll: true });
6528
6297
  }
6529
6298
  if (this.isLoaded) {
@@ -6535,36 +6304,18 @@ var TreeView = /** @class */ (function (_super) {
6535
6304
  }
6536
6305
  }
6537
6306
  };
6538
- /**
6539
- * Updates the selected state of a node in `virtualFlatNodes` and re-renders the window.
6540
- *
6541
- * @param {string} id - The `data-uid` of the target node.
6542
- * @param {boolean} isSelected - Whether the node is now selected.
6543
- * @returns {void}
6544
- * @private
6545
- */
6546
- TreeView.prototype.updateVirtualNodeSelection = function (id, isSelected) {
6307
+ TreeView.prototype.updateVirtualNodeSelection = function (id, selected) {
6547
6308
  var idx = this.virtualNodeMap.get(id);
6548
6309
  if (!isNOU(idx)) {
6549
- this.virtualFlatNodes[parseInt(idx.toString(), 10)].isSelected = isSelected;
6310
+ this.virtualFlatNodes[parseInt(idx.toString(), 10)].selected = selected;
6550
6311
  this.renderVirtualItems();
6551
6312
  }
6552
6313
  };
6553
- /**
6554
- * Updates the checkbox state of a node in `virtualFlatNodes` and re-renders the window.
6555
- *
6556
- * @param {string} id - The `data-uid` of the target node.
6557
- * @param {'check' | 'uncheck' | 'indeterminate'} state - The new check state.
6558
- * @returns {void}
6559
- * @private
6560
- */
6561
6314
  TreeView.prototype.updateVirtualNodeCheckState = function (id, state) {
6562
6315
  var idx = this.virtualNodeMap.get(id);
6563
6316
  if (!isNOU(idx)) {
6564
6317
  this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
6565
6318
  state === 'check' ? true : state === 'indeterminate' ? 'indeterminate' : false;
6566
- // Keep the persistent indeterminate set in sync so flattenVisibleNodes()
6567
- // correctly restores state after any full list rebuild (e.g. collapse).
6568
6319
  if (state === 'indeterminate') {
6569
6320
  this.virtualIndeterminateNodesSet.add(id);
6570
6321
  }
@@ -6574,9 +6325,6 @@ var TreeView = /** @class */ (function (_super) {
6574
6325
  this.renderVirtualItems();
6575
6326
  }
6576
6327
  };
6577
- // ═══════════════════════════════════════════════════════════════════════════════
6578
- // END VIRTUALIZATION
6579
- // ═══════════════════════════════════════════════════════════════════════════════
6580
6328
  /**
6581
6329
  * Called internally if any of the property value changed.
6582
6330
  *
@@ -6721,7 +6469,6 @@ var TreeView = /** @class */ (function (_super) {
6721
6469
  }
6722
6470
  }
6723
6471
  else {
6724
- // Tear down virtualization and do a full re-render
6725
6472
  this.unwireVirtualScrollEvents();
6726
6473
  this.destroyVirtualMask();
6727
6474
  removeClass([this.element], VIRTUALIZATION);
@@ -6732,7 +6479,6 @@ var TreeView = /** @class */ (function (_super) {
6732
6479
  break;
6733
6480
  case 'height':
6734
6481
  if (this.enableVirtualization) {
6735
- // Re-initialise so the new height takes effect
6736
6482
  this.unwireVirtualScrollEvents();
6737
6483
  this.initVirtualization();
6738
6484
  }
@@ -6750,7 +6496,6 @@ var TreeView = /** @class */ (function (_super) {
6750
6496
  * @returns {void}
6751
6497
  */
6752
6498
  TreeView.prototype.destroy = function () {
6753
- // Tear down virtualization before standard cleanup
6754
6499
  if (this.enableVirtualization) {
6755
6500
  this.unwireVirtualScrollEvents();
6756
6501
  this.destroyVirtualMask();
@@ -6841,10 +6586,6 @@ var TreeView = /** @class */ (function (_super) {
6841
6586
  if (isNOU(fNode)) {
6842
6587
  this.setNodeFocusable();
6843
6588
  }
6844
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
6845
- // the window so the newly added nodes appear in the correct hierarchical position.
6846
- // When height is not provided, the tree uses normal rendering and the DOM is
6847
- // already updated by addGivenNodes() above.
6848
6589
  if (this.enableVirtualization && !isNOU(this.height)) {
6849
6590
  this.setRootData();
6850
6591
  this.rebuildVirtualCheckCache();
@@ -6914,7 +6655,7 @@ var TreeView = /** @class */ (function (_super) {
6914
6655
  }
6915
6656
  for (var i = 0; i < nodes.length; i++) {
6916
6657
  var node = nodes[parseInt(i.toString(), 10)];
6917
- node['isExpanded'] = false;
6658
+ node['expanded'] = false;
6918
6659
  var children = getValue(_this.fields.child.toString(), node);
6919
6660
  if (children && Array.isArray(children)) {
6920
6661
  setAllCollapsed_1(children);
@@ -7014,7 +6755,7 @@ var TreeView = /** @class */ (function (_super) {
7014
6755
  var nodeId = getValue(idFieldName_1, node);
7015
6756
  if (node.hasChild) {
7016
6757
  allParentIds_1.push(nodeId);
7017
- node.isExpanded = true;
6758
+ node.expanded = true;
7018
6759
  }
7019
6760
  }
7020
6761
  };