@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
@@ -1506,8 +1506,10 @@ var MenuBase = /** @__PURE__ @class */ (function (_super) {
1506
1506
  if (!active) {
1507
1507
  return;
1508
1508
  }
1509
+ var isInputElement = active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' ||
1510
+ active.contentEditable === 'true';
1509
1511
  var expanded = active.getAttribute('aria-expanded') === 'true';
1510
- if (!expanded) {
1512
+ if (!expanded || isInputElement) {
1511
1513
  return;
1512
1514
  }
1513
1515
  var ul = this.element;
@@ -1621,13 +1623,19 @@ var MenuBase = /** @__PURE__ @class */ (function (_super) {
1621
1623
  var cli = cul.children[fliIdx];
1622
1624
  fliIdx = this.isValidLI(cli, fliIdx, e.action);
1623
1625
  cul.children[fliIdx].classList.add(FOCUSED);
1624
- cul.children[fliIdx].focus();
1626
+ var liToFocus = cul.children[fliIdx];
1625
1627
  if (closest(this.element, '.e-popup')) {
1626
- var focusedLiItem = cul.children[fliIdx];
1627
- var focusableChild = focusedLiItem.querySelector('[tabindex], span[tabindex], a, button, [tabindex]:not([tabindex="-1"])');
1628
- if (!focusableChild) {
1629
- cul.focus();
1628
+ var focusableChild = liToFocus.querySelector('[tabindex], span[tabindex], a, button, [tabindex]:not([tabindex="-1"])');
1629
+ var isSpreadsheet = this.element.id.includes('_cf_menu') && this.element.parentElement.classList.contains('e-cf-menu');
1630
+ if (focusableChild && !isSpreadsheet) {
1631
+ focusableChild.focus();
1630
1632
  }
1633
+ else {
1634
+ liToFocus.focus();
1635
+ }
1636
+ }
1637
+ else {
1638
+ liToFocus.focus();
1631
1639
  }
1632
1640
  };
1633
1641
  MenuBase.prototype.isValidLI = function (cli, index, action) {
@@ -11016,54 +11024,25 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
11016
11024
  _this.isDropIn = false;
11017
11025
  _this.OldCheckedData = [];
11018
11026
  _this.isHiddenItem = false;
11019
- // ── Virtualization state ──────────────────────────────────────────────────────────────
11020
- /** Flat ordered list of all currently-visible (non-collapsed) tree nodes. */
11027
+ // Virtualization state
11021
11028
  _this.virtualFlatNodes = [];
11022
- /** Maps node id → index in virtualFlatNodes for O(1) lookups. */
11023
11029
  _this.virtualNodeMap = new Map();
11024
- /** Set of expanded node IDs for O(1) lookup performance during flattening (avoids array indexOf). */
11025
11030
  _this.virtualExpandedNodesSet = new Set();
11026
- /** Set of checked node IDs for O(1) lookup performance during flattening. */
11027
11031
  _this.virtualCheckedNodesSet = new Set();
11028
- /** Set of indeterminate node IDs — persists across flattenVisibleNodes() rebuilds. */
11029
11032
  _this.virtualIndeterminateNodesSet = new Set();
11030
- /** Set of selected node IDs for O(1) lookup performance during flattening. */
11031
11033
  _this.virtualSelectedNodesSet = new Set();
11032
- /** Row height in pixels, measured from the first rendered item or set to 40 as a default. */
11033
11034
  _this.virtualItemHeight = 40;
11034
- /** Height of the scrollable container in pixels. */
11035
11035
  _this.virtualViewportHeight = 0;
11036
- /** scrollTop captured during the previous scroll handler invocation. */
11037
11036
  _this.virtualPrevScrollTop = 0;
11038
- /** Index of the first rendered row in the current window. */
11039
11037
  _this.virtualStartIndex = 0;
11040
- /** Index one past the last rendered row in the current window. */
11041
11038
  _this.virtualEndIndex = 0;
11042
- /** Extra rows rendered above and below the visible window to smooth scrolling. */
11043
11039
  _this.virtualOverscanCount = 3;
11044
- /** RequestAnimationFrame handle used for throttling scroll events. */
11045
11040
  _this.virtualScrollRAF = null;
11046
- /** The `.e-mask-parent` overlay element shown during fast scroll. */
11047
11041
  _this.virtualMaskElement = null;
11048
- /** Bound scroll handler stored for later removeEventListener. */
11049
11042
  _this.virtualScrollBound = null;
11050
- /** Skeleton component instances inside the mask overlay. */
11051
11043
  _this.skeletonInstances = [];
11052
- /** Set to true whenever the flat node list must be rebuilt on next render. */
11053
11044
  _this.virtualTreeDirty = false;
11054
- /** The data-uid of the currently keyboard-focused node in virtual mode (persists across re-renders). */
11055
11045
  _this.virtualFocusedId = null;
11056
- /**
11057
- * Set to true by the document-level `keydown` handler when the user presses
11058
- * the Tab key. The browser will auto-scroll the focused element into view
11059
- * immediately after focus, which fires a `scroll` event on the TreeView
11060
- * container. We want to skip the re-render that the virtual scroll handler
11061
- * would otherwise trigger, because the element being focused is already
11062
- * rendered (it is the single `<li tabindex="0">` of the current window) and
11063
- * re-rendering would tear down the DOM element that is currently holding
11064
- * focus. The flag is consumed (reset to `false`) inside `onVirtualScroll`
11065
- * after the auto-scroll has been handled.
11066
- */
11067
11046
  _this.virtualSkipNextScrollRender = false;
11068
11047
  return _this;
11069
11048
  }
@@ -11366,16 +11345,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
11366
11345
  }
11367
11346
  };
11368
11347
  TreeView.prototype.renderItems = function (isSorted) {
11369
- // In virtual mode (with a fixed height) create only the container <ul> —
11370
- // initVirtualization will populate the items directly, avoiding the O(n)
11371
- // ListBase.createList cost.
11372
- // updateCheckedStateFromDS / updateParentCheckState are intentionally skipped here:
11373
- // they call getCheckedNodeDetails which traverses the DOM (no DOM exists yet in
11374
- // virtual mode) and would throw on empty recursion results. The initial checked
11375
- // state is derived from the raw data inside flattenNodeRecursive instead.
11376
- //
11377
- // When enableVirtualization is true but height is NOT provided, we fall through
11378
- // to the normal (non-virtualized) rendering path so the tree is still visible.
11379
11348
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
11380
11349
  this.ulElement = this.createElement('ul', {
11381
11350
  className: PARENTITEM + ' ' + 'e-ul',
@@ -12462,7 +12431,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12462
12431
  this.checkDisabledState(id);
12463
12432
  }
12464
12433
  if (this.enableVirtualization) {
12465
- // Clear any indeterminate state — node is now fully checked
12466
12434
  this.virtualIndeterminateNodesSet.delete(id);
12467
12435
  var actualIndex = this.virtualNodeMap.get(id);
12468
12436
  if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
@@ -12483,19 +12451,16 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12483
12451
  var actualIndex = this.virtualNodeMap.get(id);
12484
12452
  if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
12485
12453
  if (isIndeterminate) {
12486
- // Parent is transitioning to indeterminate — preserve children states
12487
- // and mark this node as 'indeterminate' so scroll/collapse re-renders correctly.
12488
12454
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = 'indeterminate';
12489
- // Also record it in the persistent indeterminate set so flattenVisibleNodes()
12490
- // can restore this state after a full rebuild (e.g. on collapse).
12491
12455
  this.virtualIndeterminateNodesSet.add(id);
12492
12456
  }
12493
12457
  else {
12494
- // Node is being fully unchecked — clear indeterminate tracking too
12495
12458
  this.virtualIndeterminateNodesSet.delete(id);
12496
12459
  this.virtualCheckedNodesSet.delete(id);
12497
12460
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = false;
12498
- this.treeData[parseInt(id.toString(), 10) - 1].isChecked = false;
12461
+ if (this.treeData && this.fields && this.fields.isChecked) {
12462
+ this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
12463
+ }
12499
12464
  if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
12500
12465
  this.setVirtualChildCheckState(id, false);
12501
12466
  }
@@ -12540,8 +12505,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12540
12505
  }
12541
12506
  }
12542
12507
  }
12543
- // Collapsed children are not present in virtualFlatNodes, so walk the raw data
12544
- // to keep this.treeData and checkedNodes in sync for hidden descendants too.
12545
12508
  var collapsedChildren = this.getChildNodes(this.treeData, parentId);
12546
12509
  if (collapsedChildren) {
12547
12510
  for (var i = 0; i < collapsedChildren.length; i++) {
@@ -12687,7 +12650,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12687
12650
  }
12688
12651
  this.isLoaded = true;
12689
12652
  this.isNodeDropped = false;
12690
- // Initialise (or re-initialise) virtual mode after data binding has settled
12691
12653
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
12692
12654
  this.initVirtualization();
12693
12655
  }
@@ -12827,9 +12789,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12827
12789
  };
12828
12790
  TreeView.prototype.expandNode = function (currLi, icon, loaded, isKeyboardNav) {
12829
12791
  var _this = this;
12830
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
12831
- // Only engage when height is also set; without a fixed height the tree falls
12832
- // back to normal rendering and the standard expand path must be used.
12833
12792
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
12834
12793
  var id = currLi ? currLi.getAttribute('data-uid') : null;
12835
12794
  if (id) {
@@ -12958,9 +12917,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12958
12917
  };
12959
12918
  TreeView.prototype.nodeCollapseAction = function (currLi, icon, colArgs, isKeyboardNav) {
12960
12919
  var _this = this;
12961
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
12962
- // Only engage when height is also set; without a fixed height the tree falls
12963
- // back to normal rendering and the standard collapse path must be used.
12964
12920
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
12965
12921
  var id = currLi ? currLi.getAttribute('data-uid') : null;
12966
12922
  if (id) {
@@ -13431,7 +13387,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13431
13387
  li.setAttribute('tabindex', '0');
13432
13388
  EventHandler.add(li, 'blur', this.focusOut, this);
13433
13389
  this.updateIdAttr(focusedNode, li);
13434
- // Track focused id for virtual mode re-render restoration
13435
13390
  if (this.enableVirtualization) {
13436
13391
  this.virtualFocusedId = li.getAttribute('data-uid');
13437
13392
  }
@@ -13444,6 +13399,13 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13444
13399
  if (!isNullOrUndefined(id) && this.selectedNodes.indexOf(id) === -1) {
13445
13400
  this.selectedNodes.push(id);
13446
13401
  }
13402
+ if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13403
+ this.virtualSelectedNodesSet.add(id);
13404
+ var actualIndex = this.virtualNodeMap.get(id);
13405
+ if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
13406
+ this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
13407
+ }
13408
+ }
13447
13409
  };
13448
13410
  TreeView.prototype.removeSelect = function (liEle) {
13449
13411
  if (this.allowMultiSelection) {
@@ -13457,6 +13419,14 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13457
13419
  if (index > -1) {
13458
13420
  this.selectedNodes.splice(index, 1);
13459
13421
  }
13422
+ if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13423
+ var id = liEle.getAttribute('data-uid');
13424
+ this.virtualSelectedNodesSet.delete(id);
13425
+ var actualIndex = this.virtualNodeMap.get(id);
13426
+ if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
13427
+ this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
13428
+ }
13429
+ }
13460
13430
  };
13461
13431
  TreeView.prototype.removeSelectAll = function () {
13462
13432
  var selectedLI = this.element.querySelectorAll('.' + ACTIVE);
@@ -13471,6 +13441,15 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13471
13441
  }
13472
13442
  removeClass(selectedLI, ACTIVE);
13473
13443
  this.setProperties({ selectedNodes: [] }, true);
13444
+ if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13445
+ this.virtualSelectedNodesSet.clear();
13446
+ for (var i = 0; i < this.virtualFlatNodes.length; i++) {
13447
+ var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
13448
+ if (node.selected) {
13449
+ node.selected = false;
13450
+ }
13451
+ }
13452
+ }
13474
13453
  };
