@syncfusion/ej2-navigations 34.2.3 → 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.
@@ -11024,54 +11024,25 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
11024
11024
  _this.isDropIn = false;
11025
11025
  _this.OldCheckedData = [];
11026
11026
  _this.isHiddenItem = false;
11027
- // ── Virtualization state ──────────────────────────────────────────────────────────────
11028
- /** Flat ordered list of all currently-visible (non-collapsed) tree nodes. */
11027
+ // Virtualization state
11029
11028
  _this.virtualFlatNodes = [];
11030
- /** Maps node id → index in virtualFlatNodes for O(1) lookups. */
11031
11029
  _this.virtualNodeMap = new Map();
11032
- /** Set of expanded node IDs for O(1) lookup performance during flattening (avoids array indexOf). */
11033
11030
  _this.virtualExpandedNodesSet = new Set();
11034
- /** Set of checked node IDs for O(1) lookup performance during flattening. */
11035
11031
  _this.virtualCheckedNodesSet = new Set();
11036
- /** Set of indeterminate node IDs — persists across flattenVisibleNodes() rebuilds. */
11037
11032
  _this.virtualIndeterminateNodesSet = new Set();
11038
- /** Set of selected node IDs for O(1) lookup performance during flattening. */
11039
11033
  _this.virtualSelectedNodesSet = new Set();
11040
- /** Row height in pixels, measured from the first rendered item or set to 40 as a default. */
11041
11034
  _this.virtualItemHeight = 40;
11042
- /** Height of the scrollable container in pixels. */
11043
11035
  _this.virtualViewportHeight = 0;
11044
- /** scrollTop captured during the previous scroll handler invocation. */
11045
11036
  _this.virtualPrevScrollTop = 0;
11046
- /** Index of the first rendered row in the current window. */
11047
11037
  _this.virtualStartIndex = 0;
11048
- /** Index one past the last rendered row in the current window. */
11049
11038
  _this.virtualEndIndex = 0;
11050
- /** Extra rows rendered above and below the visible window to smooth scrolling. */
11051
11039
  _this.virtualOverscanCount = 3;
11052
- /** RequestAnimationFrame handle used for throttling scroll events. */
11053
11040
  _this.virtualScrollRAF = null;
11054
- /** The `.e-mask-parent` overlay element shown during fast scroll. */
11055
11041
  _this.virtualMaskElement = null;
11056
- /** Bound scroll handler stored for later removeEventListener. */
11057
11042
  _this.virtualScrollBound = null;
11058
- /** Skeleton component instances inside the mask overlay. */
11059
11043
  _this.skeletonInstances = [];
11060
- /** Set to true whenever the flat node list must be rebuilt on next render. */
11061
11044
  _this.virtualTreeDirty = false;
11062
- /** The data-uid of the currently keyboard-focused node in virtual mode (persists across re-renders). */
11063
11045
  _this.virtualFocusedId = null;
11064
- /**
11065
- * Set to true by the document-level `keydown` handler when the user presses
11066
- * the Tab key. The browser will auto-scroll the focused element into view
11067
- * immediately after focus, which fires a `scroll` event on the TreeView
11068
- * container. We want to skip the re-render that the virtual scroll handler
11069
- * would otherwise trigger, because the element being focused is already
11070
- * rendered (it is the single `<li tabindex="0">` of the current window) and
11071
- * re-rendering would tear down the DOM element that is currently holding
11072
- * focus. The flag is consumed (reset to `false`) inside `onVirtualScroll`
11073
- * after the auto-scroll has been handled.
11074
- */
11075
11046
  _this.virtualSkipNextScrollRender = false;
11076
11047
  return _this;
11077
11048
  }
@@ -11374,16 +11345,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
11374
11345
  }
11375
11346
  };
11376
11347
  TreeView.prototype.renderItems = function (isSorted) {
11377
- // In virtual mode (with a fixed height) create only the container <ul> —
11378
- // initVirtualization will populate the items directly, avoiding the O(n)
11379
- // ListBase.createList cost.
11380
- // updateCheckedStateFromDS / updateParentCheckState are intentionally skipped here:
11381
- // they call getCheckedNodeDetails which traverses the DOM (no DOM exists yet in
11382
- // virtual mode) and would throw on empty recursion results. The initial checked
11383
- // state is derived from the raw data inside flattenNodeRecursive instead.
11384
- //
11385
- // When enableVirtualization is true but height is NOT provided, we fall through
11386
- // to the normal (non-virtualized) rendering path so the tree is still visible.
11387
11348
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
11388
11349
  this.ulElement = this.createElement('ul', {
11389
11350
  className: PARENTITEM + ' ' + 'e-ul',
@@ -12470,7 +12431,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12470
12431
  this.checkDisabledState(id);
12471
12432
  }
12472
12433
  if (this.enableVirtualization) {
12473
- // Clear any indeterminate state — node is now fully checked
12474
12434
  this.virtualIndeterminateNodesSet.delete(id);
12475
12435
  var actualIndex = this.virtualNodeMap.get(id);
12476
12436
  if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
@@ -12491,20 +12451,15 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12491
12451
  var actualIndex = this.virtualNodeMap.get(id);
12492
12452
  if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
12493
12453
  if (isIndeterminate) {
12494
- // Parent is transitioning to indeterminate — preserve children states
12495
- // and mark this node as 'indeterminate' so scroll/collapse re-renders correctly.
12496
12454
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = 'indeterminate';
12497
- // Also record it in the persistent indeterminate set so flattenVisibleNodes()
12498
- // can restore this state after a full rebuild (e.g. on collapse).
12499
12455
  this.virtualIndeterminateNodesSet.add(id);
12500
12456
  }
12501
12457
  else {
12502
- // Node is being fully unchecked — clear indeterminate tracking too
12503
12458
  this.virtualIndeterminateNodesSet.delete(id);
12504
12459
  this.virtualCheckedNodesSet.delete(id);
12505
12460
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = false;
12506
12461
  if (this.treeData && this.fields && this.fields.isChecked) {
12507
- setValue(this.fields.isChecked, false, this.treeData[parseInt(id.toString(), 10) - 1]);
12462
+ this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
12508
12463
  }
12509
12464
  if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
12510
12465
  this.setVirtualChildCheckState(id, false);
@@ -12550,8 +12505,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12550
12505
  }
12551
12506
  }
12552
12507
  }
12553
- // Collapsed children are not present in virtualFlatNodes, so walk the raw data
12554
- // to keep this.treeData and checkedNodes in sync for hidden descendants too.
12555
12508
  var collapsedChildren = this.getChildNodes(this.treeData, parentId);
12556
12509
  if (collapsedChildren) {
12557
12510
  for (var i = 0; i < collapsedChildren.length; i++) {
@@ -12697,7 +12650,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12697
12650
  }
12698
12651
  this.isLoaded = true;
12699
12652
  this.isNodeDropped = false;
12700
- // Initialise (or re-initialise) virtual mode after data binding has settled
12701
12653
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
12702
12654
  this.initVirtualization();
12703
12655
  }
@@ -12837,9 +12789,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12837
12789
  };
12838
12790
  TreeView.prototype.expandNode = function (currLi, icon, loaded, isKeyboardNav) {
12839
12791
  var _this = this;
12840
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
12841
- // Only engage when height is also set; without a fixed height the tree falls
12842
- // back to normal rendering and the standard expand path must be used.
12843
12792
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
12844
12793
  var id = currLi ? currLi.getAttribute('data-uid') : null;
12845
12794
  if (id) {
@@ -12968,9 +12917,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
12968
12917
  };
12969
12918
  TreeView.prototype.nodeCollapseAction = function (currLi, icon, colArgs, isKeyboardNav) {
12970
12919
  var _this = this;
12971
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
12972
- // Only engage when height is also set; without a fixed height the tree falls
12973
- // back to normal rendering and the standard collapse path must be used.
12974
12920
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
12975
12921
  var id = currLi ? currLi.getAttribute('data-uid') : null;
12976
12922
  if (id) {
@@ -13441,7 +13387,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13441
13387
  li.setAttribute('tabindex', '0');
13442
13388
  EventHandler.add(li, 'blur', this.focusOut, this);
13443
13389
  this.updateIdAttr(focusedNode, li);
13444
- // Track focused id for virtual mode re-render restoration
13445
13390
  if (this.enableVirtualization) {
13446
13391
  this.virtualFocusedId = li.getAttribute('data-uid');
13447
13392
  }
@@ -13457,7 +13402,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13457
13402
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13458
13403
  this.virtualSelectedNodesSet.add(id);
13459
13404
  var actualIndex = this.virtualNodeMap.get(id);
13460
- if (actualIndex && actualIndex > -1) {
13405
+ if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
13461
13406
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
13462
13407
  }
13463
13408
  }
@@ -13478,7 +13423,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13478
13423
  var id = liEle.getAttribute('data-uid');
13479
13424
  this.virtualSelectedNodesSet.delete(id);
13480
13425
  var actualIndex = this.virtualNodeMap.get(id);
13481
- if (actualIndex && actualIndex > -1) {
13426
+ if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
13482
13427
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
13483
13428
  }
13484
13429
  }
@@ -13573,12 +13518,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13573
13518
  }
