@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.
@@ -228,54 +228,25 @@ var TreeView = /** @class */ (function (_super) {
228
228
  _this.isDropIn = false;
229
229
  _this.OldCheckedData = [];
230
230
  _this.isHiddenItem = false;
231
- // ── Virtualization state ──────────────────────────────────────────────────────────────
232
- /** Flat ordered list of all currently-visible (non-collapsed) tree nodes. */
231
+ // Virtualization state
233
232
  _this.virtualFlatNodes = [];
234
- /** Maps node id → index in virtualFlatNodes for O(1) lookups. */
235
233
  _this.virtualNodeMap = new Map();
236
- /** Set of expanded node IDs for O(1) lookup performance during flattening (avoids array indexOf). */
237
234
  _this.virtualExpandedNodesSet = new Set();
238
- /** Set of checked node IDs for O(1) lookup performance during flattening. */
239
235
  _this.virtualCheckedNodesSet = new Set();
240
- /** Set of indeterminate node IDs — persists across flattenVisibleNodes() rebuilds. */
241
236
  _this.virtualIndeterminateNodesSet = new Set();
242
- /** Set of selected node IDs for O(1) lookup performance during flattening. */
243
237
  _this.virtualSelectedNodesSet = new Set();
244
- /** Row height in pixels, measured from the first rendered item or set to 40 as a default. */
245
238
  _this.virtualItemHeight = 40;
246
- /** Height of the scrollable container in pixels. */
247
239
  _this.virtualViewportHeight = 0;
248
- /** scrollTop captured during the previous scroll handler invocation. */
249
240
  _this.virtualPrevScrollTop = 0;
250
- /** Index of the first rendered row in the current window. */
251
241
  _this.virtualStartIndex = 0;
252
- /** Index one past the last rendered row in the current window. */
253
242
  _this.virtualEndIndex = 0;
254
- /** Extra rows rendered above and below the visible window to smooth scrolling. */
255
243
  _this.virtualOverscanCount = 3;
256
- /** RequestAnimationFrame handle used for throttling scroll events. */
257
244
  _this.virtualScrollRAF = null;
258
- /** The `.e-mask-parent` overlay element shown during fast scroll. */
259
245
  _this.virtualMaskElement = null;
260
- /** Bound scroll handler stored for later removeEventListener. */
261
246
  _this.virtualScrollBound = null;
262
- /** Skeleton component instances inside the mask overlay. */
263
247
  _this.skeletonInstances = [];
264
- /** Set to true whenever the flat node list must be rebuilt on next render. */
265
248
  _this.virtualTreeDirty = false;
266
- /** The data-uid of the currently keyboard-focused node in virtual mode (persists across re-renders). */
267
249
  _this.virtualFocusedId = null;
268
- /**
269
- * Set to true by the document-level `keydown` handler when the user presses
270
- * the Tab key. The browser will auto-scroll the focused element into view
271
- * immediately after focus, which fires a `scroll` event on the TreeView
272
- * container. We want to skip the re-render that the virtual scroll handler
273
- * would otherwise trigger, because the element being focused is already
274
- * rendered (it is the single `<li tabindex="0">` of the current window) and
275
- * re-rendering would tear down the DOM element that is currently holding
276
- * focus. The flag is consumed (reset to `false`) inside `onVirtualScroll`
277
- * after the auto-scroll has been handled.
278
- */
279
250
  _this.virtualSkipNextScrollRender = false;
280
251
  return _this;
281
252
  }
@@ -578,16 +549,6 @@ var TreeView = /** @class */ (function (_super) {
578
549
  }
579
550
  };
580
551
  TreeView.prototype.renderItems = function (isSorted) {
581
- // In virtual mode (with a fixed height) create only the container <ul> —
582
- // initVirtualization will populate the items directly, avoiding the O(n)
583
- // ListBase.createList cost.
584
- // updateCheckedStateFromDS / updateParentCheckState are intentionally skipped here:
585
- // they call getCheckedNodeDetails which traverses the DOM (no DOM exists yet in
586
- // virtual mode) and would throw on empty recursion results. The initial checked
587
- // state is derived from the raw data inside flattenNodeRecursive instead.
588
- //
589
- // When enableVirtualization is true but height is NOT provided, we fall through
590
- // to the normal (non-virtualized) rendering path so the tree is still visible.
591
552
  if (this.enableVirtualization && !isNOU(this.height)) {
592
553
  this.ulElement = this.createElement('ul', {
593
554
  className: PARENTITEM + ' ' + 'e-ul',
@@ -1674,7 +1635,6 @@ var TreeView = /** @class */ (function (_super) {
1674
1635
  this.checkDisabledState(id);
1675
1636
  }
1676
1637
  if (this.enableVirtualization) {
1677
- // Clear any indeterminate state — node is now fully checked
1678
1638
  this.virtualIndeterminateNodesSet.delete(id);
1679
1639
  var actualIndex = this.virtualNodeMap.get(id);
1680
1640
  if (!isNOU(actualIndex) && actualIndex > -1) {
@@ -1695,20 +1655,15 @@ var TreeView = /** @class */ (function (_super) {
1695
1655
  var actualIndex = this.virtualNodeMap.get(id);
1696
1656
  if (!isNOU(actualIndex) && actualIndex > -1) {
1697
1657
  if (isIndeterminate) {
1698
- // Parent is transitioning to indeterminate — preserve children states
1699
- // and mark this node as 'indeterminate' so scroll/collapse re-renders correctly.
1700
1658
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = 'indeterminate';
1701
- // Also record it in the persistent indeterminate set so flattenVisibleNodes()
1702
- // can restore this state after a full rebuild (e.g. on collapse).
1703
1659
  this.virtualIndeterminateNodesSet.add(id);
1704
1660
  }
1705
1661
  else {
1706
- // Node is being fully unchecked — clear indeterminate tracking too
1707
1662
  this.virtualIndeterminateNodesSet.delete(id);
1708
1663
  this.virtualCheckedNodesSet.delete(id);
1709
1664
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = false;
1710
1665
  if (this.treeData && this.fields && this.fields.isChecked) {
1711
- setValue(this.fields.isChecked, false, this.treeData[parseInt(id.toString(), 10) - 1]);
1666
+ this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
1712
1667
  }
1713
1668
  if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
1714
1669
  this.setVirtualChildCheckState(id, false);
@@ -1754,8 +1709,6 @@ var TreeView = /** @class */ (function (_super) {
1754
1709
  }
1755
1710
  }
1756
1711
  }
1757
- // Collapsed children are not present in virtualFlatNodes, so walk the raw data
1758
- // to keep this.treeData and checkedNodes in sync for hidden descendants too.
1759
1712
  var collapsedChildren = this.getChildNodes(this.treeData, parentId);
1760
1713
  if (collapsedChildren) {
1761
1714
  for (var i = 0; i < collapsedChildren.length; i++) {
@@ -1901,7 +1854,6 @@ var TreeView = /** @class */ (function (_super) {
1901
1854
  }
1902
1855
  this.isLoaded = true;
1903
1856
  this.isNodeDropped = false;
1904
- // Initialise (or re-initialise) virtual mode after data binding has settled
1905
1857
  if (this.enableVirtualization && !isNOU(this.height)) {
1906
1858
  this.initVirtualization();
1907
1859
  }
@@ -2041,9 +1993,6 @@ var TreeView = /** @class */ (function (_super) {
2041
1993
  };
2042
1994
  TreeView.prototype.expandNode = function (currLi, icon, loaded, isKeyboardNav) {
2043
1995
  var _this = this;
2044
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
2045
- // Only engage when height is also set; without a fixed height the tree falls
2046
- // back to normal rendering and the standard expand path must be used.
2047
1996
  if (this.enableVirtualization && !isNOU(this.height)) {
2048
1997
  var id = currLi ? currLi.getAttribute('data-uid') : null;
2049
1998
  if (id) {
@@ -2172,9 +2121,6 @@ var TreeView = /** @class */ (function (_super) {
2172
2121
  };
2173
2122
  TreeView.prototype.nodeCollapseAction = function (currLi, icon, colArgs, isKeyboardNav) {
2174
2123
  var _this = this;
2175
- // Virtual mode fast-path: rebuild the flat list and re-render the window.
2176
- // Only engage when height is also set; without a fixed height the tree falls
2177
- // back to normal rendering and the standard collapse path must be used.
2178
2124
  if (this.enableVirtualization && !isNOU(this.height)) {
2179
2125
  var id = currLi ? currLi.getAttribute('data-uid') : null;
2180
2126
  if (id) {
@@ -2645,7 +2591,6 @@ var TreeView = /** @class */ (function (_super) {
2645
2591
  li.setAttribute('tabindex', '0');
2646
2592
  EventHandler.add(li, 'blur', this.focusOut, this);
2647
2593
  this.updateIdAttr(focusedNode, li);
2648
- // Track focused id for virtual mode re-render restoration
2649
2594
  if (this.enableVirtualization) {
2650
2595
  this.virtualFocusedId = li.getAttribute('data-uid');
2651
2596
  }
@@ -2661,7 +2606,7 @@ var TreeView = /** @class */ (function (_super) {
2661
2606
  if (this.enableVirtualization && !isNOU(this.height)) {
2662
2607
  this.virtualSelectedNodesSet.add(id);
2663
2608
  var actualIndex = this.virtualNodeMap.get(id);
2664
- if (actualIndex && actualIndex > -1) {
2609
+ if (!isNOU(actualIndex) && actualIndex > -1) {
2665
2610
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
2666
2611
  }
2667
2612
  }
@@ -2682,7 +2627,7 @@ var TreeView = /** @class */ (function (_super) {
2682
2627
  var id = liEle.getAttribute('data-uid');
2683
2628
  this.virtualSelectedNodesSet.delete(id);
2684
2629
  var actualIndex = this.virtualNodeMap.get(id);
2685
- if (actualIndex && actualIndex > -1) {
2630
+ if (!isNOU(actualIndex) && actualIndex > -1) {
2686
2631
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
2687
2632
  }
2688
2633
  }
@@ -2777,12 +2722,6 @@ var TreeView = /** @class */ (function (_super) {
2777
2722
  }
2778
2723
  };
2779
2724
  TreeView.prototype.nodeExpandAction = function (currLi, icon, expandChild, callback, isFromExpandAll, isKeyboardNav) {
2780
- // In virtual mode (with a fixed height) the flat <li> never has a child <ul>
2781
- // in the DOM, so the PARENTITEM check below would always fail and fall into
2782
- // renderChildNodes(), which corrupts the virtual DOM.
2783
- // Delegate directly to expandNode() which has the handleVirtualExpand fast-path.
2784
- // When height is not provided, fall through to normal expand logic below since
2785
- // the tree is rendered with a full nested DOM structure.
2786
2725
  if (this.enableVirtualization && !isNOU(this.height)) {
2787
2726
  this.expandNode(currLi, icon, undefined, isKeyboardNav);
2788
2727
  return;
@@ -2900,9 +2839,6 @@ var TreeView = /** @class */ (function (_super) {
2900
2839
  });
2901
2840
  };
2902
2841
  TreeView.prototype.navigateToFocus = function (isUp) {
2903
- // In virtual mode (with a fixed height) the element itself is the scroll
2904
- // container; use ensureVirtualNodeVisible. When height is not provided,
2905
- // the tree uses normal rendering, so fall through to the standard scroll logic.
2906
2842
  if (this.enableVirtualization && !isNOU(this.height)) {
2907
2843
  var focusedLi = this.getFocusedNode();
2908
2844
  if (focusedLi) {
@@ -3334,7 +3270,6 @@ var TreeView = /** @class */ (function (_super) {
3334
3270
  };
3335
3271
  TreeView.prototype.focusNextNode = function (li, isTowards) {
3336
3272
  if (this.enableVirtualization && !isNOU(this.height)) {
3337
- // Determine the id of the node one step in the requested direction
3338
3273
  var currentId = li ? li.getAttribute('data-uid') : null;
3339
3274
  var currentIdx = -1;
3340
3275
  if (!isNOU(currentId)) {
@@ -3346,26 +3281,14 @@ var TreeView = /** @class */ (function (_super) {
3346
3281
  return;
3347
3282
  }
3348
3283
  var targetId = this.virtualFlatNodes[parseInt(targetIdx.toString(), 10)].id;
3349
- // ── Fix 1: set virtualFocusedId BEFORE calling ensureVirtualNodeVisible ──
3350
- // renderVirtualItems() is called synchronously inside ensureVirtualNodeVisible
3351
- // when a re-render is needed. If virtualFocusedId is still the old value at
3352
- // that point, focus restoration falls back to the first item. Setting it here
3353
- // ensures renderVirtualItems always restores focus to the correct target node.
3354
3284
  this.virtualFocusedId = targetId;
3355
- // ── Fix 2: clear focus classes from ALL nested list items, not just top-level ones ──
3356
- // With hierarchical virtual DOM, the currently focused <li> may be nested inside
3357
- // a child <ul>. Using ':scope > li' only cleans up direct children and leaves
3358
- // stale e-focused / e-node-focus classes on nested nodes across re-renders.
3359
3285
  var allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
3360
3286
  for (var k = 0; k < allLi.length; k++) {
3361
3287
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
3362
3288
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED);
3363
3289
  allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
3364
3290
  }
3365
- // Ensure the target <li> is in the DOM window (may trigger a re-render).
3366
- // virtualFocusedId is already set above so renderVirtualItems restores focus correctly.
3367
3291
  this.ensureVirtualNodeVisible(targetId);
3368
- // Re-query both nodes after the potential re-render so setFocus gets live DOM elements
3369
3292
  var prevNode = currentId ? this.getElement(currentId) : li;
3370
3293
  var nextNode_1 = this.getElement(targetId);
3371
3294
  if (!isNOU(nextNode_1)) {
@@ -3466,18 +3389,6 @@ var TreeView = /** @class */ (function (_super) {
3466
3389
  if (!focusedElement) {
3467
3390
  return;
3468
3391
  }
3469
- // Virtual-mode guard: when the browser moves focus into the TreeView (e.g. via
3470
- // the Tab key) it always lands on the single `<li tabindex="0">`. In that
3471
- // case the element is already the focusable one, FOCUS is already applied
3472
- // and the only thing left to do is the keyboard focus styling. Skip the
3473
- // redundant setAttribute/EventHandler.add work so we never re-render the
3474
- // virtual window as a side effect of focusing the existing `tabindex="0"`
3475
- // item.
3476
- // ── Fix: Use document.activeElement to detect browser-focused node ──
3477
- // After renderVirtualItems() restores focus, the browser-focused element is
3478
- // the authoritative source, not getFocusedNode(). If they don't match, it means
3479
- // renderVirtualItems already correctly set up the element, so we must not
3480
- // override its setup by calling setFocus again.
3481
3392
  var actuallyFocusedElement = document.activeElement;
3482
3393
  var isAlreadyFocusedInVirtualMode = this.enableVirtualization
3483
3394
  && actuallyFocusedElement === focusedElement
@@ -3732,12 +3643,6 @@ var TreeView = /** @class */ (function (_super) {
3732
3643
  else {
3733
3644
  txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
3734
3645
  }
3735
- // ── Virtual mode: sync the flat-node snapshot so re-renders show updated text ──
3736
- // virtualFlatNodes is rebuilt from this.treeData (via flattenVisibleNodes) only on
3737
- // expand/collapse/scroll. Between those events the snapshot is the single source of
3738
- // truth used by createVirtualListItem. setValue() above updates this.editData (which
3739
- // IS the treeData object reference), but virtualFlatNodes[idx].text still holds the
3740
- // old value. Update it here so the next renderVirtualItems() call shows the new text.
3741
3646
  if (this.enableVirtualization) {
3742
3647
  var id = liEle.getAttribute('data-uid');
3743
3648
  var idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
@@ -4185,11 +4090,49 @@ var TreeView = /** @class */ (function (_super) {
4185
4090
  this.fields.dataSource = this.treeData;
4186
4091
  }
4187
4092
  this.triggerEvent('nodeDropped', nodeData);
4093
+ if (this.enableVirtualization && !isNOU(this.height)) {
4094
+ this.setRootData();
4095
+ this.rebuildVirtualCheckCache();
4096
+ this.virtualFlatNodes = this.flattenVisibleNodes();
4097
+ this.rebuildVirtualNodeMap();
4098
+ var scrollTopSnapshot = this.element.scrollTop;
4099
+ var win = this.getVirtualWindowAt(scrollTopSnapshot);
4100
+ this.virtualStartIndex = win.startIndex;
4101
+ this.virtualEndIndex = win.endIndex;
4102
+ this.virtualSkipNextScrollRender = true;
4103
+ this.renderVirtualItems();
4104
+ }
4188
4105
  };
4189
4106
  TreeView.prototype.appendNode = function (dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
4190
4107
  var checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
4191
4108
  var collapse = closest(e.target, '.' + COLLAPSIBLE);
4192
4109
  var expand = closest(e.target, '.' + EXPANDABLE);
4110
+ if (this.enableVirtualization && !isNOU(this.height)) {
4111
+ var routesToSiblingBranch = !dragLi.classList.contains('e-disable') && !checkWrapper &&
4112
+ ((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) ||
4113
+ (expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) ||
4114
+ (!expand && !collapse));
4115
+ if (routesToSiblingBranch) {
4116
+ if (dropTarget.nodeName === 'LI') {
4117
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4118
+ }
4119
+ else if (dropTarget.firstElementChild && dropTarget.classList.contains(ROOT)) {
4120
+ if (dropTarget.firstElementChild.nodeName === 'UL') {
4121
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4122
+ }
4123
+ }
4124
+ else if ((dropTarget.classList.contains('e-icon-collapsible')) || (dropTarget.classList.contains('e-icon-expandable'))) {
4125
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4126
+ }
4127
+ else {
4128
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
4129
+ }
4130
+ }
4131
+ else {
4132
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
4133
+ }
4134
+ return;
4135
+ }
4193
4136
  if (!dragLi.classList.contains('e-disable') && !checkWrapper && ((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) || (expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) || (!expand && !collapse))) {
4194
4137
  if (dropTarget.nodeName === 'LI') {
4195
4138
  this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
@@ -4213,6 +4156,62 @@ var TreeView = /** @class */ (function (_super) {
4213
4156
  this.ensureIndeterminate();
4214
4157
  }
4215
4158
  };
4159
+ TreeView.prototype.virtualMoveAsSibling = function (dragLi, dropLi, e, dragObj) {
4160
+ var dropUl = closest(dropLi, '.' + PARENTITEM);
4161
+ var pre;
4162
+ if (e.target.offsetHeight > 0 && e.event.offsetY > e.target.offsetHeight - 2) {
4163
+ pre = false;
4164
+ }
4165
+ else if (e.event.offsetY < 2) {
4166
+ pre = true;
4167
+ }
4168
+ else if (e.target.classList.contains('e-icon-expandable') || (e.target.classList.contains('e-icon-collapsible'))) {
4169
+ if ((e.event.offsetY < 5) || (e.event.offsetX < 3)) {
4170
+ pre = true;
4171
+ }
4172
+ else if ((e.event.offsetY > 15) || (e.event.offsetX > 17)) {
4173
+ pre = false;
4174
+ }
4175
+ }
4176
+ this.moveData(dragLi, dropLi, dropUl, pre, dragObj);
4177
+ };
4178
+ TreeView.prototype.virtualMoveAsChild = function (dragLi, dropLi, dragObj, offsetY, dropTarget) {
4179
+ var pos = offsetY;
4180
+ var isSiblingMarker = dropTarget.classList.contains('e-sibling');
4181
+ var refLi = (pos < 7 && !isSiblingMarker) || (isSiblingMarker &&
4182
+ dropLi.lastChild && !dropLi.lastChild.classList.contains('e-sibling'))
4183
+ ? dropLi : null;
4184
+ var dropTargetHt = dropTarget;
4185
+ var isAfter = (dropTargetHt.offsetHeight > 0 && pos > dropTargetHt.offsetHeight - 10);
4186
+ if (refLi) {
4187
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
4188
+ this.moveData(dragLi, dropLi, dropParentUl, true, dragObj);
4189
+ }
4190
+ else if (isAfter) {
4191
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
4192
+ this.moveData(dragLi, dropLi, dropParentUl, false, dragObj);
4193
+ }
4194
+ else {
4195
+ var dropIcon = select('div.' + EXPANDABLE + ', div.' + COLLAPSIBLE, dropLi);
4196
+ var dropIsCollapsedParent = dropIcon && dropIcon.classList.contains(EXPANDABLE);
4197
+ var dropIsLeaf = isNOU(dropIcon);
4198
+ if (this.preventExpand !== true && (dropIsCollapsedParent || dropIsLeaf)) {
4199
+ var dropLiId = dropLi.getAttribute('data-uid');
4200
+ if (dropLiId && this.expandedNodes.indexOf(dropLiId) === -1) {
4201
+ this.expandedNodes.push(dropLiId);
4202
+ }
4203
+ if (this.virtualExpandedNodesSet && dropLiId) {
4204
+ this.virtualExpandedNodesSet.add(dropLiId);
4205
+ }
4206
+ var dropRecord = this.findNodeInTreeData(dropLiId);
4207
+ if (dropRecord && this.fields && this.fields.expanded) {
4208
+ setValue(this.fields.expanded, true, dropRecord);
4209
+ }
4210
+ }
4211
+ var dropUl = select('.' + PARENTITEM, dropLi) || dropLi;
4212
+ this.moveData(dragLi, null, dropUl, true, dragObj);
4213
+ }
4214
+ };
4216
4215
  TreeView.prototype.dropAsSiblingNode = function (dragLi, dropLi, e, dragObj) {
4217
4216
  var dropUl = closest(dropLi, '.' + PARENTITEM);
4218
4217
  var dragParentUl = closest(dragLi, '.' + PARENTITEM);
@@ -4300,7 +4299,7 @@ var TreeView = /** @class */ (function (_super) {
4300
4299
  delete removedData[0][this.fields.parentID];
4301
4300
  }
4302
4301
  else {
4303
- var currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
4302
+ var currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
4304
4303
  setValue(this.fields.parentID, currPid, removedData[0]);
4305
4304
  }
4306
4305
  this.treeData.splice(pos, 0, removedData[0]);
@@ -4442,7 +4441,7 @@ var TreeView = /** @class */ (function (_super) {
4442
4441
  var draggedNodeData = obj ? obj.dragData : null;
4443
4442
  var newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
4444
4443
  var dragLiParent = obj.dragLi.parentElement;
4445
- var dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
4444
+ var dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
4446
4445
  var targetParent = null;
4447
4446
  var indexValue = null;
4448
4447
  var iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
@@ -4451,7 +4450,7 @@ var TreeView = /** @class */ (function (_super) {
4451
4450
  var index = node ? closest(node, '.e-list-parent') : null;
4452
4451
  var i = 0;
4453
4452
  var position = null;
4454
- dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
4453
+ dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
4455
4454
  dragParent = (drop === true) ? this.dragParent : dragParent;
4456
4455
  if (cloneEle) {
4457
4456
  while (i < 4) {
@@ -5125,26 +5124,10 @@ var TreeView = /** @class */ (function (_super) {
5125
5124
  TreeView.prototype.keyDownHandler = function (e) {
5126
5125
  if (e.key === 'Tab' || e.keyCode === 9) {
5127
5126
  this.isKeyboardFocus = true;
5128
- // Mark the upcoming scroll event (if any) as Tab-induced so the
5129
- // virtual scroll handler skips its re-render path. The browser
5130
- // calls `Element.scrollIntoView` semantics on focus, which scrolls
5131
- // a `<li tabindex="0">` in the middle of the tree into view and
5132
- // therefore fires `scroll` on the container. Without this guard
5133
- // the scroll handler would tear down the focused `<li>` and move
5134
- // focus elsewhere.
5135
5127
  if (this.enableVirtualization) {
5136
5128
  this.virtualSkipNextScrollRender = true;
5137
5129
  }
5138
5130
  }
5139
- // ── Fix: Also skip scroll render for arrow key navigation ──────────────────
5140
- // Arrow Down/Up calls focusNextNode() which calls ensureVirtualNodeVisible()
5141
- // to scroll the target node into view. Without this guard, the scroll event
5142
- // would trigger onVirtualScroll() which re-renders the window and resets the
5143
- // focus that focusNextNode() just set. This manifests as tabindex resetting
5144
- // to the first item instead of staying on the arrow-navigated target.
5145
- // Arrow Right/Left (expand/collapse) also calls renderVirtualItems() which
5146
- // calls li.focus() in branch 2; that can trigger a scroll on an edge node,
5147
- // so we consume it here to prevent a stale RAF re-render.
5148
5131
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
5149
5132
  || e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
5150
5133
  if (this.enableVirtualization) {
@@ -5506,10 +5489,6 @@ var TreeView = /** @class */ (function (_super) {
5506
5489
  if (this.dataType === 1) {
5507
5490
  this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
5508
5491
  }
5509
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
5510
- // after deletion so the removed nodes are correctly reflected in the hierarchical
5511
- // virtual DOM. When height is not provided, the tree uses normal rendering and
5512
- // the DOM is already updated by removeNode() above.
5513
5492
  if (this.enableVirtualization && !isNOU(this.height)) {
5514
5493
  this.virtualFlatNodes = this.flattenVisibleNodes();
5515
5494
  this.rebuildVirtualNodeMap();
@@ -5578,16 +5557,6 @@ var TreeView = /** @class */ (function (_super) {
5578
5557
  }
5579
5558
  return parentsID;
5580
5559
  };
5581
- // ═══════════════════════════════════════════════════════════════════════════════
5582
- // VIRTUALIZATION — private helpers
5583
- // ═══════════════════════════════════════════════════════════════════════════════
5584
- /**
5585
- * Parses the `height` property (string like '400px' or number) to a pixel value.
5586
- * Returns 0 if the value cannot be determined.
5587
- *
5588
- * @returns {number} Pixel height.
5589
- * @private
5590
- */
5591
5560
  TreeView.prototype.parseHeightToPixels = function () {
5592
5561
  var h = this.height;
5593
5562
  if (isNOU(h)) {
@@ -5599,13 +5568,6 @@ var TreeView = /** @class */ (function (_super) {
5599
5568
  var parsed = parseFloat(h);
5600
5569
  return isNaN(parsed) ? 0 : parsed;
5601
5570
  };
5602
- /**
5603
- * Measures the height of the first rendered list item so subsequent calculations
5604
- * use real layout values. Falls back to the stored `virtualItemHeight` default (40).
5605
- *
5606
- * @returns {number} Row height in pixels.
5607
- * @private
5608
- */
5609
5571
  TreeView.prototype.measureVirtualItemHeight = function () {
5610
5572
  var firstLi = select('.' + LISTITEM, this.element);
5611
5573
  if (firstLi && firstLi.offsetHeight > 0) {
@@ -5613,13 +5575,6 @@ var TreeView = /** @class */ (function (_super) {
5613
5575
  }
5614
5576
  return this.virtualItemHeight;
5615
5577
  };
5616
- /**
5617
- * Rebuilds the cached Sets for checked/selected nodes to enable O(1) lookup
5618
- * during the flatten operation instead of O(n) array indexOf calls.
5619
- *
5620
- * @returns {void}
5621
- * @private
5622
- */
5623
5578
  TreeView.prototype.rebuildVirtualCheckCache = function () {
5624
5579
  this.virtualExpandedNodesSet.clear();
5625
5580
  this.virtualCheckedNodesSet.clear();
@@ -5640,20 +5595,10 @@ var TreeView = /** @class */ (function (_super) {
5640
5595
  }
5641
5596
  }
5642
5597
  };
5643
- /**
5644
- * Recursively walks the treeData (or groupedData for flat lists) and builds
5645
- * `virtualFlatNodes` — an ordered list of every node that is currently visible
5646
- * (i.e. its ancestors are all expanded). Uses cached Sets for O(1) state lookups.
5647
- *
5648
- * @returns {VirtualNodeData[]} Ordered flat list of visible nodes.
5649
- * @private
5650
- */
5651
5598
  TreeView.prototype.flattenVisibleNodes = function () {
5652
- // Build cached Sets for fast lookups
5653
5599
  this.rebuildVirtualCheckCache();
5654
5600
  var result = [];
5655
5601
  if (this.dataType === 1) {
5656
- // Flat data with parentID field — use getChildGroup to find root nodes
5657
5602
  var rootNodes = [];
5658
5603
  if (this.groupedData) {
5659
5604
  rootNodes = this.getChildGroup(this.groupedData, null, true);
@@ -5661,40 +5606,29 @@ var TreeView = /** @class */ (function (_super) {
5661
5606
  if (!rootNodes || rootNodes.length === 0) {
5662
5607
  rootNodes = this.rootData;
5663
5608
  }
5664
- // Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
5665
5609
  rootNodes = this.getSortedData(rootNodes);
5666
5610
  this.flattenNodeRecursive(rootNodes, 1, null, result);
5667
5611
  }
5668
5612
  else {
5669
- // Hierarchical data
5670
5613
  var sorted = this.getSortedData(this.rootData);
5671
5614
  this.flattenNodeRecursive(sorted, 1, null, result);
5672
5615
  }
5673
5616
  return result;
5674
5617
  };
5675
- /**
5676
- * Recursive helper for `flattenVisibleNodes`.
5677
- *
5678
- * @param {Object[]} nodes - Array of raw data objects at this level.
5679
- * @param {number} level - 1-based depth level.
5680
- * @param {string | null} parentId - The parent node id, or null for root.
5681
- * @param {VirtualNodeData[]} result - Accumulator array.
5682
- * @returns {void}
5683
- * @private
5684
- */
5685
5618
  TreeView.prototype.flattenNodeRecursive = function (nodes, level, parentId, result) {
5686
5619
  if (!nodes || nodes.length === 0) {
5687
5620
  return;
5688
5621
  }
5689
- // Cache field names and settings outside the loop to avoid repeated conversions
5690
- var childFieldName = this.fields.child.toString();
5691
- var expandedFieldName = this.fields.expanded;
5692
- var isCheckedFieldName = this.fields.isChecked;
5693
- var iconCssFieldName = this.fields.iconCss;
5694
- var imageUrlFieldName = this.fields.imageUrl;
5695
- var idFieldName = this.fields.id;
5696
- var textFieldName = this.fields.text;
5697
- var selectedFieldName = this.fields.selected;
5622
+ var levelMapper = level === 1 ? this.fields : this.getChildFields(this.fields, level, 1);
5623
+ var childFieldName = levelMapper.child && levelMapper.child.toString();
5624
+ var expandedFieldName = levelMapper.expanded;
5625
+ var isCheckedFieldName = levelMapper.isChecked;
5626
+ var iconCssFieldName = levelMapper.iconCss;
5627
+ var imageUrlFieldName = levelMapper.imageUrl;
5628
+ var idFieldName = levelMapper.id;
5629
+ var textFieldName = levelMapper.text;
5630
+ var selectedFieldName = levelMapper.selected;
5631
+ var hasChildrenFieldName = levelMapper.hasChildren;
5698
5632
  var needsSorting = this.sortOrder !== 'None';
5699
5633
  var isDataTypeFlat = this.dataType === 1;
5700
5634
  var enableVirtualization = this.enableVirtualization;
@@ -5702,36 +5636,57 @@ var TreeView = /** @class */ (function (_super) {
5702
5636
  var raw = nodes[i];
5703
5637
  var id = isNOU(raw[idFieldName]) ? '' : raw[idFieldName].toString();
5704
5638
  var text = isNOU(raw[textFieldName]) ? '' : raw[textFieldName].toString();
5705
- // Use expandedNodes as the single source of truth.
5706
5639
  var inExpandedNodes = this.virtualExpandedNodesSet.has(id);
5707
- var rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
5640
+ var rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
5708
5641
  var expanded = inExpandedNodes || rawExpanded;
5709
5642
  if (rawExpanded && id) {
5710
5643
  this.expandedNodes.push(id);
5711
5644
  this.virtualExpandedNodesSet.add(id);
5712
5645
  }
5713
- // Inline hasChildRecords check to avoid function call (optimization)
5714
5646
  var hasChildren = false;
5715
- if (isDataTypeFlat) {
5647
+ var explicitHasChildren = hasChildrenFieldName ? getValue(hasChildrenFieldName, raw) : null;
5648
+ if (levelMapper.dataSource instanceof DataManager && !isNOU(explicitHasChildren) && explicitHasChildren) {
5649
+ hasChildren = true;
5650
+ }
5651
+ else if (isDataTypeFlat) {
5716
5652
  if (this.groupedData) {
5717
5653
  var groupedChild = this.getChildGroup(this.groupedData, id, false);
5718
5654
  hasChildren = !isNOU(groupedChild) && groupedChild.length > 0;
5719
5655
  }
5720
5656
  }
5721
5657
  else {
5722
- var childrenObj = getValue(childFieldName, raw);
5658
+ var childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
5723
5659
  hasChildren = !isNOU(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
5724
5660
  }
5725
- // Virtual-mode only: honour fields.isChecked === true on initial render
5726
- if (enableVirtualization && id && isCheckedFieldName && !this.virtualCheckedNodesSet.has(id)) {
5727
- var rawChecked = getValue(isCheckedFieldName, raw);
5728
- if (!isNOU(rawChecked) && rawChecked.toString() === 'true') {
5661
+ var parentChecked = null;
5662
+ if (levelMapper.dataSource instanceof DataManager && level > 1 && this.autoCheck && parentId) {
5663
+ if (this.virtualCheckedNodesSet.has(parentId) ||
5664
+ (this.checkedNodes && this.checkedNodes.indexOf(parentId) !== -1)) {
5665
+ parentChecked = true;
5666
+ }
5667
+ else {
5668
+ parentChecked = false;
5669
+ }
5670
+ }
5671
+ var rawChecked = isCheckedFieldName && getValue(isCheckedFieldName, raw);
5672
+ if (enableVirtualization && id && !this.virtualCheckedNodesSet.has(id)) {
5673
+ if ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'true') ||
5674
+ (levelMapper.dataSource instanceof DataManager && parentChecked === true)) {
5729
5675
  this.virtualCheckedNodesSet.add(id);
5730
5676
  if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
5731
5677
  this.checkedNodes.push(id);
5732
5678
  }
5733
5679
  }
5734
5680
  }
5681
+ else if (enableVirtualization && id && this.virtualCheckedNodesSet.has(id) && levelMapper.dataSource instanceof DataManager) {
5682
+ if (level > 1 && !this.virtualIndeterminateNodesSet.has(parentId) && this.autoCheck &&
5683
+ ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'false') || parentChecked === false)) {
5684
+ this.virtualCheckedNodesSet.delete(id);
5685
+ if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
5686
+ this.checkedNodes.splice(this.checkedNodes.indexOf(id), 1);
5687
+ }
5688
+ }
5689
+ }
5735
5690
  if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
5736
5691
  var rawSelected = getValue(selectedFieldName, raw);
5737
5692
  if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
@@ -5741,10 +5696,7 @@ var TreeView = /** @class */ (function (_super) {
5741
5696
  }
5742
5697
  }
5743
5698
  }
5744
- // Use cached Sets for O(1) lookup
5745
5699
  var selected = this.virtualSelectedNodesSet.has(id);
5746
- // Resolve checkbox state: indeterminate takes precedence over the checked set,
5747
- // because a node can be in the indeterminate state without being in checkedNodes.
5748
5700
  var isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
5749
5701
  ? 'indeterminate'
5750
5702
  : this.virtualCheckedNodesSet.has(id);
@@ -5758,22 +5710,19 @@ var TreeView = /** @class */ (function (_super) {
5758
5710
  };
5759
5711
  result.push(node);
5760
5712
  if (expanded && hasChildren) {
5761
- // Determine children list
5762
5713
  var children = [];
5763
5714
  if (isDataTypeFlat) {
5764
5715
  if (this.groupedData) {
5765
5716
  children = this.getChildGroup(this.groupedData, id, false);
5766
- // Apply sortOrder to flat-data children (same as hierarchical path)
5767
5717
  if (needsSorting) {
5768
5718
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
5769
5719
  }
5770
5720
  }
5771
5721
  }
5772
5722
  else {
5773
- var rawChildren = getValue(childFieldName, raw);
5723
+ var rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
5774
5724
  if (!isNOU(rawChildren) && Array.isArray(rawChildren)) {
5775
5725
  children = rawChildren;
5776
- // Only sort if needed
5777
5726
  if (needsSorting) {
5778
5727
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
5779
5728
  }
@@ -5783,35 +5732,6 @@ var TreeView = /** @class */ (function (_super) {
5783
5732
  }
5784
5733
  }
5785
5734
  };
5786
- /**
5787
- * Checks whether a raw datasource node has child records.
5788
- *
5789
- * @param {Object} raw - Raw datasource object.
5790
- * @returns {boolean} True if children exist.
5791
- * @private
5792
- */
5793
- TreeView.prototype.hasChildRecords = function (raw) {
5794
- if (this.dataType === 1) {
5795
- if (!this.groupedData) {
5796
- return false;
5797
- }
5798
- var rawId = raw[this.fields.id];
5799
- if (isNOU(rawId)) {
5800
- return false;
5801
- }
5802
- var groupedChild = this.getChildGroup(this.groupedData, rawId.toString(), false);
5803
- return !isNOU(groupedChild) && groupedChild.length > 0;
5804
- }
5805
- var childField = this.fields.child.toString();
5806
- var children = getValue(childField, raw);
5807
- return !isNOU(children) && Array.isArray(children) && children.length > 0;
5808
- };
5809
- /**
5810
- * Rebuilds `virtualNodeMap` (id → index) from the current `virtualFlatNodes`.
5811
- *
5812
- * @returns {void}
5813
- * @private
5814
- */
5815
5735
  TreeView.prototype.rebuildVirtualNodeMap = function () {
5816
5736
  this.virtualNodeMap.clear();
5817
5737
  for (var i = 0; i < this.virtualFlatNodes.length; i++) {
@@ -5844,49 +5764,12 @@ var TreeView = /** @class */ (function (_super) {
5844
5764
  var paddingBottom = Math.max(0, (total - end) * itemH);
5845
5765
  return { paddingTop: paddingTop, paddingBottom: paddingBottom };
5846
5766
  };
5847
- /**
5848
- * Renders the virtual window into a **nested** `<ul>/<li>` hierarchy identical to
5849
- * the non-virtualized DOM structure.
5850
- *
5851
- * ## Scroll math
5852
- * The flat-index ↔ pixel mapping stays unchanged: every entry in
5853
- * `virtualFlatNodes` represents exactly one `itemHeight` row.
5854
- * `paddingTop = startIndex * itemHeight` and
5855
- * `paddingBottom = (total - endIndex) * itemHeight` keep the scrollbar
5856
- * thumb in the correct position.
5857
- *
5858
- * ## Ancestor injection
5859
- * When `startIndex` points at a child node whose ancestor(s) have already
5860
- * scrolled above the viewport (their flat indices are < startIndex), the
5861
- * ancestors would normally be missing from the DOM, forcing the children to
5862
- * fall back to the root `<ul>` as flat siblings — producing wrong HTML.
5863
- *
5864
- * To fix this, before rendering the window we walk the ancestor chain of the
5865
- * first visible node and collect every ancestor that lies **outside** the
5866
- * window. Those ancestors are injected as real `<li>` containers at the
5867
- * very top of the root `<ul>`. To compensate for their presence, `paddingTop`
5868
- * is reduced by `injectedCount * itemHeight` so the total scroll height
5869
- * remains the same.
5870
- *
5871
- * Once the ancestors are in the DOM they are registered in `renderedParentMap`
5872
- * so the normal window-rendering loop can nest children inside them exactly
5873
- * as it does when both parent and child are in the same window.
5874
- *
5875
- * @returns {void}
5876
- * @private
5877
- */
5878
5767
  TreeView.prototype.renderVirtualItems = function () {
5879
5768
  if (!this.ulElement) {
5880
5769
  return;
5881
5770
  }
5882
5771
  var ul = this.ulElement;
5883
- // ── Snapshot focused ID BEFORE clearing the DOM ───────────────────────────
5884
- // Removing a focused <li> from the DOM triggers a synchronous `blur` event,
5885
- // which calls focusOut() and sets virtualFocusedId = null. Capturing the id
5886
- // here (before any removeChild) ensures focus is correctly restored to the
5887
- // same node after re-render even when keyboard navigation was in progress.
5888
5772
  var focusedUid = this.virtualFocusedId || null;
5889
- // ── Clear previous render (direct children only) ─────────────────────────
5890
5773
  var existingItems = ul.querySelectorAll(':scope > li');
5891
5774
  for (var i = existingItems.length - 1; i >= 0; i--) {
5892
5775
  ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
@@ -5897,26 +5780,22 @@ var TreeView = /** @class */ (function (_super) {
5897
5780
  if (this.virtualStartIndex < this.virtualFlatNodes.length) {
5898
5781
  var firstNode = this.virtualFlatNodes[this.virtualStartIndex];
5899
5782
  if (firstNode.parentId) {
5900
- // Build the ancestor chain from root down to the immediate parent
5901
5783
  var ancestors = [];
5902
5784
  var searchId = firstNode.parentId;
5903
5785
  while (searchId) {
5904
5786
  var ancestorIdx = this.virtualNodeMap.get(searchId);
5905
5787
  if (!isNOU(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
5906
5788
  var ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
5907
- ancestors.unshift(ancestor); // prepend so root comes first
5789
+ ancestors.unshift(ancestor);
5908
5790
  searchId = ancestor.parentId;
5909
5791
  }
5910
5792
  else {
5911
5793
  break;
5912
5794
  }
5913
5795
  }
5914
- // Inject each ancestor as a real <li> into the root <ul>
5915
- // and register it in renderedParentMap for child-nesting below.
5916
5796
  for (var a = 0; a < ancestors.length; a++) {
5917
5797
  var ancestor = ancestors[parseInt(a.toString(), 10)];
5918
5798
  var ancestorLi = this.createVirtualListItem(ancestor, false);
5919
- // Find the correct container: root or a previously injected ancestor's <ul>
5920
5799
  var targetUl = ul;
5921
5800
  if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
5922
5801
  var pe = renderedParentMap.get(ancestor.parentId);
@@ -5935,26 +5814,20 @@ var TreeView = /** @class */ (function (_super) {
5935
5814
  }
5936
5815
  }
5937
5816
  }
5938
- // ── Scroll-space padding (adjusted for injected ancestors) ────────────────
5939
5817
  var total = this.virtualFlatNodes.length;
5940
5818
  var start = this.virtualStartIndex;
5941
5819
  var end = this.virtualEndIndex;
5942
5820
  var rawPaddingTop = start * itemH;
5943
- // Subtract the height of injected ancestors so the scroll position is
5944
- // exact: those ancestor rows are now real DOM elements, not hidden space.
5945
5821
  var adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
5946
5822
  ul.style.paddingTop = adjustedPaddingTop + 'px';
5947
5823
  ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
5948
- // ── Render window items (nested via renderedParentMap) ────────────────────
5949
5824
  var liListBuf = [];
5950
5825
  for (var i = start; i < end; i++) {
5951
5826
  var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
5952
5827
  var li = this.createVirtualListItem(node, i === start);
5953
- // Find the correct parent container
5954
5828
  var targetUl = ul;
5955
5829
  if (node.parentId && renderedParentMap.has(node.parentId)) {
5956
5830
  var parentEntry = renderedParentMap.get(node.parentId);
5957
- // Lazily create the child <ul> the first time a child needs it
5958
5831
  if (!parentEntry.childUl) {
5959
5832
  parentEntry.childUl = this.createElement('ul', {
5960
5833
  className: PARENTITEM + ' e-ul',
@@ -5966,30 +5839,11 @@ var TreeView = /** @class */ (function (_super) {
5966
5839
  }
5967
5840
  targetUl.appendChild(li);
5968
5841
  liListBuf.push(li);
5969
- // Register this node so its children in the same window can nest under it
5970
5842
  if (node.expanded && node.hasChildren) {
5971
5843
  renderedParentMap.set(node.id, { li: li, childUl: null });
5972
5844
  }
5973
5845
  }
5974
- // ── Restore focus / tabindex ──────────────────────────────────────────────
5975
- // In virtual mode only the li that carries the 'e-active' class should be
5976
- // focusable (tabindex='0'). All other rendered li elements must be set
5977
- // to tabindex='-1' so the tab order remains unambiguous as the window
5978
- // changes during scrolling.
5979
5846
  var focusRestored = false;
5980
- // 1. Prefer the currently active node if it is in the rendered window.
5981
- for (var k = 0; k < liListBuf.length; k++) {
5982
- var li = liListBuf[parseInt(k.toString(), 10)];
5983
- if (li.classList.contains(ACTIVE)) {
5984
- li.setAttribute('tabindex', '0');
5985
- // ── Fix: ensure FOCUS class set on active node too ──
5986
- // This ensures focusIn() guard condition is met: tabindex='0' && FOCUS class
5987
- addClass([li], FOCUS);
5988
- focusRestored = true;
5989
- break;
5990
- }
5991
- }
5992
- // 2. Otherwise fall back to the previously focused node (if it is in window).
5993
5847
  if (!focusRestored && focusedUid) {
5994
5848
  for (var k = 0; k < liListBuf.length; k++) {
5995
5849
  var li = liListBuf[parseInt(k.toString(), 10)];
@@ -5997,22 +5851,6 @@ var TreeView = /** @class */ (function (_super) {
5997
5851
  li.setAttribute('tabindex', '0');
5998
5852
  addClass([li], FOCUS);
5999
5853
  addClass([li], FOCUSED);
6000
- // ── Fix: restore actual browser focus so keyboard events keep working ──
6001
- // After renderVirtualItems() replaces all <li> elements the old element
6002
- // is gone from the DOM and document.activeElement falls back to
6003
- // document.body. Setting tabindex + classes is not enough — the new
6004
- // element also needs a programmatic .focus() call so subsequent keyboard
6005
- // events (Arrow keys, Home, End, etc.) are delivered to the TreeView.
6006
- // Re-attach the blur→focusOut handler so virtualFocusedId is cleared
6007
- // correctly when the user tabs away.
6008
- // ── Use preventScroll:true ────────────────────────────────────────────
6009
- // Without preventScroll the browser scrolls the container to bring this
6010
- // node into view during the re-render, changing scrollTop AFTER the
6011
- // correct paddingTop has been applied. This is the root cause of the
6012
- // KBN expand position-jump bug: the render is correct but the browser
6013
- // silently scrolls the viewport, making the parent node appear to jump.
6014
- // ensureVirtualNodeVisible() handles any legitimate scroll-into-view
6015
- // needs before renderVirtualItems() is called, so scroll here is wrong.
6016
5854
  EventHandler.add(li, 'blur', this.focusOut, this);
6017
5855
  li.focus({ preventScroll: true });
6018
5856
  focusRestored = true;
@@ -6020,53 +5858,36 @@ var TreeView = /** @class */ (function (_super) {
6020
5858
  }
6021
5859
  }
6022
5860
  }
6023
- // 3. Finally default to the first item in the window.
6024
5861
  if (!focusRestored && liListBuf.length > 0) {
6025
5862
  var firstLi = liListBuf[0];
6026
5863
  firstLi.setAttribute('tabindex', '0');
6027
5864
  }
6028
- // ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
6029
- // This prevents stale tabindex="0" on multiple nodes which breaks Tab order
6030
5865
  for (var k = 0; k < liListBuf.length; k++) {
6031
5866
  var li = liListBuf[parseInt(k.toString(), 10)];
6032
5867
  if (li.getAttribute('tabindex') !== '0') {
6033
5868
  li.setAttribute('tabindex', '-1');
6034
5869
  }
6035
5870
  }
6036
- // Keep liList in sync so keyboard-navigation helpers stay functional.
6037
5871
  this.liList = liListBuf;
6038
5872
  };
6039
- /**
6040
- * Creates a single `<li>` element for the virtual window representing `node`.
6041
- *
6042
- * @param {VirtualNodeData} node - The node data.
6043
- * @param {boolean} _isFirst - True if this is the first rendered item (unused here but kept for potential override).
6044
- * @returns {HTMLElement} The constructed `<li>` element.
6045
- * @private
6046
- */
6047
5873
  TreeView.prototype.createVirtualListItem = function (node, _isFirst) {
6048
5874
  var li = this.createElement('li', { className: LISTITEM });
6049
- // Level class for CSS indentation
6050
5875
  li.classList.add('e-level-' + node.level);
6051
5876
  li.setAttribute('data-uid', node.id);
6052
5877
  li.setAttribute('aria-level', node.level.toString());
6053
5878
  li.setAttribute('role', 'treeitem');
6054
5879
  li.setAttribute('tabindex', '-1');
6055
- // Full-row overlay (required for fullRowSelect mode)
6056
5880
  if (this.fullRowSelect) {
6057
5881
  var fullRow = this.createElement('div', { className: FULLROW });
6058
5882
  li.appendChild(fullRow);
6059
5883
  }
6060
- // Text/icon wrapper
6061
5884
  var textWrap = this.createElement('div', { className: TEXTWRAP });
6062
- // Expand/collapse icon
6063
5885
  if (node.hasChildren) {
6064
5886
  var iconSpan = this.createElement('div', {
6065
5887
  className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
6066
5888
  });
6067
5889
  textWrap.appendChild(iconSpan);
6068
5890
  }
6069
- // Checkbox
6070
5891
  if (this.showCheckBox) {
6071
5892
  var checkWrap = createCheckBox(this.createElement, false, {
6072
5893
  checked: node.isChecked === true,
@@ -6080,28 +5901,24 @@ var TreeView = /** @class */ (function (_super) {
6080
5901
  }
6081
5902
  textWrap.appendChild(checkWrap);
6082
5903
  }
6083
- // Icon
6084
5904
  if (node.iconCss) {
6085
5905
  var iconEl = this.createElement('span', {
6086
5906
  className: ICON + ' ' + node.iconCss
6087
5907
  });
6088
5908
  textWrap.appendChild(iconEl);
6089
5909
  }
6090
- // Image
6091
5910
  if (node.imageUrl) {
6092
5911
  var img = document.createElement('img');
6093
5912
  img.src = node.imageUrl;
6094
5913
  img.classList.add(IMAGE);
6095
5914
  textWrap.appendChild(img);
6096
5915
  }
6097
- // Text anchor
6098
5916
  var anchor = this.createElement('span', {
6099
5917
  className: 'e-list-text',
6100
5918
  innerHTML: node.text
6101
5919
  });
6102
5920
  textWrap.appendChild(anchor);
6103
5921
  li.appendChild(textWrap);
6104
- // nodeTemplate
6105
5922
  if (!isNOU(this.nodeTemplateFn)) {
6106
5923
  var rawData = this.findNodeInTreeData(node.id);
6107
5924
  if (!isNOU(rawData)) {
@@ -6109,7 +5926,6 @@ var TreeView = /** @class */ (function (_super) {
6109
5926
  this.renderNodeTemplate(rawData, anchor, node.id);
6110
5927
  }
6111
5928
  }
6112
- // State classes
6113
5929
  if (node.expanded) {
6114
5930
  li.classList.add(EXPANDED);
6115
5931
  li.setAttribute('aria-expanded', 'true');
@@ -6125,80 +5941,43 @@ var TreeView = /** @class */ (function (_super) {
6125
5941
  if (node.hasChildren) {
6126
5942
  li.classList.add(CHILD);
6127
5943
  }
6128
- // Wire the focus event so `focusIn()` fires when the user Tabs into this
6129
- // item. The non-virtualized path registers this in `beforeNodeCreate()`,
6130
- // but virtual items never go through that callback, so we must register
6131
- // it here to keep focus styling (e.g. `e-focused`) consistent between
6132
- // virtual and non-virtual modes. `unWireEvents()` already removes this
6133
- // handler during teardown by iterating over the rendered `<li>`s.
6134
5944
  EventHandler.add(li, 'focus', this.focusIn, this);
6135
5945
  return li;
6136
5946
  };
6137
- /**
6138
- * Initialises the virtual-scrolling layout:
6139
- * 1. Adds the `e-virtualization` CSS class and applies a fixed `height` + `overflow-y`.
6140
- * 2. Flattens all visible nodes into `virtualFlatNodes`.
6141
- * 3. Measures item height and viewport height.
6142
- * 4. Computes the initial window and renders it.
6143
- * 5. Wires the scroll event handler.
6144
- *
6145
- * @returns {void}
6146
- * @private
6147
- */
6148
5947
  TreeView.prototype.initVirtualization = function () {
6149
5948
  addClass([this.element], VIRTUALIZATION);
6150
- // Apply container height
6151
5949
  var px = this.parseHeightToPixels();
6152
5950
  if (px > 0) {
6153
5951
  this.element.style.height = px + 'px';
6154
5952
  this.element.style.overflowY = 'auto';
6155
5953
  }
6156
- // Ensure the inner UL is ready
6157
5954
  if (!this.ulElement) {
6158
5955
  this.ulElement = select('.' + PARENTITEM, this.element);
6159
5956
  }
6160
5957
  if (!this.ulElement) {
6161
5958
  return;
6162
5959
  }
6163
- // Make the UL position-relative for the absolute spacers
6164
5960
  this.ulElement.style.position = 'relative';
6165
- // Rebuild flat list
6166
5961
  this.virtualFlatNodes = this.flattenVisibleNodes();
6167
5962
  this.rebuildVirtualNodeMap();
6168
- // Measure dimensions
6169
5963
  this.virtualViewportHeight = px > 0 ? px : this.element.offsetHeight;
6170
- // Render a single probe item to measure the real item height before the full render
6171
5964
  if (this.virtualFlatNodes.length > 0) {
6172
5965
  var probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
6173
5966
  this.ulElement.appendChild(probe);
6174
5967
  this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
6175
5968
  this.ulElement.removeChild(probe);
6176
5969
  }
6177
- // Compute window and render all items in the initial viewport
6178
5970
  var win = this.getVirtualWindow();
6179
5971
  this.virtualStartIndex = win.startIndex;
6180
5972
  this.virtualEndIndex = win.endIndex;
6181
5973
  this.renderVirtualItems();
6182
- // Wire scroll
6183
5974
  this.wireVirtualScrollEvents();
6184
5975
  };
6185
- /**
6186
- * Wires the scroll event listener on the TreeView element.
6187
- *
6188
- * @returns {void}
6189
- * @private
6190
- */
6191
5976
  TreeView.prototype.wireVirtualScrollEvents = function () {
6192
5977
  this.unwireVirtualScrollEvents();
6193
5978
  this.virtualScrollBound = this.onVirtualScroll.bind(this);
6194
5979
  this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
6195
5980
  };
6196
- /**
6197
- * Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
6198
- *
6199
- * @returns {void}
6200
- * @private
6201
- */
6202
5981
  TreeView.prototype.unwireVirtualScrollEvents = function () {
6203
5982
  if (this.virtualScrollBound) {
6204
5983
  this.element.removeEventListener('scroll', this.virtualScrollBound);
@@ -6209,29 +5988,13 @@ var TreeView = /** @class */ (function (_super) {
6209
5988
  this.virtualScrollRAF = null;
6210
5989
  }
6211
5990
  };
6212
- /**
6213
- * Scroll handler for the virtual TreeView. Throttled via `requestAnimationFrame`.
6214
- * Shows the loading mask during fast scrolls, re-renders the window, then hides the mask.
6215
- *
6216
- * @returns {void}
6217
- * @private
6218
- */
6219
5991
  TreeView.prototype.onVirtualScroll = function () {
6220
5992
  var _this = this;
6221
5993
  if (this.virtualScrollRAF !== null) {
6222
5994
  cancelAnimationFrame(this.virtualScrollRAF);
6223
5995
  }
6224
- // ── Tab-induced focus guard ────────────────────────────────────────────────
6225
- // When the user Tabs into a `<li tabindex="0">` that is in the middle of
6226
- // the treeview, the browser auto-scrolls the container so the focused
6227
- // element becomes visible. That fires a `scroll` event here, but the
6228
- // virtual window already contains the focused element — re-rendering
6229
- // would destroy the focused `<li>` and break focus. Detect this case
6230
- // via the flag set in `keyDownHandler` and skip the re-render entirely.
6231
5996
  if (this.virtualSkipNextScrollRender) {
6232
5997
  this.virtualSkipNextScrollRender = false;
6233
- // Update prev scrollTop so a subsequent user-driven scroll still
6234
- // calculates the correct delta.
6235
5998
  this.virtualPrevScrollTop = this.element.scrollTop;
6236
5999
  return;
6237
6000
  }
@@ -6248,30 +6011,16 @@ var TreeView = /** @class */ (function (_super) {
6248
6011
  _this.virtualStartIndex = win.startIndex;
6249
6012
  _this.virtualEndIndex = win.endIndex;
6250
6013
  _this.renderVirtualItems();
6251
- // Hide the skeleton mask after a short delay so it stays visible long enough
6252
- // for the user to perceive the loading indicator even on short scrolls.
6253
6014
  setTimeout(function () {
6254
6015
  _this.hideVirtualMask();
6255
- // Keep the (now hidden) mask's coords fresh for the next show.
6256
6016
  _this.updateMaskPosition();
6257
6017
  }, 200);
6258
6018
  });
6259
6019
  };
6260
- /**
6261
- * Creates the skeleton mask overlay used to indicate loading during fast scrolls.
6262
- * Skeleton instances are stored in `skeletonInstances` for later disposal.
6263
- *
6264
- * @returns {void}
6265
- * @private
6266
- */
6267
6020
  TreeView.prototype.createVirtualMask = function () {
6268
6021
  if (this.virtualMaskElement) {
6269
6022
  return;
6270
6023
  }
6271
- // Use position:fixed so the mask is anchored to the visible viewport (not to the
6272
- // scrolling container). Width/height/left/top are set/refreshed by
6273
- // updateMaskPosition() so the mask always covers the TreeView's visible area
6274
- // regardless of how the page itself is scrolled.
6275
6024
  var rect = this.element.getBoundingClientRect();
6276
6025
  var scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
6277
6026
  var maskParent = this.createElement('div', {
@@ -6284,20 +6033,6 @@ var TreeView = /** @class */ (function (_super) {
6284
6033
  className: VIRTUAL_MASK_ITEM,
6285
6034
  styles: "display:flex;align-items:center;height:" + this.virtualItemHeight + "px;padding:0 12px;gap:8px;"
6286
6035
  });
6287
- // Skeleton circle for icon placeholder
6288
- // const iconHolder: HTMLElement = this.createElement('span', {
6289
- // styles: `width:20px;height:20px;`
6290
- // });
6291
- // maskItem.appendChild(iconHolder);
6292
- // const iconSkel: Skeleton = new Skeleton({
6293
- // shape: 'Circle',
6294
- // width: '20px',
6295
- // height: '20px',
6296
- // cssClass: 'e-virtual-skel-icon'
6297
- // });
6298
- // iconSkel.appendTo(iconHolder);
6299
- // this.skeletonInstances.push(iconSkel);
6300
- // Skeleton text bar
6301
6036
  var textHolder = this.createElement('span');
6302
6037
  textHolder.style.flex = '1';
6303
6038
  textHolder.style.height = '14px';
@@ -6315,14 +6050,6 @@ var TreeView = /** @class */ (function (_super) {
6315
6050
  this.virtualMaskElement = maskParent;
6316
6051
  this.element.appendChild(maskParent);
6317
6052
  };
6318
- /**
6319
- * Refreshes the mask position so it always overlays the TreeView's viewport.
6320
- * Called on every show / scroll, since `position:fixed` is relative to the
6321
- * page viewport, not to the TreeView scroll container.
6322
- *
6323
- * @returns {void}
6324
- * @private
6325
- */
6326
6053
  TreeView.prototype.updateMaskPosition = function () {
6327
6054
  if (!this.virtualMaskElement) {
6328
6055
  return;
@@ -6334,12 +6061,6 @@ var TreeView = /** @class */ (function (_super) {
6334
6061
  this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
6335
6062
  this.virtualMaskElement.style.height = rect.height + 'px';
6336
6063
  };
6337
- /**
6338
- * Destroys all skeleton instances and removes the mask overlay from the DOM.
6339
- *
6340
- * @returns {void}
6341
- * @private
6342
- */
6343
6064
  TreeView.prototype.destroyVirtualMask = function () {
6344
6065
  for (var i = this.skeletonInstances.length - 1; i >= 0; i--) {
6345
6066
  this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
@@ -6352,52 +6073,26 @@ var TreeView = /** @class */ (function (_super) {
6352
6073
  this.virtualMaskElement = null;
6353
6074
  }
6354
6075
  };
6355
- /**
6356
- * Positions and shows the skeleton mask at the current `scrollTop` position.
6357
- *
6358
- * @param {number} _scrollTop - Current scrollTop of the container.
6359
- * @returns {void}
6360
- * @private
6361
- */
6362
6076
  TreeView.prototype.showVirtualMask = function (_scrollTop) {
6363
6077
  if (!this.virtualMaskElement) {
6364
6078
  this.createVirtualMask();
6365
6079
  }
6366
6080
  if (this.virtualMaskElement) {
6367
- // Refresh the fixed-position coords in case the TreeView was moved/resized
6368
- // since the last time the mask was shown.
6369
6081
  this.updateMaskPosition();
6370
6082
  this.virtualMaskElement.style.display = 'block';
6371
- // Visually hide the real list while the skeleton is shown
6372
6083
  if (this.ulElement) {
6373
6084
  this.ulElement.style.visibility = 'hidden';
6374
6085
  }
6375
6086
  }
6376
6087
  };
6377
- /**
6378
- * Hides the skeleton mask overlay after the virtual window has been rendered.
6379
- *
6380
- * @returns {void}
6381
- * @private
6382
- */
6383
6088
  TreeView.prototype.hideVirtualMask = function () {
6384
6089
  if (this.virtualMaskElement) {
6385
6090
  this.virtualMaskElement.style.display = 'none';
6386
- // Restore real list visibility
6387
6091
  if (this.ulElement) {
6388
6092
  this.ulElement.style.visibility = '';
6389
6093
  }
6390
6094
  }
6391
6095
  };
6392
- /**
6393
- * Ensures the node identified by `id` is scrolled into view inside the virtual container.
6394
- * If the node falls outside the rendered window, the container's `scrollTop` is adjusted so
6395
- * that the node becomes visible, the window is re-computed and `renderVirtualItems` is called.
6396
- *
6397
- * @param {string} id - The `data-uid` of the target node.
6398
- * @returns {void}
6399
- * @private
6400
- */
6401
6096
  TreeView.prototype.ensureVirtualNodeVisible = function (id) {
6402
6097
  var idx = this.virtualNodeMap.get(id);
6403
6098
  if (isNOU(idx)) {
@@ -6416,25 +6111,13 @@ var TreeView = /** @class */ (function (_super) {
6416
6111
  this.element.scrollTop = nodeBottom - vpH;
6417
6112
  }
6418
6113
  else {
6419
- return; // already visible, no re-render needed
6114
+ return;
6420
6115
  }
6421
- // Re-render the new window synchronously so the <li> is in the DOM
6422
6116
  var win = this.getVirtualWindow();
6423
6117
  this.virtualStartIndex = win.startIndex;
6424
6118
  this.virtualEndIndex = win.endIndex;
6425
6119
  this.renderVirtualItems();
6426
6120
  };
6427
- /**
6428
- * Recursively walks the treeData (and any nested `childFieldName` arrays) to
6429
- * find the node whose `idFieldName` matches the given `id` (compared as
6430
- * strings). Used by the virtual-mode expand/collapse handlers so they can
6431
- * locate nodes at any depth — not just the root level — before writing the
6432
- * `expanded` flag back to the datasource.
6433
- *
6434
- * @param {string} id - The id (as a string) of the node to locate.
6435
- * @returns {Object | null} The matching raw datasource node, or null if not found.
6436
- * @private
6437
- */
6438
6121
  TreeView.prototype.findNodeInTreeData = function (id) {
6439
6122
  if (!this.treeData) {
6440
6123
  return null;
@@ -6462,12 +6145,19 @@ var TreeView = /** @class */ (function (_super) {
6462
6145
  };
6463
6146
  return findRecursive(this.treeData);
6464
6147
  };
6148
+ TreeView.prototype.getNodeLevel = function (id) {
6149
+ if (!this.virtualFlatNodes) {
6150
+ return 1;
6151
+ }
6152
+ for (var i = 0; i < this.virtualFlatNodes.length; i++) {
6153
+ if (this.virtualFlatNodes[parseInt(i.toString(), 10)].id === id) {
6154
+ return this.virtualFlatNodes[parseInt(i.toString(), 10)].level;
6155
+ }
6156
+ }
6157
+ return 1;
6158
+ };
6465
6159
  TreeView.prototype.handleVirtualExpand = function (id, isKeyboardNav) {
6466
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
6467
- // A scroll RAF queued before the expand click would run asynchronously after
6468
- // this synchronous expand completes, re-rendering the window from a stale
6469
- // scrollTop and moving the parent node to the wrong position. Cancel it now
6470
- // so the expand render is authoritative.
6160
+ var _this = this;
6471
6161
  if (this.virtualScrollRAF !== null) {
6472
6162
  cancelAnimationFrame(this.virtualScrollRAF);
6473
6163
  this.virtualScrollRAF = null;
@@ -6475,7 +6165,6 @@ var TreeView = /** @class */ (function (_super) {
6475
6165
  if (this.expandedNodes.indexOf(id) === -1) {
6476
6166
  this.expandedNodes.push(id);
6477
6167
  }
6478
- // Also update virtualExpandedNodesSet to keep it in sync
6479
6168
  if (this.virtualExpandedNodesSet) {
6480
6169
  this.virtualExpandedNodesSet.add(id);
6481
6170
  }
@@ -6485,56 +6174,76 @@ var TreeView = /** @class */ (function (_super) {
6485
6174
  setValue(this.fields.expanded, true, acutalRootData);
6486
6175
  }
6487
6176
  }
6488
- // ── Fix: Pin focused ID to the expanding node for keyboard navigation ──
6489
- // When keyboard-triggered, the expanded node IS definitively the focused node
6490
- // (`id` comes from currLi.getAttribute('data-uid') in expandNode, where currLi
6491
- // is getFocusedNode()). Set unconditionally — the old conditional guard
6492
- // `!this.virtualFocusedId` could leave a stale or null value if a previous
6493
- // RAF render (from arrow-key scroll) hadn't yet completed, causing branch 3
6494
- // (first item) to fire in renderVirtualItems() instead of branch 2.
6495
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
6496
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
6497
- // mouse-based expand/collapse should never add the e-focused class.
6498
6177
  if (isKeyboardNav) {
6499
6178
  this.virtualFocusedId = id;
6500
6179
  }
6501
6180
  else {
6502
6181
  this.virtualFocusedId = null;
6503
6182
  }
6183
+ if (this.fields.dataSource instanceof DataManager && !this.isOffline) {
6184
+ var parentNode = this.findNodeInTreeData(id);
6185
+ if (parentNode && !isNOU(parentNode)) {
6186
+ var childFieldName = this.fields.child.toString();
6187
+ var childrenObj = getValue(childFieldName, parentNode);
6188
+ var childrenNotLoaded = isNOU(childrenObj) || !Array.isArray(childrenObj) ||
6189
+ childrenObj.length === 0;
6190
+ if (childrenNotLoaded) {
6191
+ this.loadVirtualChildData(id, parentNode, function () {
6192
+ _this.completeVirtualExpand(id, isKeyboardNav);
6193
+ });
6194
+ return;
6195
+ }
6196
+ }
6197
+ }
6198
+ this.completeVirtualExpand(id, isKeyboardNav);
6199
+ };
6200
+ TreeView.prototype.loadVirtualChildData = function (parentId, parentNode, callback) {
6201
+ var _this = this;
6202
+ var level = this.getNodeLevel(parentId);
6203
+ var mapper = this.getChildFields(this.fields, level + 1, 1);
6204
+ if (isNOU(mapper) || isNOU(mapper.dataSource)) {
6205
+ if (callback) {
6206
+ callback();
6207
+ }
6208
+ return;
6209
+ }
6210
+ mapper.dataSource.executeQuery(this.getQuery(mapper, parentId)).then(function (e) {
6211
+ var childItems = e.result;
6212
+ if (!isNOU(childItems) && childItems.length > 0) {
6213
+ var childFieldName = _this.fields.child.toString();
6214
+ setValue(childFieldName, childItems, parentNode);
6215
+ if (_this.dataType === 2) {
6216
+ var nodeInTree = _this.findNodeInTreeData(parentId);
6217
+ if (nodeInTree) {
6218
+ setValue(childFieldName, childItems, nodeInTree);
6219
+ }
6220
+ }
6221
+ }
6222
+ if (callback) {
6223
+ callback();
6224
+ }
6225
+ }).catch(function (e) {
6226
+ _this.trigger('actionFailure', { error: e });
6227
+ if (callback) {
6228
+ callback();
6229
+ }
6230
+ });
6231
+ };
6232
+ TreeView.prototype.completeVirtualExpand = function (id, isKeyboardNav) {
6504
6233
  this.virtualFlatNodes = this.flattenVisibleNodes();
6505
6234
  this.rebuildVirtualNodeMap();
6506
- // ── Snapshot scrollTop before computing the window ────────────────────────
6507
- // Read scrollTop once here and pass it into getVirtualWindow so that any
6508
- // browser scroll-anchoring adjustment that happens when the padding values
6509
- // change cannot affect which window we render.
6510
6235
  var scrollTopSnapshot = this.element.scrollTop;
6511
6236
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
6512
6237
  this.virtualStartIndex = win.startIndex;
6513
6238
  this.virtualEndIndex = win.endIndex;
6514
- // ── Block ALL scroll-triggered re-renders during this expand ─────────────
6515
- // renderVirtualItems() calls li.focus() in branch 2 (keyboard focus restore).
6516
- // The post-render focus() call below can also trigger browser scroll. Both
6517
- // can fire the 'scroll' event and cause onVirtualScroll() to queue a RAF.
6518
- // We set the skip flag ONCE here; it is consumed by the first scroll event.
6519
- // To cover the second potential scroll (from the focus() call below) we use
6520
- // preventScroll:true on that call so it never moves the container at all.
6521
6239
  this.virtualSkipNextScrollRender = true;
6522
6240
  this.renderVirtualItems();
6523
- // renderVirtualItems() replaces all <li> elements so the old icon element that
6524
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
6525
- // so the node can be collapsed/expanded again without being blocked.
6526
6241
  var newLi = this.getElement(id);
6527
6242
  if (newLi) {
6528
6243
  var icon = select('.' + ICON, newLi);
6529
6244
  if (icon) {
6530
6245
  removeClass([icon], PROCESS);
6531
6246
  }
6532
- // ── Use preventScroll:true ────────────────────────────────────────────
6533
- // Without preventScroll the browser scrolls the container to bring this
6534
- // node into view, which fires a second 'scroll' event. The first scroll
6535
- // was already consumed by virtualSkipNextScrollRender (set above), so the
6536
- // second scroll would queue a new RAF that re-renders from the
6537
- // focus-adjusted scrollTop — moving the parent node to the wrong position.
6538
6247
  newLi.focus({ preventScroll: true });
6539
6248
  }
6540
6249
  if (this.isLoaded) {
@@ -6547,9 +6256,6 @@ var TreeView = /** @class */ (function (_super) {
6547
6256
  }
6548
6257
  };
6549
6258
  TreeView.prototype.handleVirtualCollapse = function (id, isKeyboardNav) {
6550
- // ── Cancel any pending scroll RAF ─────────────────────────────────────────
6551
- // Mirrors handleVirtualExpand: a queued RAF from a previous scroll event
6552
- // must not override the collapse render with a stale window.
6553
6259
  if (this.virtualScrollRAF !== null) {
6554
6260
  cancelAnimationFrame(this.virtualScrollRAF);
6555
6261
  this.virtualScrollRAF = null;
@@ -6558,7 +6264,6 @@ var TreeView = /** @class */ (function (_super) {
6558
6264
  if (idx !== -1) {
6559
6265
  this.expandedNodes.splice(idx, 1);
6560
6266
  }
6561
- // Also update virtualExpandedNodesSet to keep it in sync
6562
6267
  if (this.virtualExpandedNodesSet) {
6563
6268
  this.virtualExpandedNodesSet.delete(id);
6564
6269
  }
@@ -6568,13 +6273,6 @@ var TreeView = /** @class */ (function (_super) {
6568
6273
  setValue(this.fields.expanded, false, acutalRootData);
6569
6274
  }
6570
6275
  }
6571
- // ── Fix: Pin focused ID to the collapsing node for keyboard navigation ──
6572
- // Same reasoning as handleVirtualExpand: when keyboard-triggered, `id` is
6573
- // definitively the focused node. Set unconditionally to avoid the stale/null
6574
- // state that could cause the RAF re-render to fall through to branch 3.
6575
- // For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
6576
- // renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
6577
- // mouse-based expand/collapse should never add the e-focused class.
6578
6276
  if (isKeyboardNav) {
6579
6277
  this.virtualFocusedId = id;
6580
6278
  }
@@ -6583,28 +6281,18 @@ var TreeView = /** @class */ (function (_super) {
6583
6281
  }
6584
6282
  this.virtualFlatNodes = this.flattenVisibleNodes();
6585
6283
  this.rebuildVirtualNodeMap();
6586
- // ── Snapshot scrollTop before computing the window ────────────────────────
6587
- // Same rationale as handleVirtualExpand: capture scrollTop before any focus()
6588
- // call or padding change can cause the browser to auto-scroll.
6589
6284
  var scrollTopSnapshot = this.element.scrollTop;
6590
6285
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
6591
6286
  this.virtualStartIndex = win.startIndex;
6592
6287
  this.virtualEndIndex = win.endIndex;
6593
- // ── Block ALL scroll-triggered re-renders during this collapse ────────────
6594
- // Same as handleVirtualExpand: use preventScroll:true on the focus() call
6595
- // below so the second potential scroll event is never fired.
6596
6288
  this.virtualSkipNextScrollRender = true;
6597
6289
  this.renderVirtualItems();
6598
- // renderVirtualItems() replaces all <li> elements so the old icon element that
6599
- // had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
6600
- // so the node can be expanded/collapsed again without being blocked.
6601
6290
  var newLi = this.getElement(id);
6602
6291
  if (newLi) {
6603
6292
  var icon = select('.' + ICON, newLi);
6604
6293
  if (icon) {
6605
6294
  removeClass([icon], PROCESS);
6606
6295
  }
6607
- // ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
6608
6296
  newLi.focus({ preventScroll: true });
6609
6297
  }
6610
6298
  if (this.isLoaded) {
@@ -6616,14 +6304,6 @@ var TreeView = /** @class */ (function (_super) {
6616
6304
  }
6617
6305
  }
6618
6306
  };
6619
- /**
6620
- * Updates the selected state of a node in `virtualFlatNodes` and re-renders the window.
6621
- *
6622
- * @param {string} id - The `data-uid` of the target node.
6623
- * @param {boolean} selected - Whether the node is now selected.
6624
- * @returns {void}
6625
- * @private
6626
- */
6627
6307
  TreeView.prototype.updateVirtualNodeSelection = function (id, selected) {
6628
6308
  var idx = this.virtualNodeMap.get(id);
6629
6309
  if (!isNOU(idx)) {
@@ -6631,21 +6311,11 @@ var TreeView = /** @class */ (function (_super) {
6631
6311
  this.renderVirtualItems();
6632
6312
  }
6633
6313
  };
6634
- /**
6635
- * Updates the checkbox state of a node in `virtualFlatNodes` and re-renders the window.
6636
- *
6637
- * @param {string} id - The `data-uid` of the target node.
6638
- * @param {'check' | 'uncheck' | 'indeterminate'} state - The new check state.
6639
- * @returns {void}
6640
- * @private
6641
- */
6642
6314
  TreeView.prototype.updateVirtualNodeCheckState = function (id, state) {
6643
6315
  var idx = this.virtualNodeMap.get(id);
6644
6316
  if (!isNOU(idx)) {
6645
6317
  this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
6646
6318
  state === 'check' ? true : state === 'indeterminate' ? 'indeterminate' : false;
6647
- // Keep the persistent indeterminate set in sync so flattenVisibleNodes()
6648
- // correctly restores state after any full list rebuild (e.g. collapse).
6649
6319
  if (state === 'indeterminate') {
6650
6320
  this.virtualIndeterminateNodesSet.add(id);
6651
6321
  }
@@ -6655,9 +6325,6 @@ var TreeView = /** @class */ (function (_super) {
6655
6325
  this.renderVirtualItems();
6656
6326
  }
6657
6327
  };
6658
- // ═══════════════════════════════════════════════════════════════════════════════
6659
- // END VIRTUALIZATION
6660
- // ═══════════════════════════════════════════════════════════════════════════════
6661
6328
  /**
6662
6329
  * Called internally if any of the property value changed.
6663
6330
  *
@@ -6802,7 +6469,6 @@ var TreeView = /** @class */ (function (_super) {
6802
6469
  }
6803
6470
  }
6804
6471
  else {
6805
- // Tear down virtualization and do a full re-render
6806
6472
  this.unwireVirtualScrollEvents();
6807
6473
  this.destroyVirtualMask();
6808
6474
  removeClass([this.element], VIRTUALIZATION);
@@ -6813,7 +6479,6 @@ var TreeView = /** @class */ (function (_super) {
6813
6479
  break;
6814
6480
  case 'height':
6815
6481
  if (this.enableVirtualization) {
6816
- // Re-initialise so the new height takes effect
6817
6482
  this.unwireVirtualScrollEvents();
6818
6483
  this.initVirtualization();
6819
6484
  }
@@ -6831,7 +6496,6 @@ var TreeView = /** @class */ (function (_super) {
6831
6496
  * @returns {void}
6832
6497
  */
6833
6498
  TreeView.prototype.destroy = function () {
6834
- // Tear down virtualization before standard cleanup
6835
6499
  if (this.enableVirtualization) {
6836
6500
  this.unwireVirtualScrollEvents();
6837
6501
  this.destroyVirtualMask();
@@ -6922,10 +6586,6 @@ var TreeView = /** @class */ (function (_super) {
6922
6586
  if (isNOU(fNode)) {
6923
6587
  this.setNodeFocusable();
6924
6588
  }
6925
- // In virtual mode (with a fixed height), rebuild the flat node list and re-render
6926
- // the window so the newly added nodes appear in the correct hierarchical position.
6927
- // When height is not provided, the tree uses normal rendering and the DOM is
6928
- // already updated by addGivenNodes() above.
6929
6589
  if (this.enableVirtualization && !isNOU(this.height)) {
6930
6590
  this.setRootData();
6931
6591
  this.rebuildVirtualCheckCache();