13475
13454
  TreeView.prototype.getSelectEvent = function (currLi, action, e) {
13476
13455
  var nodeData = this.getNodeData(currLi);
@@ -13539,12 +13518,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13539
13518
  }
13540
13519
  };
13541
13520
  TreeView.prototype.nodeExpandAction = function (currLi, icon, expandChild, callback, isFromExpandAll, isKeyboardNav) {
13542
- // In virtual mode (with a fixed height) the flat <li> never has a child <ul>
13543
- // in the DOM, so the PARENTITEM check below would always fail and fall into
13544
- // renderChildNodes(), which corrupts the virtual DOM.
13545
- // Delegate directly to expandNode() which has the handleVirtualExpand fast-path.
13546
- // When height is not provided, fall through to normal expand logic below since
13547
- // the tree is rendered with a full nested DOM structure.
13548
13521
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13549
13522
  this.expandNode(currLi, icon, undefined, isKeyboardNav);
13550
13523
  return;
@@ -13662,9 +13635,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13662
13635
  });
13663
13636
  };
13664
13637
  TreeView.prototype.navigateToFocus = function (isUp) {
13665
- // In virtual mode (with a fixed height) the element itself is the scroll
13666
- // container; use ensureVirtualNodeVisible. When height is not provided,
13667
- // the tree uses normal rendering, so fall through to the standard scroll logic.
13668
13638
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13669
13639
  var focusedLi = this.getFocusedNode();
13670
13640
  if (focusedLi) {
@@ -14096,7 +14066,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14096
14066
  };
14097
14067
  TreeView.prototype.focusNextNode = function (li, isTowards) {
14098
14068
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
14099
- // Determine the id of the node one step in the requested direction
14100
14069
  var currentId = li ? li.getAttribute('data-uid') : null;
14101
14070
  var currentIdx = -1;
14102
14071
  if (!isNullOrUndefined(currentId)) {
@@ -14108,26 +14077,14 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14108
14077
  return;
14109
14078
  }
14110
14079
  var targetId = this.virtualFlatNodes[parseInt(targetIdx.toString(), 10)].id;
14111
- // ── Fix 1: set virtualFocusedId BEFORE calling ensureVirtualNodeVisible ──
14112
- // renderVirtualItems() is called synchronously inside ensureVirtualNodeVisible
14113
- // when a re-render is needed. If virtualFocusedId is still the old value at
14114
- // that point, focus restoration falls back to the first item. Setting it here
14115
- // ensures renderVirtualItems always restores focus to the correct target node.
14116
14080
  this.virtualFocusedId = targetId;
14117
- // ── Fix 2: clear focus classes from ALL nested list items, not just top-level ones ──
14118
- // With hierarchical virtual DOM, the currently focused <li> may be nested inside
14119
- // a child <ul>. Using ':scope > li' only cleans up direct children and leaves
14120
- // stale e-focused / e-node-focus classes on nested nodes across re-renders.
14121
14081
  var allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
14122
14082
  for (var k = 0; k < allLi.length; k++) {
14123
14083
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
14124
14084
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED$1);
14125
14085
  allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
14126
14086
  }
14127
- // Ensure the target <li> is in the DOM window (may trigger a re-render).
14128
- // virtualFocusedId is already set above so renderVirtualItems restores focus correctly.
14129
14087
  this.ensureVirtualNodeVisible(targetId);
14130
- // Re-query both nodes after the potential re-render so setFocus gets live DOM elements
14131
14088
  var prevNode = currentId ? this.getElement(currentId) : li;
14132
14089
  var nextNode_1 = this.getElement(targetId);
14133
14090
  if (!isNullOrUndefined(nextNode_1)) {
@@ -14228,18 +14185,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14228
14185
  if (!focusedElement) {
14229
14186
  return;
14230
14187
  }
14231
- // Virtual-mode guard: when the browser moves focus into the TreeView (e.g. via
14232
- // the Tab key) it always lands on the single `<li tabindex="0">`. In that
14233
- // case the element is already the focusable one, FOCUS is already applied
14234
- // and the only thing left to do is the keyboard focus styling. Skip the
14235
- // redundant setAttribute/EventHandler.add work so we never re-render the
14236
- // virtual window as a side effect of focusing the existing `tabindex="0"`
14237
- // item.
14238
- // ── Fix: Use document.activeElement to detect browser-focused node ──
14239
- // After renderVirtualItems() restores focus, the browser-focused element is
14240
- // the authoritative source, not getFocusedNode(). If they don't match, it means
14241
- // renderVirtualItems already correctly set up the element, so we must not
14242
- // override its setup by calling setFocus again.
14243
14188
  var actuallyFocusedElement = document.activeElement;
14244
14189
  var isAlreadyFocusedInVirtualMode = this.enableVirtualization
14245
14190
  && actuallyFocusedElement === focusedElement
@@ -14494,12 +14439,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14494
14439
  else {
14495
14440
  txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
14496
14441
  }
14497
- // ── Virtual mode: sync the flat-node snapshot so re-renders show updated text ──
14498
- // virtualFlatNodes is rebuilt from this.treeData (via flattenVisibleNodes) only on
14499
- // expand/collapse/scroll. Between those events the snapshot is the single source of
14500
- // truth used by createVirtualListItem. setValue() above updates this.editData (which
14501
- // IS the treeData object reference), but virtualFlatNodes[idx].text still holds the
14502
- // old value. Update it here so the next renderVirtualItems() call shows the new text.
14503
14442
  if (this.enableVirtualization) {
14504
14443
  var id = liEle.getAttribute('data-uid');
14505
14444
  var idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
@@ -14944,11 +14883,49 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14944
14883
  this.fields.dataSource = this.treeData;
14945
14884
  }
14946
14885
  this.triggerEvent('nodeDropped', nodeData);
14886
+ if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
14887
+ this.setRootData();
14888
+ this.rebuildVirtualCheckCache();
14889
+ this.virtualFlatNodes = this.flattenVisibleNodes();
14890
+ this.rebuildVirtualNodeMap();
14891
+ var scrollTopSnapshot = this.element.scrollTop;
14892
+ var win = this.getVirtualWindowAt(scrollTopSnapshot);
14893
+ this.virtualStartIndex = win.startIndex;
14894
+ this.virtualEndIndex = win.endIndex;
14895
+ this.virtualSkipNextScrollRender = true;
14896
+ this.renderVirtualItems();
14897
+ }
14947
14898
  };
14948
14899
  TreeView.prototype.appendNode = function (dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
14949
14900
  var checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
14950
14901
  var collapse = closest(e.target, '.' + COLLAPSIBLE);
14951
14902
  var expand = closest(e.target, '.' + EXPANDABLE);
14903
+ if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
14904
+ var routesToSiblingBranch = !dragLi.classList.contains('e-disable') && !checkWrapper &&
14905
+ ((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) ||
14906
+ (expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) ||
14907
+ (!expand && !collapse));
14908
+ if (routesToSiblingBranch) {
14909
+ if (dropTarget.nodeName === 'LI') {
14910
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
14911
+ }
14912
+ else if (dropTarget.firstElementChild && dropTarget.classList.contains(ROOT)) {
14913
+ if (dropTarget.firstElementChild.nodeName === 'UL') {
14914
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
14915
+ }
14916
+ }
14917
+ else if ((dropTarget.classList.contains('e-icon-collapsible')) || (dropTarget.classList.contains('e-icon-expandable'))) {
14918
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
14919
+ }
14920
+ else {
14921
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
14922
+ }
14923
+ }
14924
+ else {
14925
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
14926
+ }
14927
+ return;
14928
+ }
14952
14929
  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))) {
14953
14930
  if (dropTarget.nodeName === 'LI') {
14954
14931
  this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
@@ -14972,6 +14949,62 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14972
14949
  this.ensureIndeterminate();
14973
14950
  }
14974
14951
  };