13574
13519
  };
13575
13520
  TreeView.prototype.nodeExpandAction = function (currLi, icon, expandChild, callback, isFromExpandAll, isKeyboardNav) {
13576
- // In virtual mode (with a fixed height) the flat <li> never has a child <ul>
13577
- // in the DOM, so the PARENTITEM check below would always fail and fall into
13578
- // renderChildNodes(), which corrupts the virtual DOM.
13579
- // Delegate directly to expandNode() which has the handleVirtualExpand fast-path.
13580
- // When height is not provided, fall through to normal expand logic below since
13581
- // the tree is rendered with a full nested DOM structure.
13582
13521
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13583
13522
  this.expandNode(currLi, icon, undefined, isKeyboardNav);
13584
13523
  return;
@@ -13696,9 +13635,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
13696
13635
  });
13697
13636
  };
13698
13637
  TreeView.prototype.navigateToFocus = function (isUp) {
13699
- // In virtual mode (with a fixed height) the element itself is the scroll
13700
- // container; use ensureVirtualNodeVisible. When height is not provided,
13701
- // the tree uses normal rendering, so fall through to the standard scroll logic.
13702
13638
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
13703
13639
  var focusedLi = this.getFocusedNode();
13704
13640
  if (focusedLi) {
@@ -14130,7 +14066,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14130
14066
  };
14131
14067
  TreeView.prototype.focusNextNode = function (li, isTowards) {
14132
14068
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
14133
- // Determine the id of the node one step in the requested direction
14134
14069
  var currentId = li ? li.getAttribute('data-uid') : null;
14135
14070
  var currentIdx = -1;
14136
14071
  if (!isNullOrUndefined(currentId)) {
@@ -14142,26 +14077,14 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14142
14077
  return;
14143
14078
  }
14144
14079
  var targetId = this.virtualFlatNodes[parseInt(targetIdx.toString(), 10)].id;
14145
- // ── Fix 1: set virtualFocusedId BEFORE calling ensureVirtualNodeVisible ──
14146
- // renderVirtualItems() is called synchronously inside ensureVirtualNodeVisible
14147
- // when a re-render is needed. If virtualFocusedId is still the old value at
14148
- // that point, focus restoration falls back to the first item. Setting it here
14149
- // ensures renderVirtualItems always restores focus to the correct target node.
14150
14080
  this.virtualFocusedId = targetId;
14151
- // ── Fix 2: clear focus classes from ALL nested list items, not just top-level ones ──
14152
- // With hierarchical virtual DOM, the currently focused <li> may be nested inside
14153
- // a child <ul>. Using ':scope > li' only cleans up direct children and leaves
14154
- // stale e-focused / e-node-focus classes on nested nodes across re-renders.
14155
14081
  var allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
14156
14082
  for (var k = 0; k < allLi.length; k++) {
14157
14083
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
14158
14084
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED$1);
14159
14085
  allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
14160
14086
  }
14161
- // Ensure the target <li> is in the DOM window (may trigger a re-render).
14162
- // virtualFocusedId is already set above so renderVirtualItems restores focus correctly.
14163
14087
  this.ensureVirtualNodeVisible(targetId);
14164
- // Re-query both nodes after the potential re-render so setFocus gets live DOM elements
14165
14088
  var prevNode = currentId ? this.getElement(currentId) : li;
14166
14089
  var nextNode_1 = this.getElement(targetId);
14167
14090
  if (!isNullOrUndefined(nextNode_1)) {
@@ -14262,18 +14185,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14262
14185
  if (!focusedElement) {
14263
14186
  return;
14264
14187
  }
14265
- // Virtual-mode guard: when the browser moves focus into the TreeView (e.g. via
14266
- // the Tab key) it always lands on the single `<li tabindex="0">`. In that
14267
- // case the element is already the focusable one, FOCUS is already applied
14268
- // and the only thing left to do is the keyboard focus styling. Skip the
14269
- // redundant setAttribute/EventHandler.add work so we never re-render the
14270
- // virtual window as a side effect of focusing the existing `tabindex="0"`
14271
- // item.
14272
- // ── Fix: Use document.activeElement to detect browser-focused node ──
14273
- // After renderVirtualItems() restores focus, the browser-focused element is
14274
- // the authoritative source, not getFocusedNode(). If they don't match, it means
14275
- // renderVirtualItems already correctly set up the element, so we must not
14276
- // override its setup by calling setFocus again.
14277
14188
  var actuallyFocusedElement = document.activeElement;
14278
14189
  var isAlreadyFocusedInVirtualMode = this.enableVirtualization
14279
14190
  && actuallyFocusedElement === focusedElement
@@ -14528,12 +14439,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14528
14439
  else {
14529
14440
  txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
14530
14441
  }
14531
- // ── Virtual mode: sync the flat-node snapshot so re-renders show updated text ──
14532
- // virtualFlatNodes is rebuilt from this.treeData (via flattenVisibleNodes) only on
14533
- // expand/collapse/scroll. Between those events the snapshot is the single source of
14534
- // truth used by createVirtualListItem. setValue() above updates this.editData (which
14535
- // IS the treeData object reference), but virtualFlatNodes[idx].text still holds the
14536
- // old value. Update it here so the next renderVirtualItems() call shows the new text.
14537
14442
  if (this.enableVirtualization) {
14538
14443
  var id = liEle.getAttribute('data-uid');
14539
14444
  var idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
@@ -14978,11 +14883,49 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
14978
14883
  this.fields.dataSource = this.treeData;
14979
14884
  }
14980
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
+ }
14981
14898
  };
14982
14899
  TreeView.prototype.appendNode = function (dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
14983
14900
  var checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
14984
14901
  var collapse = closest(e.target, '.' + COLLAPSIBLE);
14985
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
+ }
14986
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))) {
14987
14930
  if (dropTarget.nodeName === 'LI') {
14988
14931
  this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
@@ -15006,6 +14949,62 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15006
14949
  this.ensureIndeterminate();
15007
14950
  }
15008
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
+ };
15009
15008
  TreeView.prototype.dropAsSiblingNode = function (dragLi, dropLi, e, dragObj) {
15010
15009
  var dropUl = closest(dropLi, '.' + PARENTITEM);
15011
15010
  var dragParentUl = closest(dragLi, '.' + PARENTITEM);
@@ -15093,7 +15092,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15093
15092
  delete removedData[0][this.fields.parentID];
15094
15093
  }
15095
15094
  else {
15096
- var currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
15095
+ var currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
15097
15096
  setValue(this.fields.parentID, currPid, removedData[0]);
15098
15097
  }
15099
15098
  this.treeData.splice(pos, 0, removedData[0]);
@@ -15235,7 +15234,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15235
15234
  var draggedNodeData = obj ? obj.dragData : null;
15236
15235
  var newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
15237
15236
  var dragLiParent = obj.dragLi.parentElement;
15238
- var dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
15237
+ var dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
15239
15238
  var targetParent = null;
15240
15239
  var indexValue = null;
15241
15240
  var iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
@@ -15244,7 +15243,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15244
15243
  var index = node ? closest(node, '.e-list-parent') : null;
15245
15244
  var i = 0;
15246
15245
  var position = null;
15247
- dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
15246
+ dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
15248
15247
  dragParent = (drop === true) ? this.dragParent : dragParent;
15249
15248
  if (cloneEle) {
15250
15249
  while (i < 4) {
@@ -15918,26 +15917,10 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
15918
15917
  TreeView.prototype.keyDownHandler = function (e) {
15919
15918
  if (e.key === 'Tab' || e.keyCode === 9) {
15920
15919
  this.isKeyboardFocus = true;
15921
- // Mark the upcoming scroll event (if any) as Tab-induced so the
15922
- // virtual scroll handler skips its re-render path. The browser
15923
- // calls `Element.scrollIntoView` semantics on focus, which scrolls
15924
- // a `<li tabindex="0">` in the middle of the tree into view and
15925
- // therefore fires `scroll` on the container. Without this guard
15926
- // the scroll handler would tear down the focused `<li>` and move
15927
- // focus elsewhere.
15928
15920
  if (this.enableVirtualization) {
15929
15921
  this.virtualSkipNextScrollRender = true;
15930
15922
  }
15931
15923
  }
15932
- // ── Fix: Also skip scroll render for arrow key navigation ──────────────────
15933
- // Arrow Down/Up calls focusNextNode() which calls ensureVirtualNodeVisible()
15934
- // to scroll the target node into view. Without this guard, the scroll event
15935
- // would trigger onVirtualScroll() which re-renders the window and resets the
15936
- // focus that focusNextNode() just set. This manifests as tabindex resetting
15937
- // to the first item instead of staying on the arrow-navigated target.
15938
- // Arrow Right/Left (expand/collapse) also calls renderVirtualItems() which
15939
- // calls li.focus() in branch 2; that can trigger a scroll on an edge node,
15940
- // so we consume it here to prevent a stale RAF re-render.
15941
15924
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
15942
15925
  || e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
15943
15926
  if (this.enableVirtualization) {
@@ -16299,10 +16282,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16299
16282
  if (this.dataType === 1) {
16300
16283
  this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
16301
16284
  }
16302
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
16303
- // after deletion so the removed nodes are correctly reflected in the hierarchical
16304
- // virtual DOM. When height is not provided, the tree uses normal rendering and
16305
- // the DOM is already updated by removeNode() above.
16306
16285
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
16307
16286
  this.virtualFlatNodes = this.flattenVisibleNodes();
16308
16287
  this.rebuildVirtualNodeMap();
@@ -16371,16 +16350,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16371
16350
  }
16372
16351
  return parentsID;
16373
16352
  };
16374
- // ═══════════════════════════════════════════════════════════════════════════════
16375
- // VIRTUALIZATION — private helpers
16376
- // ═══════════════════════════════════════════════════════════════════════════════
16377
- /**
16378
- * Parses the `height` property (string like '400px' or number) to a pixel value.
16379
- * Returns 0 if the value cannot be determined.
16380
- *
16381
- * @returns {number} Pixel height.
16382
- * @private
16383
- */
16384
16353
  TreeView.prototype.parseHeightToPixels = function () {
16385
16354
  var h = this.height;
16386
16355
  if (isNullOrUndefined(h)) {
@@ -16392,13 +16361,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16392
16361
  var parsed = parseFloat(h);
16393
16362
  return isNaN(parsed) ? 0 : parsed;
16394
16363
  };
16395
- /**
16396
- * Measures the height of the first rendered list item so subsequent calculations
16397
- * use real layout values. Falls back to the stored `virtualItemHeight` default (40).
16398
- *
16399
- * @returns {number} Row height in pixels.
16400
- * @private
16401
- */
16402
16364
  TreeView.prototype.measureVirtualItemHeight = function () {
16403
16365
  var firstLi = select('.' + LISTITEM, this.element);
16404
16366
  if (firstLi && firstLi.offsetHeight > 0) {
@@ -16406,13 +16368,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16406
16368
  }
16407
16369
  return this.virtualItemHeight;
16408
16370
  };
16409
- /**
16410
- * Rebuilds the cached Sets for checked/selected nodes to enable O(1) lookup
16411
- * during the flatten operation instead of O(n) array indexOf calls.
16412
- *
16413
- * @returns {void}
16414
- * @private
16415
- */
16416
16371
  TreeView.prototype.rebuildVirtualCheckCache = function () {
16417
16372
  this.virtualExpandedNodesSet.clear();
16418
16373
  this.virtualCheckedNodesSet.clear();
@@ -16433,20 +16388,10 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16433
16388
  }
16434
16389
  }
16435
16390
  };
16436
- /**
16437
- * Recursively walks the treeData (or groupedData for flat lists) and builds
16438
- * `virtualFlatNodes` — an ordered list of every node that is currently visible
16439
- * (i.e. its ancestors are all expanded). Uses cached Sets for O(1) state lookups.
16440
- *
16441
- * @returns {VirtualNodeData[]} Ordered flat list of visible nodes.
16442
- * @private
16443
- */
16444
16391
  TreeView.prototype.flattenVisibleNodes = function () {
16445
- // Build cached Sets for fast lookups
16446
16392
  this.rebuildVirtualCheckCache();
16447
16393
  var result = [];
16448
16394
  if (this.dataType === 1) {
16449
- // Flat data with parentID field — use getChildGroup to find root nodes
16450
16395
  var rootNodes = [];
16451
16396
  if (this.groupedData) {
16452
16397
  rootNodes = this.getChildGroup(this.groupedData, null, true);
@@ -16454,40 +16399,29 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16454
16399
  if (!rootNodes || rootNodes.length === 0) {
16455
16400
  rootNodes = this.rootData;
16456
16401
  }
16457
- // Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
16458
16402
  rootNodes = this.getSortedData(rootNodes);
16459
16403
  this.flattenNodeRecursive(rootNodes, 1, null, result);
16460
16404
  }
16461
16405
  else {
16462
- // Hierarchical data
16463
16406
  var sorted = this.getSortedData(this.rootData);
16464
16407
  this.flattenNodeRecursive(sorted, 1, null, result);
16465
16408
  }
16466
16409
  return result;
16467
16410
  };
16468
- /**
16469
- * Recursive helper for `flattenVisibleNodes`.
16470
- *
16471
- * @param {Object[]} nodes - Array of raw data objects at this level.
16472
- * @param {number} level - 1-based depth level.
16473
- * @param {string | null} parentId - The parent node id, or null for root.
16474
- * @param {VirtualNodeData[]} result - Accumulator array.
16475
- * @returns {void}
16476
- * @private
16477
- */
16478
16411
  TreeView.prototype.flattenNodeRecursive = function (nodes, level, parentId, result) {
16479
16412
  if (!nodes || nodes.length === 0) {
16480
16413
  return;
16481
16414
  }
16482
- // Cache field names and settings outside the loop to avoid repeated conversions
16483
- var childFieldName = this.fields.child.toString();
16484
- var expandedFieldName = this.fields.expanded;
16485
- var isCheckedFieldName = this.fields.isChecked;
16486
- var iconCssFieldName = this.fields.iconCss;
16487
- var imageUrlFieldName = this.fields.imageUrl;
16488
- var idFieldName = this.fields.id;
16489
- var textFieldName = this.fields.text;
16490
- var selectedFieldName = this.fields.selected;
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;
16491
16425
  var needsSorting = this.sortOrder !== 'None';
16492
16426
  var isDataTypeFlat = this.dataType === 1;
16493
16427
  var enableVirtualization = this.enableVirtualization;
@@ -16495,36 +16429,57 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16495
16429
  var raw = nodes[i];
16496
16430
  var id = isNullOrUndefined(raw[idFieldName]) ? '' : raw[idFieldName].toString();
16497
16431
  var text = isNullOrUndefined(raw[textFieldName]) ? '' : raw[textFieldName].toString();
16498
- // Use expandedNodes as the single source of truth.
16499
16432
  var inExpandedNodes = this.virtualExpandedNodesSet.has(id);
16500
- var rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
16433
+ var rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
16501
16434
  var expanded = inExpandedNodes || rawExpanded;
16502
16435
  if (rawExpanded && id) {
16503
16436
  this.expandedNodes.push(id);
16504
16437
  this.virtualExpandedNodesSet.add(id);
16505
16438
  }
16506
- // Inline hasChildRecords check to avoid function call (optimization)
16507
16439
  var hasChildren = false;
16508
- 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) {
16509
16445
  if (this.groupedData) {
16510
16446
  var groupedChild = this.getChildGroup(this.groupedData, id, false);
16511
16447
  hasChildren = !isNullOrUndefined(groupedChild) && groupedChild.length > 0;
16512
16448
  }
16513
16449
  }
16514
16450
  else {
16515
- var childrenObj = getValue(childFieldName, raw);
16451
+ var childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
16516
16452
  hasChildren = !isNullOrUndefined(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
16517
16453
  }
16518
- // Virtual-mode only: honour fields.isChecked === true on initial render
16519
- if (enableVirtualization && id && isCheckedFieldName && !this.virtualCheckedNodesSet.has(id)) {
16520
- var rawChecked = getValue(isCheckedFieldName, raw);
16521
- 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)) {
16522
16468
  this.virtualCheckedNodesSet.add(id);
16523
16469
  if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
16524
16470
  this.checkedNodes.push(id);
16525
16471
  }
16526
16472
  }
16527
16473
  }
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
+ }
16528
16483
  if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