14952
+ TreeView.prototype.virtualMoveAsSibling = function (dragLi, dropLi, e, dragObj) {
14953
+ var dropUl = closest(dropLi, '.' + PARENTITEM);
14954
+ var pre;
14955
+ if (e.target.offsetHeight > 0 && e.event.offsetY > e.target.offsetHeight - 2) {
14956
+ pre = false;
14957
+ }
14958
+ else if (e.event.offsetY < 2) {
14959
+ pre = true;
14960
+ }
14961
+ else if (e.target.classList.contains('e-icon-expandable') || (e.target.classList.contains('e-icon-collapsible'))) {
14962
+ if ((e.event.offsetY < 5) || (e.event.offsetX < 3)) {
14963
+ pre = true;
14964
+ }
14965
+ else if ((e.event.offsetY > 15) || (e.event.offsetX > 17)) {
14966
+ pre = false;
14967
+ }
14968
+ }
14969
+ this.moveData(dragLi, dropLi, dropUl, pre, dragObj);
14970
+ };
14971
+ TreeView.prototype.virtualMoveAsChild = function (dragLi, dropLi, dragObj, offsetY, dropTarget) {
14972
+ var pos = offsetY;
14973
+ var isSiblingMarker = dropTarget.classList.contains('e-sibling');
14974
+ var refLi = (pos < 7 && !isSiblingMarker) || (isSiblingMarker &&
14975
+ dropLi.lastChild && !dropLi.lastChild.classList.contains('e-sibling'))
14976
+ ? dropLi : null;
14977
+ var dropTargetHt = dropTarget;
14978
+ var isAfter = (dropTargetHt.offsetHeight > 0 && pos > dropTargetHt.offsetHeight - 10);
14979
+ if (refLi) {
14980
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
14981
+ this.moveData(dragLi, dropLi, dropParentUl, true, dragObj);
14982
+ }
14983
+ else if (isAfter) {
14984
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
14985
+ this.moveData(dragLi, dropLi, dropParentUl, false, dragObj);
14986
+ }
14987
+ else {
14988
+ var dropIcon = select('div.' + EXPANDABLE + ', div.' + COLLAPSIBLE, dropLi);
14989
+ var dropIsCollapsedParent = dropIcon && dropIcon.classList.contains(EXPANDABLE);
14990
+ var dropIsLeaf = isNullOrUndefined(dropIcon);
14991
+ if (this.preventExpand !== true && (dropIsCollapsedParent || dropIsLeaf)) {
14992
+ var dropLiId = dropLi.getAttribute('data-uid');
14993
+ if (dropLiId && this.expandedNodes.indexOf(dropLiId) === -1) {
14994
+ this.expandedNodes.push(dropLiId);
14995
+ }
14996
+ if (this.virtualExpandedNodesSet && dropLiId) {
14997
+ this.virtualExpandedNodesSet.add(dropLiId);
14998
+ }
14999
+ var dropRecord = this.findNodeInTreeData(dropLiId);
15000
+ if (dropRecord && this.fields && this.fields.expanded) {
15001
+ setValue(this.fields.expanded, true, dropRecord);
15002
+ }
15003
+ }
15004
+ var dropUl = select('.' + PARENTITEM, dropLi) || dropLi;
15005
+ this.moveData(dragLi, null, dropUl, true, dragObj);
15006
+ }
15007
+ };
14975
15008
  TreeView.prototype.dropAsSiblingNode = function (dragLi, dropLi, e, dragObj) {
14976
15009
  var dropUl = closest(dropLi, '.' + PARENTITEM);
14977
15010
  var dragParentUl = closest(dragLi, '.' + PARENTITEM);
@@ -15059,7 +15092,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15059
15092
  delete removedData[0][this.fields.parentID];
15060
15093
  }
15061
15094
  else {
15062
- var currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
15095
+ var currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
15063
15096
  setValue(this.fields.parentID, currPid, removedData[0]);
15064
15097
  }
15065
15098
  this.treeData.splice(pos, 0, removedData[0]);
@@ -15201,7 +15234,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15201
15234
  var draggedNodeData = obj ? obj.dragData : null;
15202
15235
  var newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
15203
15236
  var dragLiParent = obj.dragLi.parentElement;
15204
- var dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
15237
+ var dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
15205
15238
  var targetParent = null;
15206
15239
  var indexValue = null;
15207
15240
  var iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
@@ -15210,7 +15243,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15210
15243
  var index = node ? closest(node, '.e-list-parent') : null;
15211
15244
  var i = 0;
15212
15245
  var position = null;
15213
- dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
15246
+ dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
15214
15247
  dragParent = (drop === true) ? this.dragParent : dragParent;
15215
15248
  if (cloneEle) {
15216
15249
  while (i < 4) {
@@ -15884,26 +15917,10 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15884
15917
  TreeView.prototype.keyDownHandler = function (e) {
15885
15918
  if (e.key === 'Tab' || e.keyCode === 9) {
15886
15919
  this.isKeyboardFocus = true;
15887
- // Mark the upcoming scroll event (if any) as Tab-induced so the
15888
- // virtual scroll handler skips its re-render path. The browser
15889
- // calls `Element.scrollIntoView` semantics on focus, which scrolls
15890
- // a `<li tabindex="0">` in the middle of the tree into view and
15891
- // therefore fires `scroll` on the container. Without this guard
15892
- // the scroll handler would tear down the focused `<li>` and move
15893
- // focus elsewhere.
15894
15920
  if (this.enableVirtualization) {
15895
15921
  this.virtualSkipNextScrollRender = true;
15896
15922
  }
15897
15923
  }
15898
- // ── Fix: Also skip scroll render for arrow key navigation ──────────────────
15899
- // Arrow Down/Up calls focusNextNode() which calls ensureVirtualNodeVisible()
15900
- // to scroll the target node into view. Without this guard, the scroll event
15901
- // would trigger onVirtualScroll() which re-renders the window and resets the
15902
- // focus that focusNextNode() just set. This manifests as tabindex resetting
15903
- // to the first item instead of staying on the arrow-navigated target.
15904
- // Arrow Right/Left (expand/collapse) also calls renderVirtualItems() which
15905
- // calls li.focus() in branch 2; that can trigger a scroll on an edge node,
15906
- // so we consume it here to prevent a stale RAF re-render.
15907
15924
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
15908
15925
  || e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
15909
15926
  if (this.enableVirtualization) {
@@ -16265,10 +16282,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16265
16282
  if (this.dataType === 1) {
16266
16283
  this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
16267
16284
  }
16268
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
16269
- // after deletion so the removed nodes are correctly reflected in the hierarchical
16270
- // virtual DOM. When height is not provided, the tree uses normal rendering and
16271
- // the DOM is already updated by removeNode() above.
16272
16285
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
16273
16286
  this.virtualFlatNodes = this.flattenVisibleNodes();
16274
16287
  this.rebuildVirtualNodeMap();
@@ -16337,16 +16350,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16337
16350
  }
16338
16351
  return parentsID;
16339
16352
  };
16340
- // ═══════════════════════════════════════════════════════════════════════════════
16341
- // VIRTUALIZATION — private helpers
16342
- // ═══════════════════════════════════════════════════════════════════════════════
16343
- /**
16344
- * Parses the `height` property (string like '400px' or number) to a pixel value.
16345
- * Returns 0 if the value cannot be determined.
16346
- *
16347
- * @returns {number} Pixel height.
16348
- * @private
16349
- */
16350
16353
  TreeView.prototype.parseHeightToPixels = function () {
16351
16354
  var h = this.height;
16352
16355
  if (isNullOrUndefined(h)) {
@@ -16358,13 +16361,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16358
16361
  var parsed = parseFloat(h);
16359
16362
  return isNaN(parsed) ? 0 : parsed;
16360
16363
  };
16361
- /**
16362
- * Measures the height of the first rendered list item so subsequent calculations
16363
- * use real layout values. Falls back to the stored `virtualItemHeight` default (40).
16364
- *
16365
- * @returns {number} Row height in pixels.
16366
- * @private
16367
- */
16368
16364
  TreeView.prototype.measureVirtualItemHeight = function () {
16369
16365
  var firstLi = select('.' + LISTITEM, this.element);
16370
16366
  if (firstLi && firstLi.offsetHeight > 0) {
@@ -16372,13 +16368,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16372
16368
  }
16373
16369
  return this.virtualItemHeight;
16374
16370
  };
16375
- /**
16376
- * Rebuilds the cached Sets for checked/selected nodes to enable O(1) lookup
16377
- * during the flatten operation instead of O(n) array indexOf calls.
16378
- *
16379
- * @returns {void}
16380
- * @private
16381
- */
16382
16371
  TreeView.prototype.rebuildVirtualCheckCache = function () {
16383
16372
  this.virtualExpandedNodesSet.clear();
16384
16373
  this.virtualCheckedNodesSet.clear();
@@ -16399,20 +16388,10 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16399
16388
  }
16400
16389
  }
16401
16390
  };
16402
- /**
16403
- * Recursively walks the treeData (or groupedData for flat lists) and builds
16404
- * `virtualFlatNodes` — an ordered list of every node that is currently visible
16405
- * (i.e. its ancestors are all expanded). Uses cached Sets for O(1) state lookups.
16406
- *
16407
- * @returns {VirtualNodeData[]} Ordered flat list of visible nodes.
16408
- * @private
16409
- */
16410
16391
  TreeView.prototype.flattenVisibleNodes = function () {
16411
- // Build cached Sets for fast lookups
16412
16392
  this.rebuildVirtualCheckCache();
16413
16393
  var result = [];
16414
16394
  if (this.dataType === 1) {
16415
- // Flat data with parentID field — use getChildGroup to find root nodes
16416
16395
  var rootNodes = [];
16417
16396
  if (this.groupedData) {
16418
16397
  rootNodes = this.getChildGroup(this.groupedData, null, true);
@@ -16420,39 +16399,29 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16420
16399
  if (!rootNodes || rootNodes.length === 0) {
16421
16400
  rootNodes = this.rootData;
16422
16401
  }
16423
- // Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
16424
16402
  rootNodes = this.getSortedData(rootNodes);
16425
16403
  this.flattenNodeRecursive(rootNodes, 1, null, result);
16426
16404
  }
16427
16405
  else {
16428
- // Hierarchical data
16429
16406
  var sorted = this.getSortedData(this.rootData);
16430
16407
  this.flattenNodeRecursive(sorted, 1, null, result);
16431
16408
  }
16432
16409
  return result;
16433
16410
  };
16434
- /**
16435
- * Recursive helper for `flattenVisibleNodes`.
16436
- *
16437
- * @param {Object[]} nodes - Array of raw data objects at this level.
16438
- * @param {number} level - 1-based depth level.
16439
- * @param {string | null} parentId - The parent node id, or null for root.
16440
- * @param {VirtualNodeData[]} result - Accumulator array.
16441
- * @returns {void}
16442
- * @private
16443
- */
16444
16411
  TreeView.prototype.flattenNodeRecursive = function (nodes, level, parentId, result) {
16445
16412
  if (!nodes || nodes.length === 0) {
16446
16413
  return;
16447
16414
  }
16448
- // Cache field names and settings outside the loop to avoid repeated conversions
16449
- var childFieldName = this.fields.child.toString();
16450
- var expandedFieldName = this.fields.expanded;
16451
- var isCheckedFieldName = this.fields.isChecked;
16452
- var iconCssFieldName = this.fields.iconCss;
16453
- var imageUrlFieldName = this.fields.imageUrl;
16454
- var idFieldName = this.fields.id;
16455
- var textFieldName = this.fields.text;
16415
+ var levelMapper = level === 1 ? this.fields : this.getChildFields(this.fields, level, 1);
16416
+ var childFieldName = levelMapper.child && levelMapper.child.toString();
16417
+ var expandedFieldName = levelMapper.expanded;
16418
+ var isCheckedFieldName = levelMapper.isChecked;
16419
+ var iconCssFieldName = levelMapper.iconCss;
16420
+ var imageUrlFieldName = levelMapper.imageUrl;
16421
+ var idFieldName = levelMapper.id;
16422
+ var textFieldName = levelMapper.text;
16423
+ var selectedFieldName = levelMapper.selected;
16424
+ var hasChildrenFieldName = levelMapper.hasChildren;
16456
16425
  var needsSorting = this.sortOrder !== 'None';
16457
16426
  var isDataTypeFlat = this.dataType === 1;
16458
16427
  var enableVirtualization = this.enableVirtualization;
@@ -16460,70 +16429,93 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16460
16429
  var raw = nodes[i];
16461
16430
  var id = isNullOrUndefined(raw[idFieldName]) ? '' : raw[idFieldName].toString();
16462
16431
  var text = isNullOrUndefined(raw[textFieldName]) ? '' : raw[textFieldName].toString();
16463
- // Use expandedNodes as the single source of truth.
16464
16432
  var inExpandedNodes = this.virtualExpandedNodesSet.has(id);
16465
- var rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
16466
- var isExpanded = inExpandedNodes || rawExpanded;
16433
+ var rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
16434
+ var expanded = inExpandedNodes || rawExpanded;
16467
16435
  if (rawExpanded && id) {
16468
16436
  this.expandedNodes.push(id);
16469
16437
  this.virtualExpandedNodesSet.add(id);
16470
16438
  }
16471
- // Inline hasChildRecords check to avoid function call (optimization)
16472
16439
  var hasChildren = false;
16473
- if (isDataTypeFlat) {
16440
+ var explicitHasChildren = hasChildrenFieldName ? getValue(hasChildrenFieldName, raw) : null;
16441
+ if (levelMapper.dataSource instanceof DataManager && !isNullOrUndefined(explicitHasChildren) && explicitHasChildren) {
16442
+ hasChildren = true;
16443
+ }
16444
+ else if (isDataTypeFlat) {
16474
16445
  if (this.groupedData) {
16475
16446
  var groupedChild = this.getChildGroup(this.groupedData, id, false);
16476
16447
  hasChildren = !isNullOrUndefined(groupedChild) && groupedChild.length > 0;
16477
16448
  }
16478
16449
  }
16479
16450
  else {
16480
- var childrenObj = getValue(childFieldName, raw);
16451
+ var childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
16481
16452
  hasChildren = !isNullOrUndefined(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
16482
16453
  }
16483
- // Virtual-mode only: honour fields.isChecked === true on initial render
16484
- if (enableVirtualization && id && isCheckedFieldName && !this.virtualCheckedNodesSet.has(id)) {
16485
- var rawChecked = getValue(isCheckedFieldName, raw);
16486
- if (!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'true') {
16454
+ var parentChecked = null;
16455
+ if (levelMapper.dataSource instanceof DataManager && level > 1 && this.autoCheck && parentId) {
16456
+ if (this.virtualCheckedNodesSet.has(parentId) ||
16457
+ (this.checkedNodes && this.checkedNodes.indexOf(parentId) !== -1)) {
16458
+ parentChecked = true;
16459
+ }
16460
+ else {
16461
+ parentChecked = false;
16462
+ }
16463
+ }
16464
+ var rawChecked = isCheckedFieldName && getValue(isCheckedFieldName, raw);
16465
+ if (enableVirtualization && id && !this.virtualCheckedNodesSet.has(id)) {
16466
+ if ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'true') ||
16467
+ (levelMapper.dataSource instanceof DataManager && parentChecked === true)) {
16487
16468
  this.virtualCheckedNodesSet.add(id);
16488
16469
  if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
16489
16470
  this.checkedNodes.push(id);
16490
16471
  }
16491
16472
  }
16492
16473
  }
16493
- // Use cached Sets for O(1) lookup
16494
- var isSelected = this.virtualSelectedNodesSet.has(id);
16495
- // Resolve checkbox state: indeterminate takes precedence over the checked set,
16496
- // because a node can be in the indeterminate state without being in checkedNodes.
16474
+ else if (enableVirtualization && id && this.virtualCheckedNodesSet.has(id) && levelMapper.dataSource instanceof DataManager) {
16475
+ if (level > 1 && !this.virtualIndeterminateNodesSet.has(parentId) && this.autoCheck &&
16476
+ ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'false') || parentChecked === false)) {
16477
+ this.virtualCheckedNodesSet.delete(id);
16478
+ if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
16479
+ this.checkedNodes.splice(this.checkedNodes.indexOf(id), 1);
16480
+ }
16481
+ }
16482
+ }
16483
+ if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
16484
+ var rawSelected = getValue(selectedFieldName, raw);
16485
+ if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
16486
+ this.virtualSelectedNodesSet.add(id);
16487
+ if (this.selectedNodes && this.selectedNodes.indexOf(id) === -1) {
16488
+ this.selectedNodes.push(id);
16489
+ }
16490
+ }
16491
+ }
16492
+ var selected = this.virtualSelectedNodesSet.has(id);
16497
16493
  var isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