16529
16484
  var rawSelected = getValue(selectedFieldName, raw);
16530
16485
  if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
@@ -16534,10 +16489,7 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16534
16489
  }
16535
16490
  }
16536
16491
  }
16537
- // Use cached Sets for O(1) lookup
16538
16492
  var selected = this.virtualSelectedNodesSet.has(id);
16539
- // Resolve checkbox state: indeterminate takes precedence over the checked set,
16540
- // because a node can be in the indeterminate state without being in checkedNodes.
16541
16493
  var isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
16542
16494
  ? 'indeterminate'
16543
16495
  : this.virtualCheckedNodesSet.has(id);
@@ -16551,22 +16503,19 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16551
16503
  };
16552
16504
  result.push(node);
16553
16505
  if (expanded && hasChildren) {
16554
- // Determine children list
16555
16506
  var children = [];
16556
16507
  if (isDataTypeFlat) {
16557
16508
  if (this.groupedData) {
16558
16509
  children = this.getChildGroup(this.groupedData, id, false);
16559
- // Apply sortOrder to flat-data children (same as hierarchical path)
16560
16510
  if (needsSorting) {
16561
16511
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
16562
16512
  }
16563
16513
  }
16564
16514
  }
16565
16515
  else {
16566
- var rawChildren = getValue(childFieldName, raw);
16516
+ var rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
16567
16517
  if (!isNullOrUndefined(rawChildren) && Array.isArray(rawChildren)) {
16568
16518
  children = rawChildren;
16569
- // Only sort if needed
16570
16519
  if (needsSorting) {
16571
16520
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
16572
16521
  }
@@ -16576,35 +16525,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16576
16525
  }
16577
16526
  }
16578
16527
  };