16498
16494
  ? 'indeterminate'
16499
16495
  : this.virtualCheckedNodesSet.has(id);
16500
- var isDisabled = false; // TODO: Check if any per-node disabled field exists
16501
16496
  var iconCssVal = raw[iconCssFieldName];
16502
16497
  var iconCss = isNullOrUndefined(iconCssVal) ? '' : iconCssVal.toString();
16503
16498
  var imageUrlVal = raw[imageUrlFieldName];
16504
16499
  var imageUrl = isNullOrUndefined(imageUrlVal) ? '' : imageUrlVal.toString();
16505
16500
  var node = {
16506
- id: id, text: text, level: level, parentId: parentId, hasChildren: hasChildren, isExpanded: isExpanded,
16507
- isSelected: isSelected, isChecked: isChecked, isDisabled: isDisabled, iconCss: iconCss, imageUrl: imageUrl, rawData: raw
16501
+ id: id, text: text, level: level, parentId: parentId, hasChildren: hasChildren, expanded: expanded,
16502
+ selected: selected, isChecked: isChecked, iconCss: iconCss, imageUrl: imageUrl
16508
16503
  };
16509
16504
  result.push(node);
16510
- if (isExpanded && hasChildren) {
16511
- // Determine children list
16505
+ if (expanded && hasChildren) {
16512
16506
  var children = [];
16513
16507
  if (isDataTypeFlat) {
16514
16508
  if (this.groupedData) {
16515
16509
  children = this.getChildGroup(this.groupedData, id, false);
16516
- // Apply sortOrder to flat-data children (same as hierarchical path)
16517
16510
  if (needsSorting) {
16518
16511
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
16519
16512
  }
16520
16513
  }
16521
16514
  }
16522
16515
  else {
16523
- var rawChildren = getValue(childFieldName, raw);
16516
+ var rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
16524
16517
  if (!isNullOrUndefined(rawChildren) && Array.isArray(rawChildren)) {
16525
16518
  children = rawChildren;
16526
- // Only sort if needed
16527
16519
  if (needsSorting) {
16528
16520
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
16529
16521
  }
@@ -16533,35 +16525,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16533
16525
  }
16534
16526
  }
16535
16527
  };
16536
- /**
16537
- * Checks whether a raw datasource node has child records.
16538
- *
16539
- * @param {Object} raw - Raw datasource object.
16540
- * @returns {boolean} True if children exist.
16541
- * @private
16542
- */
16543
- TreeView.prototype.hasChildRecords = function (raw) {
16544
- if (this.dataType === 1) {
16545
- if (!this.groupedData) {
16546
- return false;
16547
- }
16548
- var rawId = raw[this.fields.id];
16549
- if (isNullOrUndefined(rawId)) {
16550
- return false;
16551
- }
16552
- var groupedChild = this.getChildGroup(this.groupedData, rawId.toString(), false);
16553
- return !isNullOrUndefined(groupedChild) && groupedChild.length > 0;
16554
- }
16555
- var childField = this.fields.child.toString();
16556
- var children = getValue(childField, raw);
16557
- return !isNullOrUndefined(children) && Array.isArray(children) && children.length > 0;
16558
- };
16559
- /**
16560
- * Rebuilds `virtualNodeMap` (id → index) from the current `virtualFlatNodes`.
16561
- *
16562
- * @returns {void}
16563
- * @private
16564
- */
16565
16528
  TreeView.prototype.rebuildVirtualNodeMap = function () {
16566
16529
  this.virtualNodeMap.clear();
16567
16530
  for (var i = 0; i < this.virtualFlatNodes.length; i++) {
@@ -16594,49 +16557,12 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16594
16557
  var paddingBottom = Math.max(0, (total - end) * itemH);
16595
16558
  return { paddingTop: paddingTop, paddingBottom: paddingBottom };
16596
16559
  };
16597
- /**
16598
- * Renders the virtual window into a **nested** `<ul>/<li>` hierarchy identical to
16599
- * the non-virtualized DOM structure.
16600
- *
16601
- * ## Scroll math
16602
- * The flat-index ↔ pixel mapping stays unchanged: every entry in
16603
- * `virtualFlatNodes` represents exactly one `itemHeight` row.
16604
- * `paddingTop = startIndex * itemHeight` and
16605
- * `paddingBottom = (total - endIndex) * itemHeight` keep the scrollbar
16606
- * thumb in the correct position.
16607
- *
16608
- * ## Ancestor injection
16609
- * When `startIndex` points at a child node whose ancestor(s) have already
16610
- * scrolled above the viewport (their flat indices are < startIndex), the
16611
- * ancestors would normally be missing from the DOM, forcing the children to
16612
- * fall back to the root `<ul>` as flat siblings — producing wrong HTML.
16613
- *
16614
- * To fix this, before rendering the window we walk the ancestor chain of the
16615
- * first visible node and collect every ancestor that lies **outside** the
16616
- * window. Those ancestors are injected as real `<li>` containers at the
16617
- * very top of the root `<ul>`. To compensate for their presence, `paddingTop`
16618
- * is reduced by `injectedCount * itemHeight` so the total scroll height
16619
- * remains the same.
16620
- *
16621
- * Once the ancestors are in the DOM they are registered in `renderedParentMap`
16622
- * so the normal window-rendering loop can nest children inside them exactly
16623
- * as it does when both parent and child are in the same window.
16624
- *
16625
- * @returns {void}
16626
- * @private
16627
- */
16628
16560
  TreeView.prototype.renderVirtualItems = function () {
16629
16561
  if (!this.ulElement) {
16630
16562
  return;
16631
16563
  }
16632
16564
  var ul = this.ulElement;
16633
- // ── Snapshot focused ID BEFORE clearing the DOM ───────────────────────────
16634
- // Removing a focused <li> from the DOM triggers a synchronous `blur` event,
16635
- // which calls focusOut() and sets virtualFocusedId = null. Capturing the id
16636
- // here (before any removeChild) ensures focus is correctly restored to the
16637
- // same node after re-render even when keyboard navigation was in progress.
16638
16565
  var focusedUid = this.virtualFocusedId || null;
16639
- // ── Clear previous render (direct children only) ─────────────────────────
16640
16566
  var existingItems = ul.querySelectorAll(':scope > li');
16641
16567
  for (var i = existingItems.length - 1; i >= 0; i--) {
16642
16568
  ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
@@ -16647,26 +16573,22 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16647
16573
  if (this.virtualStartIndex < this.virtualFlatNodes.length) {
16648
16574
  var firstNode = this.virtualFlatNodes[this.virtualStartIndex];
16649
16575
  if (firstNode.parentId) {
16650
- // Build the ancestor chain from root down to the immediate parent
16651
16576
  var ancestors = [];
16652
16577
  var searchId = firstNode.parentId;
16653
16578
  while (searchId) {
16654
16579
  var ancestorIdx = this.virtualNodeMap.get(searchId);
16655
16580
  if (!isNullOrUndefined(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
16656
16581
  var ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
16657
- ancestors.unshift(ancestor); // prepend so root comes first
16582
+ ancestors.unshift(ancestor);
16658
16583
  searchId = ancestor.parentId;
16659
16584
  }
16660
16585
  else {
16661
16586
  break;
16662
16587
  }
16663
16588
  }
16664
- // Inject each ancestor as a real <li> into the root <ul>
16665
- // and register it in renderedParentMap for child-nesting below.
16666
16589
  for (var a = 0; a < ancestors.length; a++) {
16667
16590
  var ancestor = ancestors[parseInt(a.toString(), 10)];
16668
16591
  var ancestorLi = this.createVirtualListItem(ancestor, false);
16669
- // Find the correct container: root or a previously injected ancestor's <ul>
16670
16592
  var targetUl = ul;
16671
16593
  if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
16672
16594
  var pe = renderedParentMap.get(ancestor.parentId);
@@ -16685,26 +16607,20 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16685
16607
  }
16686
16608
  }
16687
16609
  }
16688
- // ── Scroll-space padding (adjusted for injected ancestors) ────────────────
16689
16610
  var total = this.virtualFlatNodes.length;
16690
16611
  var start = this.virtualStartIndex;
16691
16612
  var end = this.virtualEndIndex;
16692
16613
  var rawPaddingTop = start * itemH;
16693
- // Subtract the height of injected ancestors so the scroll position is
16694
- // exact: those ancestor rows are now real DOM elements, not hidden space.
16695
16614
  var adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
16696
16615
  ul.style.paddingTop = adjustedPaddingTop + 'px';
16697
16616
  ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
16698
- // ── Render window items (nested via renderedParentMap) ────────────────────
16699
16617
  var liListBuf = [];
16700
16618
  for (var i = start; i < end; i++) {
16701
16619
  var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
16702
16620
  var li = this.createVirtualListItem(node, i === start);
16703
- // Find the correct parent container
16704
16621
  var targetUl = ul;
16705
16622
  if (node.parentId && renderedParentMap.has(node.parentId)) {
16706
16623
  var parentEntry = renderedParentMap.get(node.parentId);
16707
- // Lazily create the child <ul> the first time a child needs it
16708
16624
  if (!parentEntry.childUl) {
16709
16625
  parentEntry.childUl = this.createElement('ul', {
16710
16626
  className: PARENTITEM + ' e-ul',
@@ -16716,30 +16632,11 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16716
16632
  }
16717
16633
  targetUl.appendChild(li);
16718
16634
  liListBuf.push(li);
16719
- // Register this node so its children in the same window can nest under it
16720
- if (node.isExpanded && node.hasChildren) {
16635
+ if (node.expanded && node.hasChildren) {
16721
16636
  renderedParentMap.set(node.id, { li: li, childUl: null });
16722
16637
  }
16723
16638
  }
16724
- // ── Restore focus / tabindex ──────────────────────────────────────────────
16725
- // In virtual mode only the li that carries the 'e-active' class should be
16726
- // focusable (tabindex='0'). All other rendered li elements must be set
16727
- // to tabindex='-1' so the tab order remains unambiguous as the window
16728
- // changes during scrolling.
16729
16639
  var focusRestored = false;
16730
- // 1. Prefer the currently active node if it is in the rendered window.
16731
- for (var k = 0; k < liListBuf.length; k++) {
16732
- var li = liListBuf[parseInt(k.toString(), 10)];
16733
- if (li.classList.contains(ACTIVE)) {
16734
- li.setAttribute('tabindex', '0');
16735
- // ── Fix: ensure FOCUS class set on active node too ──
16736
- // This ensures focusIn() guard condition is met: tabindex='0' && FOCUS class
16737
- addClass([li], FOCUS);
16738
- focusRestored = true;
16739
- break;
16740
- }
16741
- }
16742
- // 2. Otherwise fall back to the previously focused node (if it is in window).
16743
16640
  if (!focusRestored && focusedUid) {
16744
16641
  for (var k = 0; k < liListBuf.length; k++) {
16745
16642
  var li = liListBuf[parseInt(k.toString(), 10)];
@@ -16747,22 +16644,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16747
16644
  li.setAttribute('tabindex', '0');
16748
16645
  addClass([li], FOCUS);
16749
16646
  addClass([li], FOCUSED$1);
16750
- // ── Fix: restore actual browser focus so keyboard events keep working ──
16751
- // After renderVirtualItems() replaces all <li> elements the old element
16752
- // is gone from the DOM and document.activeElement falls back to
16753
- // document.body. Setting tabindex + classes is not enough — the new
16754
- // element also needs a programmatic .focus() call so subsequent keyboard
16755
- // events (Arrow keys, Home, End, etc.) are delivered to the TreeView.
16756
- // Re-attach the blur→focusOut handler so virtualFocusedId is cleared
16757
- // correctly when the user tabs away.
16758
- // ── Use preventScroll:true ────────────────────────────────────────────
16759
- // Without preventScroll the browser scrolls the container to bring this
16760
- // node into view during the re-render, changing scrollTop AFTER the
16761
- // correct paddingTop has been applied. This is the root cause of the
16762
- // KBN expand position-jump bug: the render is correct but the browser
16763
- // silently scrolls the viewport, making the parent node appear to jump.
16764
- // ensureVirtualNodeVisible() handles any legitimate scroll-into-view
16765
- // needs before renderVirtualItems() is called, so scroll here is wrong.
16766
16647
  EventHandler.add(li, 'blur', this.focusOut, this);
16767
16648
  li.focus({ preventScroll: true });
16768
16649
  focusRestored = true;
@@ -16770,53 +16651,36 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16770
16651
  }
16771
16652
  }
16772
16653
  }
16773
- // 3. Finally default to the first item in the window.
16774
16654
  if (!focusRestored && liListBuf.length > 0) {
16775
16655
  var firstLi = liListBuf[0];
16776
16656
  firstLi.setAttribute('tabindex', '0');
16777
16657
  }
16778
- // ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
16779
- // This prevents stale tabindex="0" on multiple nodes which breaks Tab order
16780
16658
  for (var k = 0; k < liListBuf.length; k++) {
16781
16659
  var li = liListBuf[parseInt(k.toString(), 10)];
16782
16660
  if (li.getAttribute('tabindex') !== '0') {
16783
16661
  li.setAttribute('tabindex', '-1');
16784
16662
  }
16785
16663
  }
16786
- // Keep liList in sync so keyboard-navigation helpers stay functional.
16787
16664
  this.liList = liListBuf;
16788
16665
  };
16789
- /**
16790
- * Creates a single `<li>` element for the virtual window representing `node`.
16791
- *
16792
- * @param {VirtualNodeData} node - The node data.
16793
- * @param {boolean} _isFirst - True if this is the first rendered item (unused here but kept for potential override).
16794
- * @returns {HTMLElement} The constructed `<li>` element.
16795
- * @private
16796
- */
16797
16666
  TreeView.prototype.createVirtualListItem = function (node, _isFirst) {
16798
16667
  var li = this.createElement('li', { className: LISTITEM });
16799
- // Level class for CSS indentation
16800
16668
  li.classList.add('e-level-' + node.level);
16801
16669
  li.setAttribute('data-uid', node.id);
16802
16670
  li.setAttribute('aria-level', node.level.toString());
16803
16671
  li.setAttribute('role', 'treeitem');
16804
16672
  li.setAttribute('tabindex', '-1');
16805
- // Full-row overlay (required for fullRowSelect mode)
16806
16673
  if (this.fullRowSelect) {
16807
16674
  var fullRow = this.createElement('div', { className: FULLROW });
16808
16675
  li.appendChild(fullRow);
16809
16676
  }
16810
- // Text/icon wrapper
16811
16677
  var textWrap = this.createElement('div', { className: TEXTWRAP });
16812
- // Expand/collapse icon
16813
16678
  if (node.hasChildren) {
16814
16679
  var iconSpan = this.createElement('div', {
16815
- className: ICON + ' ' + (node.isExpanded ? COLLAPSIBLE : EXPANDABLE)
16680
+ className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
16816
16681
  });
16817
16682
  textWrap.appendChild(iconSpan);
16818
16683
  }
16819
- // Checkbox
16820
16684
  if (this.showCheckBox) {
16821
16685
  var checkWrap = createCheckBox(this.createElement, false, {
16822
16686
  checked: node.isChecked === true,
@@ -16830,29 +16694,32 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16830
16694
  }
16831
16695
  textWrap.appendChild(checkWrap);
16832
16696
  }
16833
- // Icon
16834
16697
  if (node.iconCss) {
16835
16698
  var iconEl = this.createElement('span', {
16836
16699
  className: ICON + ' ' + node.iconCss
16837
16700
  });
16838
16701
  textWrap.appendChild(iconEl);
16839
16702
  }
16840
- // Image
16841
16703
  if (node.imageUrl) {
16842
16704
  var img = document.createElement('img');
16843
16705
  img.src = node.imageUrl;
16844
16706
  img.classList.add(IMAGE);
16845
16707
  textWrap.appendChild(img);
16846
16708
  }
16847
- // Text anchor
16848
16709
  var anchor = this.createElement('span', {
16849
16710
  className: 'e-list-text',
16850
16711
  innerHTML: node.text
16851
16712
  });
16852
16713
  textWrap.appendChild(anchor);
16853
16714
  li.appendChild(textWrap);
16854
- // State classes
16855
- if (node.isExpanded) {
16715
+ if (!isNullOrUndefined(this.nodeTemplateFn)) {
16716
+ var rawData = this.findNodeInTreeData(node.id);
16717
+ if (!isNullOrUndefined(rawData)) {
16718
+ anchor.innerHTML = '';
16719
+ this.renderNodeTemplate(rawData, anchor, node.id);
16720
+ }
16721
+ }
16722
+ if (node.expanded) {
16856
16723
  li.classList.add(EXPANDED);
16857
16724
  li.setAttribute('aria-expanded', 'true');
16858
16725
  }
@@ -16860,91 +16727,50 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16860
16727
  li.classList.add(NODECOLLAPSED);
16861
16728
  li.setAttribute('aria-expanded', 'false');
16862
16729
  }
16863
- if (node.isSelected) {
16730
+ if (node.selected) {
16864
16731
  li.classList.add(ACTIVE);
16865
16732
  li.setAttribute('aria-selected', 'true');
16866
16733
  }
16867
- if (node.isDisabled) {
16868
- li.classList.add(DISABLED$1);
16869
- li.setAttribute('aria-disabled', 'true');
16870
- }
16871
16734
  if (node.hasChildren) {
16872
16735
  li.classList.add(CHILD);
16873
16736
  }
16874
- // Wire the focus event so `focusIn()` fires when the user Tabs into this
16875
- // item. The non-virtualized path registers this in `beforeNodeCreate()`,
16876
- // but virtual items never go through that callback, so we must register
16877
- // it here to keep focus styling (e.g. `e-focused`) consistent between
16878
- // virtual and non-virtual modes. `unWireEvents()` already removes this
16879
- // handler during teardown by iterating over the rendered `<li>`s.
16880
16737
  EventHandler.add(li, 'focus', this.focusIn, this);
16881
16738
  return li;
16882
16739
  };
16883
- /**
16884
- * Initialises the virtual-scrolling layout:
16885
- * 1. Adds the `e-virtualization` CSS class and applies a fixed `height` + `overflow-y`.
16886
- * 2. Flattens all visible nodes into `virtualFlatNodes`.
16887
- * 3. Measures item height and viewport height.
16888
- * 4. Computes the initial window and renders it.
16889
- * 5. Wires the scroll event handler.
16890
- *
16891
- * @returns {void}
16892
- * @private
16893
- */
16894
16740
  TreeView.prototype.initVirtualization = function () {
16895
16741
  addClass([this.element], VIRTUALIZATION);
16896
- // Apply container height
16897
16742
  var px = this.parseHeightToPixels();
16898
16743
  if (px > 0) {
16899
16744
  this.element.style.height = px + 'px';
16900
16745
  this.element.style.overflowY = 'auto';
16901
16746
  }
16902
- // Ensure the inner UL is ready
16903
16747
  if (!this.ulElement) {
16904
16748
  this.ulElement = select('.' + PARENTITEM, this.element);
16905
16749
  }
16906
16750
  if (!this.ulElement) {
16907
16751
  return;
16908
16752
  }
16909
- // Make the UL position-relative for the absolute spacers
16910
16753
  this.ulElement.style.position = 'relative';
16911
- // Rebuild flat list
16912
16754
  this.virtualFlatNodes = this.flattenVisibleNodes();
16913
16755
  this.rebuildVirtualNodeMap();
16914
- // Measure dimensions
16915
16756
  this.virtualViewportHeight = px > 0 ? px : this.element.offsetHeight;
16916
- // Render a single probe item to measure the real item height before the full render
16917
16757
  if (this.virtualFlatNodes.length > 0) {
16918
16758
  var probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
16919
16759
  this.ulElement.appendChild(probe);
16920
16760
  this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
16921
16761
  this.ulElement.removeChild(probe);
16922
16762
  }
16923
- // Compute window and render all items in the initial viewport
16924
16763
  var win = this.getVirtualWindow();
16925
16764
  this.virtualStartIndex = win.startIndex;
16926
16765
  this.virtualEndIndex = win.endIndex;
16927
16766
  this.renderVirtualItems();
16928
- // Wire scroll
16929
16767
  this.wireVirtualScrollEvents();
16930
16768
  };
16931
- /**
16932
- * Wires the scroll event listener on the TreeView element.
16933
- *
16934
- * @returns {void}
16935
- * @private
16936
- */
16937
16769
  TreeView.prototype.wireVirtualScrollEvents = function () {
16938
16770
  this.unwireVirtualScrollEvents();
16939
16771
  this.virtualScrollBound = this.onVirtualScroll.bind(this);
16940
16772
  this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
16941
16773
  };
16942
- /**
16943
- * Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
16944
- *
16945
- * @returns {void}
16946
- * @private
16947
- */
16948
16774
  TreeView.prototype.unwireVirtualScrollEvents = function () {
16949
16775
  if (this.virtualScrollBound) {
16950
16776
  this.element.removeEventListener('scroll', this.virtualScrollBound);
@@ -16955,29 +16781,13 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16955
16781
  this.virtualScrollRAF = null;
16956
16782
  }
16957
16783
  };
16958
- /**
16959
- * Scroll handler for the virtual TreeView. Throttled via `requestAnimationFrame`.
16960
- * Shows the loading mask during fast scrolls, re-renders the window, then hides the mask.
16961
- *
16962
- * @returns {void}
16963
- * @private
16964
- */
16965
16784
  TreeView.prototype.onVirtualScroll = function () {
16966
16785
  var _this = this;
16967
16786
  if (this.virtualScrollRAF !== null) {
16968
16787
  cancelAnimationFrame(this.virtualScrollRAF);
16969
16788
  }
16970
- // ── Tab-induced focus guard ────────────────────────────────────────────────
16971
- // When the user Tabs into a `<li tabindex="0">` that is in the middle of
16972
- // the treeview, the browser auto-scrolls the container so the focused
16973
- // element becomes visible. That fires a `scroll` event here, but the
16974
- // virtual window already contains the focused element — re-rendering
16975
- // would destroy the focused `<li>` and break focus. Detect this case
16976
- // via the flag set in `keyDownHandler` and skip the re-render entirely.
16977
16789
  if (this.virtualSkipNextScrollRender) {
16978
16790
  this.virtualSkipNextScrollRender = false;
16979
- // Update prev scrollTop so a subsequent user-driven scroll still
16980
- // calculates the correct delta.
16981
16791
  this.virtualPrevScrollTop = this.element.scrollTop;
16982
16792
  return;
16983
16793
  }
@@ -16994,30 +16804,16 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16994
16804
  _this.virtualStartIndex = win.startIndex;
16995
16805
  _this.virtualEndIndex = win.endIndex;
16996
16806
  _this.renderVirtualItems();
16997
- // Hide the skeleton mask after a short delay so it stays visible long enough
16998
- // for the user to perceive the loading indicator even on short scrolls.
16999
16807
  setTimeout(function () {
17000
16808
  _this.hideVirtualMask();
17001
- // Keep the (now hidden) mask's coords fresh for the next show.
17002
16809
  _this.updateMaskPosition();
17003
16810
  }, 200);
17004
16811
  });
17005
16812
  };
17006
- /**
17007
- * Creates the skeleton mask overlay used to indicate loading during fast scrolls.
17008
- * Skeleton instances are stored in `skeletonInstances` for later disposal.
17009
- *
17010
- * @returns {void}
17011
- * @private
17012
- */
17013
16813
  TreeView.prototype.createVirtualMask = function () {
17014
16814
  if (this.virtualMaskElement) {
17015
16815
  return;
17016
16816
  }
17017
- // Use position:fixed so the mask is anchored to the visible viewport (not to the
17018
- // scrolling container). Width/height/left/top are set/refreshed by
17019
- // updateMaskPosition() so the mask always covers the TreeView's visible area
17020
- // regardless of how the page itself is scrolled.
17021
16817
  var rect = this.element.getBoundingClientRect();
17022
16818
  var scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
17023
16819
  var maskParent = this.createElement('div', {
@@ -17030,20 +16826,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17030
16826
  className: VIRTUAL_MASK_ITEM,
17031
16827
  styles: "display:flex;align-items:center;height:" + this.virtualItemHeight + "px;padding:0 12px;gap:8px;"
17032
16828
  });
17033
- // Skeleton circle for icon placeholder
17034
- // const iconHolder: HTMLElement = this.createElement('span', {
17035
- // styles: `width:20px;height:20px;`
17036
- // });
17037
- // maskItem.appendChild(iconHolder);
17038
- // const iconSkel: Skeleton = new Skeleton({
17039
- // shape: 'Circle',
17040
- // width: '20px',
17041
- // height: '20px',
17042
- // cssClass: 'e-virtual-skel-icon'
17043
- // });
17044
- // iconSkel.appendTo(iconHolder);
17045
- // this.skeletonInstances.push(iconSkel);
17046
- // Skeleton text bar
17047
16829
  var textHolder = this.createElement('span');
17048
16830
  textHolder.style.flex = '1';
17049
16831
  textHolder.style.height = '14px';
@@ -17061,14 +16843,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17061
16843
  this.virtualMaskElement = maskParent;
17062
16844
  this.element.appendChild(maskParent);
17063
16845
  };
17064
- /**
17065
- * Refreshes the mask position so it always overlays the TreeView's viewport.
17066
- * Called on every show / scroll, since `position:fixed` is relative to the
17067
- * page viewport, not to the TreeView scroll container.
17068
- *
17069
- * @returns {void}
17070
- * @private
17071
- */
17072
16846
  TreeView.prototype.updateMaskPosition = function () {
17073
16847
  if (!this.virtualMaskElement) {
17074
16848
  return;
@@ -17080,12 +16854,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17080
16854
  this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
17081
16855
  this.virtualMaskElement.style.height = rect.height + 'px';
17082
16856
  };
17083
- /**
17084
- * Destroys all skeleton instances and removes the mask overlay from the DOM.
17085
- *
17086
- * @returns {void}
17087
- * @private
17088
- */
17089
16857
  TreeView.prototype.destroyVirtualMask = function () {
17090
16858
  for (var i = this.skeletonInstances.length - 1; i >= 0; i--) {
17091
16859
  this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
@@ -17098,52 +16866,26 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17098
16866
  this.virtualMaskElement = null;
17099
16867
  }
17100
16868
  };
17101
- /**
17102
- * Positions and shows the skeleton mask at the current `scrollTop` position.
17103
- *
17104
- * @param {number} _scrollTop - Current scrollTop of the container.
17105
- * @returns {void}
17106
- * @private
17107
- */
17108
16869
  TreeView.prototype.showVirtualMask = function (_scrollTop) {
17109
16870
  if (!this.virtualMaskElement) {
17110
16871
  this.createVirtualMask();
17111
16872
  }
17112
16873
  if (this.virtualMaskElement) {
17113
- // Refresh the fixed-position coords in case the TreeView was moved/resized
17114
- // since the last time the mask was shown.
17115
16874
  this.updateMaskPosition();
17116
16875
  this.virtualMaskElement.style.display = 'block';
17117
- // Visually hide the real list while the skeleton is shown
17118
16876
  if (this.ulElement) {
17119
16877
  this.ulElement.style.visibility = 'hidden';
17120
16878
  }
17121
16879
  }
17122
16880
  };
17123
- /**
17124
- * Hides the skeleton mask overlay after the virtual window has been rendered.
17125
- *
17126
- * @returns {void}
17127
- * @private
17128
- */
17129
16881
  TreeView.prototype.hideVirtualMask = function () {
17130
16882
  if (this.virtualMaskElement) {
17131
16883
  this.virtualMaskElement.style.display = 'none';
17132
- // Restore real list visibility
17133
16884
  if (this.ulElement) {
17134
16885
  this.ulElement.style.visibility = '';
17135
16886
  }
17136
16887
  }
17137
16888
  };
17138
- /**
17139
- * Ensures the node identified by `id` is scrolled into view inside the virtual container.
17140
- * If the node falls outside the rendered window, the container's `scrollTop` is adjusted so
17141
- * that the node becomes visible, the window is re-computed and `renderVirtualItems` is called.
17142
- *
17143
- * @param {string} id - The `data-uid` of the target node.
17144
- * @returns {void}
17145
- * @private
17146
- */
17147
16889
  TreeView.prototype.ensureVirtualNodeVisible = function (id) {
17148
16890
  var idx = this.virtualNodeMap.get(id);
17149
16891
  if (isNullOrUndefined(idx)) {
@@ -17162,20 +16904,53 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17162
16904
  this.element.scrollTop = nodeBottom - vpH;
17163
16905
  }
17164
16906
  else {
17165
- return; // already visible, no re-render needed
16907
+ return;
17166
16908
  }
17167
- // Re-render the new window synchronously so the <li> is in the DOM
17168
16909
  var win = this.getVirtualWindow();
17169
16910
  this.virtualStartIndex = win.startIndex;
17170
16911
  this.virtualEndIndex = win.endIndex;
17171
16912
  this.renderVirtualItems();
17172
16913
  };
16914
+ TreeView.prototype.findNodeInTreeData = function (id) {
16915
+ if (!this.treeData) {
16916
+ return null;
16917
+ }
16918
+ var idFieldName = this.fields.id.toString();
16919
+ var childFieldName = this.fields.child.toString();
16920
+ var findRecursive = function (nodes) {
16921
+ if (!nodes || nodes.length === 0) {
16922
+ return null;
16923
+ }
16924
+ for (var i = 0; i < nodes.length; i++) {
16925
+ var node = nodes[i];
16926
+ if (node[idFieldName] && node[idFieldName].toString() === id) {
16927
+ return node;
16928
+ }
16929
+ var children = getValue(childFieldName, node);
16930
+ if (children && Array.isArray(children)) {
16931
+ var found = findRecursive(children);
16932
+ if (found) {
16933
+ return found;
16934
+ }
16935
+ }
16936
+ }
16937
+ return null;
16938
+ };
16939
+ return findRecursive(this.treeData);
16940
+ };
16941
+ TreeView.prototype.getNodeLevel = function (id) {
16942
+ if (!this.virtualFlatNodes) {
16943
+ return 1;
16944
+ }
16945
+ for (var i = 0; i < this.virtualFlatNodes.length; i++) {
16946
+ if (this.virtualFlatNodes[parseInt(i.toString(), 10)].id === id) {
16947
+ return this.virtualFlatNodes[parseInt(i.toString(), 10)].level;
16948
+ }
16949
+ }
16950
+ return 1;
16951
+ };
17173
16952
  TreeView.prototype.handleVirtualExpand = function (id, isKeyboardNav) {
17174
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
17175
- // A scroll RAF queued before the expand click would run asynchronously after
17176
- // this synchronous expand completes, re-rendering the window from a stale
17177
- // scrollTop and moving the parent node to the wrong position. Cancel it now
17178
- // so the expand render is authoritative.
16953
+ var _this = this;
17179
16954
  if (this.virtualScrollRAF !== null) {
17180
16955
  cancelAnimationFrame(this.virtualScrollRAF);
17181
16956
  this.virtualScrollRAF = null;
@@ -17183,64 +16958,85 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17183
16958
  if (this.expandedNodes.indexOf(id) === -1) {
17184
16959
  this.expandedNodes.push(id);
17185
16960
  }
17186
- // Also update virtualExpandedNodesSet to keep it in sync
17187
16961
  if (this.virtualExpandedNodesSet) {
17188
16962
  this.virtualExpandedNodesSet.add(id);
17189
16963
  }
17190
- if (this.rootData) {
17191
- var acutalRootData = this.rootData.find(function (item) { return item.id === parseInt(id, 10); });
17192
- acutalRootData.isExpanded = true;
17193
- }
17194
- // ── Fix: Pin focused ID to the expanding node for keyboard navigation ──
17195
- // When keyboard-triggered, the expanded node IS definitively the focused node
17196
- // (`id` comes from currLi.getAttribute('data-uid') in expandNode, where currLi
17197
- // is getFocusedNode()). Set unconditionally — the old conditional guard
17198
- // `!this.virtualFocusedId` could leave a stale or null value if a previous
17199
- // RAF render (from arrow-key scroll) hadn't yet completed, causing branch 3
17200
- // (first item) to fire in renderVirtualItems() instead of branch 2.
17201
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
17202
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
17203
- // mouse-based expand/collapse should never add the e-focused class.
16964
+ if (this.treeData) {
16965
+ var acutalRootData = this.findNodeInTreeData(id);
16966
+ if (acutalRootData && this.fields && this.fields.expanded) {
16967
+ setValue(this.fields.expanded, true, acutalRootData);
16968
+ }
16969
+ }
17204
16970
  if (isKeyboardNav) {
17205
16971
  this.virtualFocusedId = id;
17206
16972
  }
17207
16973
  else {
17208
16974
  this.virtualFocusedId = null;
17209
16975
  }
16976
+ if (this.fields.dataSource instanceof DataManager && !this.isOffline) {
16977
+ var parentNode = this.findNodeInTreeData(id);
16978
+ if (parentNode && !isNullOrUndefined(parentNode)) {
16979
+ var childFieldName = this.fields.child.toString();
16980
+ var childrenObj = getValue(childFieldName, parentNode);
16981
+ var childrenNotLoaded = isNullOrUndefined(childrenObj) || !Array.isArray(childrenObj) ||
16982
+ childrenObj.length === 0;
16983
+ if (childrenNotLoaded) {
16984
+ this.loadVirtualChildData(id, parentNode, function () {
16985
+ _this.completeVirtualExpand(id, isKeyboardNav);
16986
+ });
16987
+ return;
16988
+ }
16989
+ }
16990
+ }
16991
+ this.completeVirtualExpand(id, isKeyboardNav);
16992
+ };
16993
+ TreeView.prototype.loadVirtualChildData = function (parentId, parentNode, callback) {
16994
+ var _this = this;
16995
+ var level = this.getNodeLevel(parentId);
16996
+ var mapper = this.getChildFields(this.fields, level + 1, 1);
16997
+ if (isNullOrUndefined(mapper) || isNullOrUndefined(mapper.dataSource)) {
16998
+ if (callback) {
16999
+ callback();
17000
+ }
17001
+ return;
17002
+ }
17003
+ mapper.dataSource.executeQuery(this.getQuery(mapper, parentId)).then(function (e) {
17004
+ var childItems = e.result;
17005
+ if (!isNullOrUndefined(childItems) && childItems.length > 0) {
17006
+ var childFieldName = _this.fields.child.toString();
17007
+ setValue(childFieldName, childItems, parentNode);
17008
+ if (_this.dataType === 2) {
17009
+ var nodeInTree = _this.findNodeInTreeData(parentId);
17010
+ if (nodeInTree) {
17011
+ setValue(childFieldName, childItems, nodeInTree);
17012
+ }
17013
+ }
17014
+ }
17015
+ if (callback) {
17016
+ callback();
17017
+ }
17018
+ }).catch(function (e) {
17019
+ _this.trigger('actionFailure', { error: e });
17020
+ if (callback) {
17021
+ callback();
17022
+ }
17023
+ });
17024
+ };
17025
+ TreeView.prototype.completeVirtualExpand = function (id, isKeyboardNav) {
17210
17026
  this.virtualFlatNodes = this.flattenVisibleNodes();
17211
17027
  this.rebuildVirtualNodeMap();
17212
- // ── Snapshot scrollTop before computing the window ────────────────────────
17213
- // Read scrollTop once here and pass it into getVirtualWindow so that any
17214
- // browser scroll-anchoring adjustment that happens when the padding values
17215
- // change cannot affect which window we render.
17216
17028
  var scrollTopSnapshot = this.element.scrollTop;
17217
17029
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
17218
17030
  this.virtualStartIndex = win.startIndex;
17219
17031
  this.virtualEndIndex = win.endIndex;
17220
- // ── Block ALL scroll-triggered re-renders during this expand ─────────────
17221
- // renderVirtualItems() calls li.focus() in branch 2 (keyboard focus restore).
17222
- // The post-render focus() call below can also trigger browser scroll. Both
17223
- // can fire the 'scroll' event and cause onVirtualScroll() to queue a RAF.
17224
- // We set the skip flag ONCE here; it is consumed by the first scroll event.
17225
- // To cover the second potential scroll (from the focus() call below) we use
17226
- // preventScroll:true on that call so it never moves the container at all.
17227
17032
  this.virtualSkipNextScrollRender = true;
17228
17033
  this.renderVirtualItems();
17229
- // renderVirtualItems() replaces all <li> elements so the old icon element that
17230
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
17231
- // so the node can be collapsed/expanded again without being blocked.
17232
17034
  var newLi = this.getElement(id);
17233
17035
  if (newLi) {
17234
17036
  var icon = select('.' + ICON, newLi);
17235
17037
  if (icon) {
17236
17038
  removeClass([icon], PROCESS);
17237
17039
  }
17238
- // ── Use preventScroll:true ────────────────────────────────────────────
17239
- // Without preventScroll the browser scrolls the container to bring this
17240
- // node into view, which fires a second 'scroll' event. The first scroll
17241
- // was already consumed by virtualSkipNextScrollRender (set above), so the
17242
- // second scroll would queue a new RAF that re-renders from the
17243
- // focus-adjusted scrollTop — moving the parent node to the wrong position.
17244
17040
  newLi.focus({ preventScroll: true });
17245
17041
  }
17246
17042
  if (this.isLoaded) {
@@ -17253,9 +17049,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17253
17049
  }
17254
17050
  };
17255
17051
  TreeView.prototype.handleVirtualCollapse = function (id, isKeyboardNav) {
17256
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
17257
- // Mirrors handleVirtualExpand: a queued RAF from a previous scroll event
17258
- // must not override the collapse render with a stale window.
17259
17052
  if (this.virtualScrollRAF !== null) {
17260
17053
  cancelAnimationFrame(this.virtualScrollRAF);
17261
17054
  this.virtualScrollRAF = null;
@@ -17264,21 +17057,15 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17264
17057
  if (idx !== -1) {
17265
17058
  this.expandedNodes.splice(idx, 1);
17266
17059
  }
17267
- // Also update virtualExpandedNodesSet to keep it in sync
17268
17060
  if (this.virtualExpandedNodesSet) {
17269
17061
  this.virtualExpandedNodesSet.delete(id);
17270
17062
  }
17271
- if (this.rootData) {
17272
- var acutalRootData = this.rootData.find(function (item) { return item.id === parseInt(id, 10); });
17273
- acutalRootData.isExpanded = false;
17274
- }
17275
- // ── Fix: Pin focused ID to the collapsing node for keyboard navigation ──
17276
- // Same reasoning as handleVirtualExpand: when keyboard-triggered, `id` is
17277
- // definitively the focused node. Set unconditionally to avoid the stale/null
17278
- // state that could cause the RAF re-render to fall through to branch 3.
17279
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
17280
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
17281
- // mouse-based expand/collapse should never add the e-focused class.
17063
+ if (this.treeData) {
17064
+ var acutalRootData = this.findNodeInTreeData(id);
17065
+ if (acutalRootData && this.fields && this.fields.expanded) {
17066
+ setValue(this.fields.expanded, false, acutalRootData);
17067
+ }
17068
+ }
17282
17069
  if (isKeyboardNav) {
17283
17070
  this.virtualFocusedId = id;
17284
17071
  }
@@ -17287,28 +17074,18 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17287
17074
  }
17288
17075
  this.virtualFlatNodes = this.flattenVisibleNodes();
17289
17076
  this.rebuildVirtualNodeMap();
17290
- // ── Snapshot scrollTop before computing the window ────────────────────────
17291
- // Same rationale as handleVirtualExpand: capture scrollTop before any focus()
17292
- // call or padding change can cause the browser to auto-scroll.
17293
17077
  var scrollTopSnapshot = this.element.scrollTop;
17294
17078
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
17295
17079
  this.virtualStartIndex = win.startIndex;
17296
17080
  this.virtualEndIndex = win.endIndex;
17297
- // ── Block ALL scroll-triggered re-renders during this collapse ────────────
17298
- // Same as handleVirtualExpand: use preventScroll:true on the focus() call
17299
- // below so the second potential scroll event is never fired.
17300
17081
  this.virtualSkipNextScrollRender = true;
17301
17082
  this.renderVirtualItems();
17302
- // renderVirtualItems() replaces all <li> elements so the old icon element that
17303
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
17304
- // so the node can be expanded/collapsed again without being blocked.
17305
17083
  var newLi = this.getElement(id);
17306
17084
  if (newLi) {
17307
17085
  var icon = select('.' + ICON, newLi);
17308
17086
  if (icon) {
17309
17087
  removeClass([icon], PROCESS);
17310
17088
  }
17311
- // ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
17312
17089
  newLi.focus({ preventScroll: true });
17313
17090
  }
17314
17091
  if (this.isLoaded) {
@@ -17320,36 +17097,18 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17320
17097
  }
17321
17098
  }
17322
17099
  };
17323
- /**
17324
- * Updates the selected state of a node in `virtualFlatNodes` and re-renders the window.
17325
- *
17326
- * @param {string} id - The `data-uid` of the target node.
17327
- * @param {boolean} isSelected - Whether the node is now selected.
17328
- * @returns {void}
17329
- * @private
17330
- */
17331
- TreeView.prototype.updateVirtualNodeSelection = function (id, isSelected) {
17100
+ TreeView.prototype.updateVirtualNodeSelection = function (id, selected) {
17332
17101
  var idx = this.virtualNodeMap.get(id);
17333
17102
  if (!isNullOrUndefined(idx)) {
17334
- this.virtualFlatNodes[parseInt(idx.toString(), 10)].isSelected = isSelected;
17103
+ this.virtualFlatNodes[parseInt(idx.toString(), 10)].selected = selected;
17335
17104
  this.renderVirtualItems();
17336
17105
  }
17337
17106
  };
17338
- /**
17339
- * Updates the checkbox state of a node in `virtualFlatNodes` and re-renders the window.
17340
- *
17341
- * @param {string} id - The `data-uid` of the target node.
17342
- * @param {'check' | 'uncheck' | 'indeterminate'} state - The new check state.
17343
- * @returns {void}
17344
- * @private
17345
- */
17346
17107
  TreeView.prototype.updateVirtualNodeCheckState = function (id, state) {
17347
17108
  var idx = this.virtualNodeMap.get(id);
17348
17109
  if (!isNullOrUndefined(idx)) {
17349
17110
  this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
17350
17111
  state === 'check' ? true : state === 'indeterminate' ? 'indeterminate' : false;
17351
- // Keep the persistent indeterminate set in sync so flattenVisibleNodes()
17352
- // correctly restores state after any full list rebuild (e.g. collapse).
17353
17112
  if (state === 'indeterminate') {
17354
17113
  this.virtualIndeterminateNodesSet.add(id);
17355
17114
  }
@@ -17359,9 +17118,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17359
17118
  this.renderVirtualItems();
17360
17119
  }
17361
17120
  };
17362
- // ═══════════════════════════════════════════════════════════════════════════════
17363
- // END VIRTUALIZATION
17364
- // ═══════════════════════════════════════════════════════════════════════════════
17365
17121
  /**
17366
17122
  * Called internally if any of the property value changed.
17367
17123
  *
@@ -17506,7 +17262,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17506
17262
  }
17507
17263
  }
17508
17264
  else {
17509
- // Tear down virtualization and do a full re-render
17510
17265
  this.unwireVirtualScrollEvents();
17511
17266
  this.destroyVirtualMask();
17512
17267
  removeClass([this.element], VIRTUALIZATION);
@@ -17517,7 +17272,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17517
17272
  break;
17518
17273
  case 'height':
17519
17274
  if (this.enableVirtualization) {
17520
- // Re-initialise so the new height takes effect
17521
17275
  this.unwireVirtualScrollEvents();
17522
17276
  this.initVirtualization();
17523
17277
  }
@@ -17535,7 +17289,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17535
17289
  * @returns {void}
17536
17290
  */
17537
17291
  TreeView.prototype.destroy = function () {
17538
- // Tear down virtualization before standard cleanup
17539
17292
  if (this.enableVirtualization) {
17540
17293
  this.unwireVirtualScrollEvents();
17541
17294
  this.destroyVirtualMask();
@@ -17626,10 +17379,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17626
17379
  if (isNullOrUndefined(fNode)) {
17627
17380
  this.setNodeFocusable();
17628
17381
  }
17629
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
17630
- // the window so the newly added nodes appear in the correct hierarchical position.
17631
- // When height is not provided, the tree uses normal rendering and the DOM is
17632
- // already updated by addGivenNodes() above.
17633
17382
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
17634
17383
  this.setRootData();
17635
17384
  this.rebuildVirtualCheckCache();
@@ -17699,7 +17448,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17699
17448
  }
17700
17449
  for (var i = 0; i < nodes.length; i++) {
17701
17450
  var node = nodes[parseInt(i.toString(), 10)];
17702
- node['isExpanded'] = false;
17451
+ node['expanded'] = false;
17703
17452
  var children = getValue(_this.fields.child.toString(), node);
17704
17453
  if (children && Array.isArray(children)) {
17705
17454
  setAllCollapsed_1(children);
@@ -17799,7 +17548,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17799
17548
  var nodeId = getValue(idFieldName_1, node);
17800
17549
  if (node.hasChild) {
17801
17550
  allParentIds_1.push(nodeId);
17802
- node.isExpanded = true;
17551
+ node.expanded = true;
17803
17552
  }
17804
17553
  }
17805
17554
  };