16579
- /**
16580
- * Checks whether a raw datasource node has child records.
16581
- *
16582
- * @param {Object} raw - Raw datasource object.
16583
- * @returns {boolean} True if children exist.
16584
- * @private
16585
- */
16586
- TreeView.prototype.hasChildRecords = function (raw) {
16587
- if (this.dataType === 1) {
16588
- if (!this.groupedData) {
16589
- return false;
16590
- }
16591
- var rawId = raw[this.fields.id];
16592
- if (isNullOrUndefined(rawId)) {
16593
- return false;
16594
- }
16595
- var groupedChild = this.getChildGroup(this.groupedData, rawId.toString(), false);
16596
- return !isNullOrUndefined(groupedChild) && groupedChild.length > 0;
16597
- }
16598
- var childField = this.fields.child.toString();
16599
- var children = getValue(childField, raw);
16600
- return !isNullOrUndefined(children) && Array.isArray(children) && children.length > 0;
16601
- };
16602
- /**
16603
- * Rebuilds `virtualNodeMap` (id → index) from the current `virtualFlatNodes`.
16604
- *
16605
- * @returns {void}
16606
- * @private
16607
- */
16608
16528
  TreeView.prototype.rebuildVirtualNodeMap = function () {
16609
16529
  this.virtualNodeMap.clear();
16610
16530
  for (var i = 0; i < this.virtualFlatNodes.length; i++) {
@@ -16637,49 +16557,12 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16637
16557
  var paddingBottom = Math.max(0, (total - end) * itemH);
16638
16558
  return { paddingTop: paddingTop, paddingBottom: paddingBottom };
16639
16559
  };
16640
- /**
16641
- * Renders the virtual window into a **nested** `<ul>/<li>` hierarchy identical to
16642
- * the non-virtualized DOM structure.
16643
- *
16644
- * ## Scroll math
16645
- * The flat-index ↔ pixel mapping stays unchanged: every entry in
16646
- * `virtualFlatNodes` represents exactly one `itemHeight` row.
16647
- * `paddingTop = startIndex * itemHeight` and
16648
- * `paddingBottom = (total - endIndex) * itemHeight` keep the scrollbar
16649
- * thumb in the correct position.
16650
- *
16651
- * ## Ancestor injection
16652
- * When `startIndex` points at a child node whose ancestor(s) have already
16653
- * scrolled above the viewport (their flat indices are < startIndex), the
16654
- * ancestors would normally be missing from the DOM, forcing the children to
16655
- * fall back to the root `<ul>` as flat siblings — producing wrong HTML.
16656
- *
16657
- * To fix this, before rendering the window we walk the ancestor chain of the
16658
- * first visible node and collect every ancestor that lies **outside** the
16659
- * window. Those ancestors are injected as real `<li>` containers at the
16660
- * very top of the root `<ul>`. To compensate for their presence, `paddingTop`
16661
- * is reduced by `injectedCount * itemHeight` so the total scroll height
16662
- * remains the same.
16663
- *
16664
- * Once the ancestors are in the DOM they are registered in `renderedParentMap`
16665
- * so the normal window-rendering loop can nest children inside them exactly
16666
- * as it does when both parent and child are in the same window.
16667
- *
16668
- * @returns {void}
16669
- * @private
16670
- */
16671
16560
  TreeView.prototype.renderVirtualItems = function () {
16672
16561
  if (!this.ulElement) {
16673
16562
  return;
16674
16563
  }
16675
16564
  var ul = this.ulElement;
16676
- // ── Snapshot focused ID BEFORE clearing the DOM ───────────────────────────
16677
- // Removing a focused <li> from the DOM triggers a synchronous `blur` event,
16678
- // which calls focusOut() and sets virtualFocusedId = null. Capturing the id
16679
- // here (before any removeChild) ensures focus is correctly restored to the
16680
- // same node after re-render even when keyboard navigation was in progress.
16681
16565
  var focusedUid = this.virtualFocusedId || null;
16682
- // ── Clear previous render (direct children only) ─────────────────────────
16683
16566
  var existingItems = ul.querySelectorAll(':scope > li');
16684
16567
  for (var i = existingItems.length - 1; i >= 0; i--) {
16685
16568
  ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
@@ -16690,26 +16573,22 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16690
16573
  if (this.virtualStartIndex < this.virtualFlatNodes.length) {
16691
16574
  var firstNode = this.virtualFlatNodes[this.virtualStartIndex];
16692
16575
  if (firstNode.parentId) {
16693
- // Build the ancestor chain from root down to the immediate parent
16694
16576
  var ancestors = [];
16695
16577
  var searchId = firstNode.parentId;
16696
16578
  while (searchId) {
16697
16579
  var ancestorIdx = this.virtualNodeMap.get(searchId);
16698
16580
  if (!isNullOrUndefined(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
16699
16581
  var ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
16700
- ancestors.unshift(ancestor); // prepend so root comes first
16582
+ ancestors.unshift(ancestor);
16701
16583
  searchId = ancestor.parentId;
16702
16584
  }
16703
16585
  else {
16704
16586
  break;
16705
16587
  }
16706
16588
  }
16707
- // Inject each ancestor as a real <li> into the root <ul>
16708
- // and register it in renderedParentMap for child-nesting below.
16709
16589
  for (var a = 0; a < ancestors.length; a++) {
16710
16590
  var ancestor = ancestors[parseInt(a.toString(), 10)];
16711
16591
  var ancestorLi = this.createVirtualListItem(ancestor, false);
16712
- // Find the correct container: root or a previously injected ancestor's <ul>
16713
16592
  var targetUl = ul;
16714
16593
  if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
16715
16594
  var pe = renderedParentMap.get(ancestor.parentId);
@@ -16728,26 +16607,20 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16728
16607
  }
16729
16608
  }
16730
16609
  }
16731
- // ── Scroll-space padding (adjusted for injected ancestors) ────────────────
16732
16610
  var total = this.virtualFlatNodes.length;
16733
16611
  var start = this.virtualStartIndex;
16734
16612
  var end = this.virtualEndIndex;
16735
16613
  var rawPaddingTop = start * itemH;
16736
- // Subtract the height of injected ancestors so the scroll position is
16737
- // exact: those ancestor rows are now real DOM elements, not hidden space.
16738
16614
  var adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
16739
16615
  ul.style.paddingTop = adjustedPaddingTop + 'px';
16740
16616
  ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
16741
- // ── Render window items (nested via renderedParentMap) ────────────────────
16742
16617
  var liListBuf = [];
16743
16618
  for (var i = start; i < end; i++) {
16744
16619
  var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
16745
16620
  var li = this.createVirtualListItem(node, i === start);
16746
- // Find the correct parent container
16747
16621
  var targetUl = ul;
16748
16622
  if (node.parentId && renderedParentMap.has(node.parentId)) {
16749
16623
  var parentEntry = renderedParentMap.get(node.parentId);
16750
- // Lazily create the child <ul> the first time a child needs it
16751
16624
  if (!parentEntry.childUl) {
16752
16625
  parentEntry.childUl = this.createElement('ul', {
16753
16626
  className: PARENTITEM + ' e-ul',
@@ -16759,30 +16632,11 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16759
16632
  }
16760
16633
  targetUl.appendChild(li);
16761
16634
  liListBuf.push(li);
16762
- // Register this node so its children in the same window can nest under it
16763
16635
  if (node.expanded && node.hasChildren) {
16764
16636
  renderedParentMap.set(node.id, { li: li, childUl: null });
16765
16637
  }
16766
16638
  }
16767
- // ── Restore focus / tabindex ──────────────────────────────────────────────
16768
- // In virtual mode only the li that carries the 'e-active' class should be
16769
- // focusable (tabindex='0'). All other rendered li elements must be set
16770
- // to tabindex='-1' so the tab order remains unambiguous as the window
16771
- // changes during scrolling.
16772
16639
  var focusRestored = false;
16773
- // 1. Prefer the currently active node if it is in the rendered window.
16774
- for (var k = 0; k < liListBuf.length; k++) {
16775
- var li = liListBuf[parseInt(k.toString(), 10)];
16776
- if (li.classList.contains(ACTIVE)) {
16777
- li.setAttribute('tabindex', '0');
16778
- // ── Fix: ensure FOCUS class set on active node too ──
16779
- // This ensures focusIn() guard condition is met: tabindex='0' && FOCUS class
16780
- addClass([li], FOCUS);
16781
- focusRestored = true;
16782
- break;
16783
- }
16784
- }
16785
- // 2. Otherwise fall back to the previously focused node (if it is in window).
16786
16640
  if (!focusRestored && focusedUid) {
16787
16641
  for (var k = 0; k < liListBuf.length; k++) {
16788
16642
  var li = liListBuf[parseInt(k.toString(), 10)];
@@ -16790,22 +16644,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16790
16644
  li.setAttribute('tabindex', '0');
16791
16645
  addClass([li], FOCUS);
16792
16646
  addClass([li], FOCUSED$1);
16793
- // ── Fix: restore actual browser focus so keyboard events keep working ──
16794
- // After renderVirtualItems() replaces all <li> elements the old element
16795
- // is gone from the DOM and document.activeElement falls back to
16796
- // document.body. Setting tabindex + classes is not enough — the new
16797
- // element also needs a programmatic .focus() call so subsequent keyboard
16798
- // events (Arrow keys, Home, End, etc.) are delivered to the TreeView.
16799
- // Re-attach the blur→focusOut handler so virtualFocusedId is cleared
16800
- // correctly when the user tabs away.
16801
- // ── Use preventScroll:true ────────────────────────────────────────────
16802
- // Without preventScroll the browser scrolls the container to bring this
16803
- // node into view during the re-render, changing scrollTop AFTER the
16804
- // correct paddingTop has been applied. This is the root cause of the
16805
- // KBN expand position-jump bug: the render is correct but the browser
16806
- // silently scrolls the viewport, making the parent node appear to jump.
16807
- // ensureVirtualNodeVisible() handles any legitimate scroll-into-view
16808
- // needs before renderVirtualItems() is called, so scroll here is wrong.
16809
16647
  EventHandler.add(li, 'blur', this.focusOut, this);
16810
16648
  li.focus({ preventScroll: true });
16811
16649
  focusRestored = true;
@@ -16813,53 +16651,36 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16813
16651
  }
16814
16652
  }
16815
16653
  }
16816
- // 3. Finally default to the first item in the window.
16817
16654
  if (!focusRestored && liListBuf.length > 0) {
16818
16655
  var firstLi = liListBuf[0];
16819
16656
  firstLi.setAttribute('tabindex', '0');
16820
16657
  }
16821
- // ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
16822
- // This prevents stale tabindex="0" on multiple nodes which breaks Tab order
16823
16658
  for (var k = 0; k < liListBuf.length; k++) {
16824
16659
  var li = liListBuf[parseInt(k.toString(), 10)];
16825
16660
  if (li.getAttribute('tabindex') !== '0') {
16826
16661
  li.setAttribute('tabindex', '-1');
16827
16662
  }
16828
16663
  }
16829
- // Keep liList in sync so keyboard-navigation helpers stay functional.
16830
16664
  this.liList = liListBuf;
16831
16665
  };
16832
- /**
16833
- * Creates a single `<li>` element for the virtual window representing `node`.
16834
- *
16835
- * @param {VirtualNodeData} node - The node data.
16836
- * @param {boolean} _isFirst - True if this is the first rendered item (unused here but kept for potential override).
16837
- * @returns {HTMLElement} The constructed `<li>` element.
16838
- * @private
16839
- */
16840
16666
  TreeView.prototype.createVirtualListItem = function (node, _isFirst) {
16841
16667
  var li = this.createElement('li', { className: LISTITEM });
16842
- // Level class for CSS indentation
16843
16668
  li.classList.add('e-level-' + node.level);
16844
16669
  li.setAttribute('data-uid', node.id);
16845
16670
  li.setAttribute('aria-level', node.level.toString());
16846
16671
  li.setAttribute('role', 'treeitem');
16847
16672
  li.setAttribute('tabindex', '-1');
16848
- // Full-row overlay (required for fullRowSelect mode)
16849
16673
  if (this.fullRowSelect) {
16850
16674
  var fullRow = this.createElement('div', { className: FULLROW });
16851
16675
  li.appendChild(fullRow);
16852
16676
  }
16853
- // Text/icon wrapper
16854
16677
  var textWrap = this.createElement('div', { className: TEXTWRAP });
16855
- // Expand/collapse icon
16856
16678
  if (node.hasChildren) {
16857
16679
  var iconSpan = this.createElement('div', {
16858
16680
  className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
16859
16681
  });
16860
16682
  textWrap.appendChild(iconSpan);
16861
16683
  }
16862
- // Checkbox
16863
16684
  if (this.showCheckBox) {
16864
16685
  var checkWrap = createCheckBox(this.createElement, false, {
16865
16686
  checked: node.isChecked === true,
@@ -16873,28 +16694,24 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16873
16694
  }
16874
16695
  textWrap.appendChild(checkWrap);
16875
16696
  }
16876
- // Icon
16877
16697
  if (node.iconCss) {
16878
16698
  var iconEl = this.createElement('span', {
16879
16699
  className: ICON + ' ' + node.iconCss
16880
16700
  });
16881
16701
  textWrap.appendChild(iconEl);
16882
16702
  }
16883
- // Image
16884
16703
  if (node.imageUrl) {
16885
16704
  var img = document.createElement('img');
16886
16705
  img.src = node.imageUrl;
16887
16706
  img.classList.add(IMAGE);
16888
16707
  textWrap.appendChild(img);
16889
16708
  }
16890
- // Text anchor
16891
16709
  var anchor = this.createElement('span', {
16892
16710
  className: 'e-list-text',
16893
16711
  innerHTML: node.text
16894
16712
  });
16895
16713
  textWrap.appendChild(anchor);
16896
16714
  li.appendChild(textWrap);
16897
- // nodeTemplate
16898
16715
  if (!isNullOrUndefined(this.nodeTemplateFn)) {
16899
16716
  var rawData = this.findNodeInTreeData(node.id);
16900
16717
  if (!isNullOrUndefined(rawData)) {
@@ -16902,7 +16719,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16902
16719
  this.renderNodeTemplate(rawData, anchor, node.id);
16903
16720
  }
16904
16721
  }
16905
- // State classes
16906
16722
  if (node.expanded) {
16907
16723
  li.classList.add(EXPANDED);
16908
16724
  li.setAttribute('aria-expanded', 'true');
@@ -16918,80 +16734,43 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
16918
16734
  if (node.hasChildren) {
16919
16735
  li.classList.add(CHILD);
16920
16736
  }
16921
- // Wire the focus event so `focusIn()` fires when the user Tabs into this
16922
- // item. The non-virtualized path registers this in `beforeNodeCreate()`,
16923
- // but virtual items never go through that callback, so we must register
16924
- // it here to keep focus styling (e.g. `e-focused`) consistent between
16925
- // virtual and non-virtual modes. `unWireEvents()` already removes this
16926
- // handler during teardown by iterating over the rendered `<li>`s.
16927
16737
  EventHandler.add(li, 'focus', this.focusIn, this);
16928
16738
  return li;
16929
16739
  };
16930
- /**
16931
- * Initialises the virtual-scrolling layout:
16932
- * 1. Adds the `e-virtualization` CSS class and applies a fixed `height` + `overflow-y`.
16933
- * 2. Flattens all visible nodes into `virtualFlatNodes`.
16934
- * 3. Measures item height and viewport height.
16935
- * 4. Computes the initial window and renders it.
16936
- * 5. Wires the scroll event handler.
16937
- *
16938
- * @returns {void}
16939
- * @private
16940
- */
16941
16740
  TreeView.prototype.initVirtualization = function () {
16942
16741
  addClass([this.element], VIRTUALIZATION);
16943
- // Apply container height
16944
16742
  var px = this.parseHeightToPixels();
16945
16743
  if (px > 0) {
16946
16744
  this.element.style.height = px + 'px';
16947
16745
  this.element.style.overflowY = 'auto';
16948
16746
  }
16949
- // Ensure the inner UL is ready
16950
16747
  if (!this.ulElement) {
16951
16748
  this.ulElement = select('.' + PARENTITEM, this.element);
16952
16749
  }
16953
16750
  if (!this.ulElement) {
16954
16751
  return;
16955
16752
  }
16956
- // Make the UL position-relative for the absolute spacers
16957
16753
  this.ulElement.style.position = 'relative';
16958
- // Rebuild flat list
16959
16754
  this.virtualFlatNodes = this.flattenVisibleNodes();
16960
16755
  this.rebuildVirtualNodeMap();
16961
- // Measure dimensions
16962
16756
  this.virtualViewportHeight = px > 0 ? px : this.element.offsetHeight;
16963
- // Render a single probe item to measure the real item height before the full render
16964
16757
  if (this.virtualFlatNodes.length > 0) {
16965
16758
  var probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
16966
16759
  this.ulElement.appendChild(probe);
16967
16760
  this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
16968
16761
  this.ulElement.removeChild(probe);
16969
16762
  }
16970
- // Compute window and render all items in the initial viewport
16971
16763
  var win = this.getVirtualWindow();
16972
16764
  this.virtualStartIndex = win.startIndex;
16973
16765
  this.virtualEndIndex = win.endIndex;
16974
16766
  this.renderVirtualItems();
16975
- // Wire scroll
16976
16767
  this.wireVirtualScrollEvents();
16977
16768
  };
16978
- /**
16979
- * Wires the scroll event listener on the TreeView element.
16980
- *
16981
- * @returns {void}
16982
- * @private
16983
- */
16984
16769
  TreeView.prototype.wireVirtualScrollEvents = function () {
16985
16770
  this.unwireVirtualScrollEvents();
16986
16771
  this.virtualScrollBound = this.onVirtualScroll.bind(this);
16987
16772
  this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
16988
16773
  };
16989
- /**
16990
- * Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
16991
- *
16992
- * @returns {void}
16993
- * @private
16994
- */
16995
16774
  TreeView.prototype.unwireVirtualScrollEvents = function () {
16996
16775
  if (this.virtualScrollBound) {
16997
16776
  this.element.removeEventListener('scroll', this.virtualScrollBound);
@@ -17002,29 +16781,13 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17002
16781
  this.virtualScrollRAF = null;
17003
16782
  }
17004
16783
  };
17005
- /**
17006
- * Scroll handler for the virtual TreeView. Throttled via `requestAnimationFrame`.
17007
- * Shows the loading mask during fast scrolls, re-renders the window, then hides the mask.
17008
- *
17009
- * @returns {void}
17010
- * @private
17011
- */
17012
16784
  TreeView.prototype.onVirtualScroll = function () {
17013
16785
  var _this = this;
17014
16786
  if (this.virtualScrollRAF !== null) {
17015
16787
  cancelAnimationFrame(this.virtualScrollRAF);
17016
16788
  }
17017
- // ── Tab-induced focus guard ────────────────────────────────────────────────
17018
- // When the user Tabs into a `<li tabindex="0">` that is in the middle of
17019
- // the treeview, the browser auto-scrolls the container so the focused
17020
- // element becomes visible. That fires a `scroll` event here, but the
17021
- // virtual window already contains the focused element — re-rendering
17022
- // would destroy the focused `<li>` and break focus. Detect this case
17023
- // via the flag set in `keyDownHandler` and skip the re-render entirely.
17024
16789
  if (this.virtualSkipNextScrollRender) {
17025
16790
  this.virtualSkipNextScrollRender = false;
17026
- // Update prev scrollTop so a subsequent user-driven scroll still
17027
- // calculates the correct delta.
17028
16791
  this.virtualPrevScrollTop = this.element.scrollTop;
17029
16792
  return;
17030
16793
  }
@@ -17041,30 +16804,16 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17041
16804
  _this.virtualStartIndex = win.startIndex;
17042
16805
  _this.virtualEndIndex = win.endIndex;
17043
16806
  _this.renderVirtualItems();
17044
- // Hide the skeleton mask after a short delay so it stays visible long enough
17045
- // for the user to perceive the loading indicator even on short scrolls.
17046
16807
  setTimeout(function () {
17047
16808
  _this.hideVirtualMask();
17048
- // Keep the (now hidden) mask's coords fresh for the next show.
17049
16809
  _this.updateMaskPosition();
17050
16810
  }, 200);
17051
16811
  });
17052
16812
  };
17053
- /**
17054
- * Creates the skeleton mask overlay used to indicate loading during fast scrolls.
17055
- * Skeleton instances are stored in `skeletonInstances` for later disposal.
17056
- *
17057
- * @returns {void}
17058
- * @private
17059
- */
17060
16813
  TreeView.prototype.createVirtualMask = function () {
17061
16814
  if (this.virtualMaskElement) {
17062
16815
  return;
17063
16816
  }
17064
- // Use position:fixed so the mask is anchored to the visible viewport (not to the
17065
- // scrolling container). Width/height/left/top are set/refreshed by
17066
- // updateMaskPosition() so the mask always covers the TreeView's visible area
17067
- // regardless of how the page itself is scrolled.
17068
16817
  var rect = this.element.getBoundingClientRect();
17069
16818
  var scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
17070
16819
  var maskParent = this.createElement('div', {
@@ -17077,20 +16826,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17077
16826
  className: VIRTUAL_MASK_ITEM,
17078
16827
  styles: "display:flex;align-items:center;height:" + this.virtualItemHeight + "px;padding:0 12px;gap:8px;"
17079
16828
  });
17080
- // Skeleton circle for icon placeholder
17081
- // const iconHolder: HTMLElement = this.createElement('span', {
17082
- // styles: `width:20px;height:20px;`
17083
- // });
17084
- // maskItem.appendChild(iconHolder);
17085
- // const iconSkel: Skeleton = new Skeleton({
17086
- // shape: 'Circle',
17087
- // width: '20px',
17088
- // height: '20px',
17089
- // cssClass: 'e-virtual-skel-icon'
17090
- // });
17091
- // iconSkel.appendTo(iconHolder);
17092
- // this.skeletonInstances.push(iconSkel);
17093
- // Skeleton text bar
17094
16829
  var textHolder = this.createElement('span');
17095
16830
  textHolder.style.flex = '1';
17096
16831
  textHolder.style.height = '14px';
@@ -17108,14 +16843,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17108
16843
  this.virtualMaskElement = maskParent;
17109
16844
  this.element.appendChild(maskParent);
17110
16845
  };
17111
- /**
17112
- * Refreshes the mask position so it always overlays the TreeView's viewport.
17113
- * Called on every show / scroll, since `position:fixed` is relative to the
17114
- * page viewport, not to the TreeView scroll container.
17115
- *
17116
- * @returns {void}
17117
- * @private
17118
- */
17119
16846
  TreeView.prototype.updateMaskPosition = function () {
17120
16847
  if (!this.virtualMaskElement) {
17121
16848
  return;
@@ -17127,12 +16854,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17127
16854
  this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
17128
16855
  this.virtualMaskElement.style.height = rect.height + 'px';
17129
16856
  };
17130
- /**
17131
- * Destroys all skeleton instances and removes the mask overlay from the DOM.
17132
- *
17133
- * @returns {void}
17134
- * @private
17135
- */
17136
16857
  TreeView.prototype.destroyVirtualMask = function () {
17137
16858
  for (var i = this.skeletonInstances.length - 1; i >= 0; i--) {
17138
16859
  this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
@@ -17145,52 +16866,26 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17145
16866
  this.virtualMaskElement = null;
17146
16867
  }
17147
16868
  };
17148
- /**
17149
- * Positions and shows the skeleton mask at the current `scrollTop` position.
17150
- *
17151
- * @param {number} _scrollTop - Current scrollTop of the container.
17152
- * @returns {void}
17153
- * @private
17154
- */
17155
16869
  TreeView.prototype.showVirtualMask = function (_scrollTop) {
17156
16870
  if (!this.virtualMaskElement) {
17157
16871
  this.createVirtualMask();
17158
16872
  }
17159
16873
  if (this.virtualMaskElement) {
17160
- // Refresh the fixed-position coords in case the TreeView was moved/resized
17161
- // since the last time the mask was shown.
17162
16874
  this.updateMaskPosition();
17163
16875
  this.virtualMaskElement.style.display = 'block';
17164
- // Visually hide the real list while the skeleton is shown
17165
16876
  if (this.ulElement) {
17166
16877
  this.ulElement.style.visibility = 'hidden';
17167
16878
  }
17168
16879
  }
17169
16880
  };
17170
- /**
17171
- * Hides the skeleton mask overlay after the virtual window has been rendered.
17172
- *
17173
- * @returns {void}
17174
- * @private
17175
- */
17176
16881
  TreeView.prototype.hideVirtualMask = function () {
17177
16882
  if (this.virtualMaskElement) {
17178
16883
  this.virtualMaskElement.style.display = 'none';
17179
- // Restore real list visibility
17180
16884
  if (this.ulElement) {
17181
16885
  this.ulElement.style.visibility = '';
17182
16886
  }
17183
16887
  }
17184
16888
  };
17185
- /**
17186
- * Ensures the node identified by `id` is scrolled into view inside the virtual container.
17187
- * If the node falls outside the rendered window, the container's `scrollTop` is adjusted so
17188
- * that the node becomes visible, the window is re-computed and `renderVirtualItems` is called.
17189
- *
17190
- * @param {string} id - The `data-uid` of the target node.
17191
- * @returns {void}
17192
- * @private
17193
- */
17194
16889
  TreeView.prototype.ensureVirtualNodeVisible = function (id) {
17195
16890
  var idx = this.virtualNodeMap.get(id);
17196
16891
  if (isNullOrUndefined(idx)) {
@@ -17209,25 +16904,13 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17209
16904
  this.element.scrollTop = nodeBottom - vpH;
17210
16905
  }
17211
16906
  else {
17212
- return; // already visible, no re-render needed
16907
+ return;
17213
16908
  }
17214
- // Re-render the new window synchronously so the <li> is in the DOM
17215
16909
  var win = this.getVirtualWindow();
17216
16910
  this.virtualStartIndex = win.startIndex;
17217
16911
  this.virtualEndIndex = win.endIndex;
17218
16912
  this.renderVirtualItems();
17219
16913
  };
17220
- /**
17221
- * Recursively walks the treeData (and any nested `childFieldName` arrays) to
17222
- * find the node whose `idFieldName` matches the given `id` (compared as
17223
- * strings). Used by the virtual-mode expand/collapse handlers so they can
17224
- * locate nodes at any depth — not just the root level — before writing the
17225
- * `expanded` flag back to the datasource.
17226
- *
17227
- * @param {string} id - The id (as a string) of the node to locate.
17228
- * @returns {Object | null} The matching raw datasource node, or null if not found.
17229
- * @private
17230
- */
17231
16914
  TreeView.prototype.findNodeInTreeData = function (id) {
17232
16915
  if (!this.treeData) {
17233
16916
  return null;
@@ -17255,12 +16938,19 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17255
16938
  };
17256
16939
  return findRecursive(this.treeData);
17257
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
+ };
17258
16952
  TreeView.prototype.handleVirtualExpand = function (id, isKeyboardNav) {
17259
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
17260
- // A scroll RAF queued before the expand click would run asynchronously after
17261
- // this synchronous expand completes, re-rendering the window from a stale
17262
- // scrollTop and moving the parent node to the wrong position. Cancel it now
17263
- // so the expand render is authoritative.
16953
+ var _this = this;
17264
16954
  if (this.virtualScrollRAF !== null) {
17265
16955
  cancelAnimationFrame(this.virtualScrollRAF);
17266
16956
  this.virtualScrollRAF = null;
@@ -17268,7 +16958,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17268
16958
  if (this.expandedNodes.indexOf(id) === -1) {
17269
16959
  this.expandedNodes.push(id);
17270
16960
  }
17271
- // Also update virtualExpandedNodesSet to keep it in sync
17272
16961
  if (this.virtualExpandedNodesSet) {
17273
16962
  this.virtualExpandedNodesSet.add(id);
17274
16963
  }
@@ -17278,56 +16967,76 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17278
16967
  setValue(this.fields.expanded, true, acutalRootData);
17279
16968
  }
17280
16969
  }
17281
- // ── Fix: Pin focused ID to the expanding node for keyboard navigation ──
17282
- // When keyboard-triggered, the expanded node IS definitively the focused node
17283
- // (`id` comes from currLi.getAttribute('data-uid') in expandNode, where currLi
17284
- // is getFocusedNode()). Set unconditionally — the old conditional guard
17285
- // `!this.virtualFocusedId` could leave a stale or null value if a previous
17286
- // RAF render (from arrow-key scroll) hadn't yet completed, causing branch 3
17287
- // (first item) to fire in renderVirtualItems() instead of branch 2.
17288
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
17289
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
17290
- // mouse-based expand/collapse should never add the e-focused class.
17291
16970
  if (isKeyboardNav) {
17292
16971
  this.virtualFocusedId = id;
17293
16972
  }
17294
16973
  else {
17295
16974
  this.virtualFocusedId = null;
17296
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) {
17297
17026
  this.virtualFlatNodes = this.flattenVisibleNodes();
17298
17027
  this.rebuildVirtualNodeMap();
17299
- // ── Snapshot scrollTop before computing the window ────────────────────────
17300
- // Read scrollTop once here and pass it into getVirtualWindow so that any
17301
- // browser scroll-anchoring adjustment that happens when the padding values
17302
- // change cannot affect which window we render.
17303
17028
  var scrollTopSnapshot = this.element.scrollTop;
17304
17029
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
17305
17030
  this.virtualStartIndex = win.startIndex;
17306
17031
  this.virtualEndIndex = win.endIndex;
17307
- // ── Block ALL scroll-triggered re-renders during this expand ─────────────
17308
- // renderVirtualItems() calls li.focus() in branch 2 (keyboard focus restore).
17309
- // The post-render focus() call below can also trigger browser scroll. Both
17310
- // can fire the 'scroll' event and cause onVirtualScroll() to queue a RAF.
17311
- // We set the skip flag ONCE here; it is consumed by the first scroll event.
17312
- // To cover the second potential scroll (from the focus() call below) we use
17313
- // preventScroll:true on that call so it never moves the container at all.
17314
17032
  this.virtualSkipNextScrollRender = true;
17315
17033
  this.renderVirtualItems();
17316
- // renderVirtualItems() replaces all <li> elements so the old icon element that
17317
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
17318
- // so the node can be collapsed/expanded again without being blocked.
17319
17034
  var newLi = this.getElement(id);
17320
17035
  if (newLi) {
17321
17036
  var icon = select('.' + ICON, newLi);
17322
17037
  if (icon) {
17323
17038
  removeClass([icon], PROCESS);
17324
17039
  }
17325
- // ── Use preventScroll:true ────────────────────────────────────────────
17326
- // Without preventScroll the browser scrolls the container to bring this
17327
- // node into view, which fires a second 'scroll' event. The first scroll
17328
- // was already consumed by virtualSkipNextScrollRender (set above), so the
17329
- // second scroll would queue a new RAF that re-renders from the
17330
- // focus-adjusted scrollTop — moving the parent node to the wrong position.
17331
17040
  newLi.focus({ preventScroll: true });
17332
17041
  }
17333
17042
  if (this.isLoaded) {
@@ -17340,9 +17049,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17340
17049
  }
17341
17050
  };
17342
17051
  TreeView.prototype.handleVirtualCollapse = function (id, isKeyboardNav) {
17343
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
17344
- // Mirrors handleVirtualExpand: a queued RAF from a previous scroll event
17345
- // must not override the collapse render with a stale window.
17346
17052
  if (this.virtualScrollRAF !== null) {
17347
17053
  cancelAnimationFrame(this.virtualScrollRAF);
17348
17054
  this.virtualScrollRAF = null;
@@ -17351,7 +17057,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17351
17057
  if (idx !== -1) {
17352
17058
  this.expandedNodes.splice(idx, 1);
17353
17059
  }
17354
- // Also update virtualExpandedNodesSet to keep it in sync
17355
17060
  if (this.virtualExpandedNodesSet) {
17356
17061
  this.virtualExpandedNodesSet.delete(id);
17357
17062
  }
@@ -17361,13 +17066,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17361
17066
  setValue(this.fields.expanded, false, acutalRootData);
17362
17067
  }
17363
17068
  }
17364
- // ── Fix: Pin focused ID to the collapsing node for keyboard navigation ──
17365
- // Same reasoning as handleVirtualExpand: when keyboard-triggered, `id` is
17366
- // definitively the focused node. Set unconditionally to avoid the stale/null
17367
- // state that could cause the RAF re-render to fall through to branch 3.
17368
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
17369
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
17370
- // mouse-based expand/collapse should never add the e-focused class.
17371
17069
  if (isKeyboardNav) {
17372
17070
  this.virtualFocusedId = id;
17373
17071
  }
@@ -17376,28 +17074,18 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17376
17074
  }
17377
17075
  this.virtualFlatNodes = this.flattenVisibleNodes();
17378
17076
  this.rebuildVirtualNodeMap();
17379
- // ── Snapshot scrollTop before computing the window ────────────────────────
17380
- // Same rationale as handleVirtualExpand: capture scrollTop before any focus()
17381
- // call or padding change can cause the browser to auto-scroll.
17382
17077
  var scrollTopSnapshot = this.element.scrollTop;
17383
17078
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
17384
17079
  this.virtualStartIndex = win.startIndex;
17385
17080
  this.virtualEndIndex = win.endIndex;
17386
- // ── Block ALL scroll-triggered re-renders during this collapse ────────────
17387
- // Same as handleVirtualExpand: use preventScroll:true on the focus() call
17388
- // below so the second potential scroll event is never fired.
17389
17081
  this.virtualSkipNextScrollRender = true;
17390
17082
  this.renderVirtualItems();
17391
- // renderVirtualItems() replaces all <li> elements so the old icon element that
17392
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
17393
- // so the node can be expanded/collapsed again without being blocked.
17394
17083
  var newLi = this.getElement(id);
17395
17084
  if (newLi) {
17396
17085
  var icon = select('.' + ICON, newLi);
17397
17086
  if (icon) {
17398
17087
  removeClass([icon], PROCESS);
17399
17088
  }
17400
- // ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
17401
17089
  newLi.focus({ preventScroll: true });
17402
17090
  }
17403
17091
  if (this.isLoaded) {
@@ -17409,14 +17097,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17409
17097
  }
17410
17098
  }
17411
17099
  };
17412
- /**
17413
- * Updates the selected state of a node in `virtualFlatNodes` and re-renders the window.
17414
- *
17415
- * @param {string} id - The `data-uid` of the target node.
17416
- * @param {boolean} selected - Whether the node is now selected.
17417
- * @returns {void}
17418
- * @private
17419
- */
17420
17100
  TreeView.prototype.updateVirtualNodeSelection = function (id, selected) {
17421
17101
  var idx = this.virtualNodeMap.get(id);
17422
17102
  if (!isNullOrUndefined(idx)) {
@@ -17424,21 +17104,11 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17424
17104
  this.renderVirtualItems();
17425
17105
  }
17426
17106
  };
17427
- /**
17428
- * Updates the checkbox state of a node in `virtualFlatNodes` and re-renders the window.
17429
- *
17430
- * @param {string} id - The `data-uid` of the target node.
17431
- * @param {'check' | 'uncheck' | 'indeterminate'} state - The new check state.
17432
- * @returns {void}
17433
- * @private
17434
- */
17435
17107
  TreeView.prototype.updateVirtualNodeCheckState = function (id, state) {
17436
17108
  var idx = this.virtualNodeMap.get(id);
17437
17109
  if (!isNullOrUndefined(idx)) {
17438
17110
  this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
17439
17111
  state === 'check' ? true : state === 'indeterminate' ? 'indeterminate' : false;
17440
- // Keep the persistent indeterminate set in sync so flattenVisibleNodes()
17441
- // correctly restores state after any full list rebuild (e.g. collapse).
17442
17112
  if (state === 'indeterminate') {
17443
17113
  this.virtualIndeterminateNodesSet.add(id);
17444
17114
  }
@@ -17448,9 +17118,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17448
17118
  this.renderVirtualItems();
17449
17119
  }
17450
17120
  };
17451
- // ═══════════════════════════════════════════════════════════════════════════════
17452
- // END VIRTUALIZATION
17453
- // ═══════════════════════════════════════════════════════════════════════════════
17454
17121
  /**
17455
17122
  * Called internally if any of the property value changed.
17456
17123
  *
@@ -17595,7 +17262,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17595
17262
  }
17596
17263
  }
17597
17264
  else {
17598
- // Tear down virtualization and do a full re-render
17599
17265
  this.unwireVirtualScrollEvents();
17600
17266
  this.destroyVirtualMask();
17601
17267
  removeClass([this.element], VIRTUALIZATION);
@@ -17606,7 +17272,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17606
17272
  break;
17607
17273
  case 'height':
17608
17274
  if (this.enableVirtualization) {
17609
- // Re-initialise so the new height takes effect
17610
17275
  this.unwireVirtualScrollEvents();
17611
17276
  this.initVirtualization();
17612
17277
  }
@@ -17624,7 +17289,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17624
17289
  * @returns {void}
17625
17290
  */
17626
17291
  TreeView.prototype.destroy = function () {
17627
- // Tear down virtualization before standard cleanup
17628
17292
  if (this.enableVirtualization) {
17629
17293
  this.unwireVirtualScrollEvents();
17630
17294
  this.destroyVirtualMask();
@@ -17715,10 +17379,6 @@ var TreeView = /** @__PURE__ @class */ (function (_super) {
17715
17379
  if (isNullOrUndefined(fNode)) {
17716
17380
  this.setNodeFocusable();
17717
17381
  }
17718
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
17719
- // the window so the newly added nodes appear in the correct hierarchical position.
17720
- // When height is not provided, the tree uses normal rendering and the DOM is
17721
- // already updated by addGivenNodes() above.
17722
17382
  if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
17723
17383
  this.setRootData();
17724
17384
  this.rebuildVirtualCheckCache();