@syncfusion/ej2-navigations 34.2.3 → 34.2.5

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,55 +228,28 @@ 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;
251
+ _this.filterCheckedNodes = [];
252
+ _this.filterIndeterminateNodes = [];
280
253
  return _this;
281
254
  }
282
255
  TreeView_1 = TreeView;
@@ -577,17 +550,12 @@ var TreeView = /** @class */ (function (_super) {
577
550
  return false;
578
551
  }
579
552
  };
553
+ TreeView.prototype.isRemoteDataLoadOnDemand = function () {
554
+ var remoteDataSource = (this.fields.dataSource instanceof DataManager) ||
555
+ (this.fields.child && this.fields.child.dataSource instanceof DataManager ? true : false);
556
+ return !remoteDataSource && !this.loadOnDemand;
557
+ };
580
558
  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
559
  if (this.enableVirtualization && !isNOU(this.height)) {
592
560
  this.ulElement = this.createElement('ul', {
593
561
  className: PARENTITEM + ' ' + 'e-ul',
@@ -775,7 +743,9 @@ var TreeView = /** @class */ (function (_super) {
775
743
  if (this_1.checkedNodes.indexOf(childId) !== -1) {
776
744
  count++;
777
745
  }
778
- else if (this_1.element.classList.contains('e-filtering') && OldCheckedNodes.findIndex(function (e) { return e['id'] === childId; }) !== -1) {
746
+ else if (this_1.element.classList.contains('e-filtering') && (this_1.isRemoteDataLoadOnDemand() ?
747
+ this_1.filterCheckedNodes.findIndex(function (id) { return id === childId; }) !== -1 :
748
+ OldCheckedNodes.findIndex(function (e) { return e['id'] === childId; }) !== -1)) {
779
749
  count++;
780
750
  }
781
751
  };
@@ -1094,6 +1064,23 @@ var TreeView = /** @class */ (function (_super) {
1094
1064
  }
1095
1065
  });
1096
1066
  var parentNodeChecked = false;
1067
+ if (this.isRemoteDataLoadOnDemand() && this.element.classList.contains('e-filtering') && this.autoCheck) {
1068
+ if (this.filterCheckedNodes.indexOf(dataUid_1) !== -1) {
1069
+ this.changeState(checkBoxEle, 'check', null, true, true);
1070
+ }
1071
+ else if (this.filterIndeterminateNodes.indexOf(dataUid_1) !== -1) {
1072
+ this.changeState(checkBoxEle, 'indeterminate', null, true, true);
1073
+ }
1074
+ else {
1075
+ this.changeState(checkBoxEle, 'uncheck', null, true, true);
1076
+ }
1077
+ var parentUL_1 = closest(element, '.' + PARENTITEM);
1078
+ if (!isNOU(parentUL_1)) {
1079
+ var currentParent = closest(parentUL_1, '.' + LISTITEM);
1080
+ this.ensureParentCheckState(currentParent);
1081
+ }
1082
+ return;
1083
+ }
1097
1084
  if (this.element.classList.contains('e-filtering')) {
1098
1085
  var oldCheckedNodes = new DataManager(this.OldCheckedData).executeLocal(new Query().where('parentID', 'equal', dataUid_1, true));
1099
1086
  checkedCount = oldCheckedNodes.length;
@@ -1201,7 +1188,7 @@ var TreeView = /** @class */ (function (_super) {
1201
1188
  }
1202
1189
  });
1203
1190
  };
1204
- TreeView.prototype.getAllChildNodes = function (data, parentId) {
1191
+ TreeView.prototype.getAllChildNodes = function (data, parentId, includeAllDescendants) {
1205
1192
  if (isNOU(data) || isNOU(parentId)) {
1206
1193
  return [];
1207
1194
  }
@@ -1218,17 +1205,55 @@ var TreeView = /** @class */ (function (_super) {
1218
1205
  }
1219
1206
  if (this.dataType === 2) {
1220
1207
  var parentFieldId_1 = this.fields.id;
1221
- var childField = this.fields.child.toString();
1222
- var target_2 = parentId.toString();
1223
- var node = data.find(function (n) {
1224
- var idVal = getValue(parentFieldId_1, n);
1225
- return !isNOU(idVal) && idVal.toString() === target_2;
1226
- });
1208
+ var childField_1 = this.fields.child.toString();
1209
+ var target = parentId.toString();
1210
+ var findNodeInTree_1 = function (nodes, id) {
1211
+ if (!Array.isArray(nodes)) {
1212
+ return undefined;
1213
+ }
1214
+ for (var i = 0; i < nodes.length; i++) {
1215
+ var n = nodes[parseInt(i.toString(), 10)];
1216
+ var idVal = getValue(parentFieldId_1, n);
1217
+ if (!isNOU(idVal) && idVal.toString() === id) {
1218
+ return n;
1219
+ }
1220
+ var subs = getValue(childField_1, n);
1221
+ var found = findNodeInTree_1(subs, id);
1222
+ if (found) {
1223
+ return found;
1224
+ }
1225
+ }
1226
+ return undefined;
1227
+ };
1228
+ var node = findNodeInTree_1(data, target);
1227
1229
  if (!node) {
1228
1230
  return [];
1229
1231
  }
1230
- var children = getValue(childField, node);
1231
- return Array.isArray(children) ? children : [];
1232
+ var children = getValue(childField_1, node);
1233
+ if (!Array.isArray(children)) {
1234
+ return [];
1235
+ }
1236
+ if (!includeAllDescendants) {
1237
+ return children;
1238
+ }
1239
+ var result_1 = children.slice();
1240
+ var collectNested_1 = function (nodes) {
1241
+ if (!Array.isArray(nodes)) {
1242
+ return;
1243
+ }
1244
+ for (var i = 0; i < nodes.length; i++) {
1245
+ var n = nodes[parseInt(i.toString(), 10)];
1246
+ var grandChildren = getValue(childField_1, n);
1247
+ if (Array.isArray(grandChildren) && grandChildren.length) {
1248
+ for (var j = 0; j < grandChildren.length; j++) {
1249
+ result_1.push(grandChildren[parseInt(j.toString(), 10)]);
1250
+ }
1251
+ collectNested_1(grandChildren);
1252
+ }
1253
+ }
1254
+ };
1255
+ collectNested_1(children);
1256
+ return result_1;
1232
1257
  }
1233
1258
  return [];
1234
1259
  };
@@ -1674,7 +1699,6 @@ var TreeView = /** @class */ (function (_super) {
1674
1699
  this.checkDisabledState(id);
1675
1700
  }
1676
1701
  if (this.enableVirtualization) {
1677
- // Clear any indeterminate state — node is now fully checked
1678
1702
  this.virtualIndeterminateNodesSet.delete(id);
1679
1703
  var actualIndex = this.virtualNodeMap.get(id);
1680
1704
  if (!isNOU(actualIndex) && actualIndex > -1) {
@@ -1695,20 +1719,15 @@ var TreeView = /** @class */ (function (_super) {
1695
1719
  var actualIndex = this.virtualNodeMap.get(id);
1696
1720
  if (!isNOU(actualIndex) && actualIndex > -1) {
1697
1721
  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
1722
  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
1723
  this.virtualIndeterminateNodesSet.add(id);
1704
1724
  }
1705
1725
  else {
1706
- // Node is being fully unchecked — clear indeterminate tracking too
1707
1726
  this.virtualIndeterminateNodesSet.delete(id);
1708
1727
  this.virtualCheckedNodesSet.delete(id);
1709
1728
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = false;
1710
1729
  if (this.treeData && this.fields && this.fields.isChecked) {
1711
- setValue(this.fields.isChecked, false, this.treeData[parseInt(id.toString(), 10) - 1]);
1730
+ this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
1712
1731
  }
1713
1732
  if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
1714
1733
  this.setVirtualChildCheckState(id, false);
@@ -1754,8 +1773,6 @@ var TreeView = /** @class */ (function (_super) {
1754
1773
  }
1755
1774
  }
1756
1775
  }
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
1776
  var collapsedChildren = this.getChildNodes(this.treeData, parentId);
1760
1777
  if (collapsedChildren) {
1761
1778
  for (var i = 0; i < collapsedChildren.length; i++) {
@@ -1901,7 +1918,6 @@ var TreeView = /** @class */ (function (_super) {
1901
1918
  }
1902
1919
  this.isLoaded = true;
1903
1920
  this.isNodeDropped = false;
1904
- // Initialise (or re-initialise) virtual mode after data binding has settled
1905
1921
  if (this.enableVirtualization && !isNOU(this.height)) {
1906
1922
  this.initVirtualization();
1907
1923
  }
@@ -2041,9 +2057,6 @@ var TreeView = /** @class */ (function (_super) {
2041
2057
  };
2042
2058
  TreeView.prototype.expandNode = function (currLi, icon, loaded, isKeyboardNav) {
2043
2059
  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
2060
  if (this.enableVirtualization && !isNOU(this.height)) {
2048
2061
  var id = currLi ? currLi.getAttribute('data-uid') : null;
2049
2062
  if (id) {
@@ -2172,9 +2185,6 @@ var TreeView = /** @class */ (function (_super) {
2172
2185
  };
2173
2186
  TreeView.prototype.nodeCollapseAction = function (currLi, icon, colArgs, isKeyboardNav) {
2174
2187
  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
2188
  if (this.enableVirtualization && !isNOU(this.height)) {
2179
2189
  var id = currLi ? currLi.getAttribute('data-uid') : null;
2180
2190
  if (id) {
@@ -2645,7 +2655,6 @@ var TreeView = /** @class */ (function (_super) {
2645
2655
  li.setAttribute('tabindex', '0');
2646
2656
  EventHandler.add(li, 'blur', this.focusOut, this);
2647
2657
  this.updateIdAttr(focusedNode, li);
2648
- // Track focused id for virtual mode re-render restoration
2649
2658
  if (this.enableVirtualization) {
2650
2659
  this.virtualFocusedId = li.getAttribute('data-uid');
2651
2660
  }
@@ -2661,7 +2670,7 @@ var TreeView = /** @class */ (function (_super) {
2661
2670
  if (this.enableVirtualization && !isNOU(this.height)) {
2662
2671
  this.virtualSelectedNodesSet.add(id);
2663
2672
  var actualIndex = this.virtualNodeMap.get(id);
2664
- if (actualIndex && actualIndex > -1) {
2673
+ if (!isNOU(actualIndex) && actualIndex > -1) {
2665
2674
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
2666
2675
  }
2667
2676
  }
@@ -2682,7 +2691,7 @@ var TreeView = /** @class */ (function (_super) {
2682
2691
  var id = liEle.getAttribute('data-uid');
2683
2692
  this.virtualSelectedNodesSet.delete(id);
2684
2693
  var actualIndex = this.virtualNodeMap.get(id);
2685
- if (actualIndex && actualIndex > -1) {
2694
+ if (!isNOU(actualIndex) && actualIndex > -1) {
2686
2695
  this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
2687
2696
  }
2688
2697
  }
@@ -2777,12 +2786,6 @@ var TreeView = /** @class */ (function (_super) {
2777
2786
  }
2778
2787
  };
2779
2788
  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
2789
  if (this.enableVirtualization && !isNOU(this.height)) {
2787
2790
  this.expandNode(currLi, icon, undefined, isKeyboardNav);
2788
2791
  return;
@@ -2900,9 +2903,6 @@ var TreeView = /** @class */ (function (_super) {
2900
2903
  });
2901
2904
  };
2902
2905
  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
2906
  if (this.enableVirtualization && !isNOU(this.height)) {
2907
2907
  var focusedLi = this.getFocusedNode();
2908
2908
  if (focusedLi) {
@@ -2998,6 +2998,83 @@ var TreeView = /** @class */ (function (_super) {
2998
2998
  }
2999
2999
  }
3000
3000
  }
3001
+ if (this.isRemoteDataLoadOnDemand() && this.element.classList.contains('e-filtering') && this.autoCheck) {
3002
+ var nodeUid = li.getAttribute('data-uid');
3003
+ if (isNOU(nodeUid)) {
3004
+ return;
3005
+ }
3006
+ var checkedIndex = this.filterCheckedNodes.indexOf(nodeUid);
3007
+ var indeterminateIndex = this.filterIndeterminateNodes.indexOf(nodeUid);
3008
+ var shouldCheck = !isCheck;
3009
+ if (shouldCheck) {
3010
+ if (checkedIndex === -1) {
3011
+ this.filterCheckedNodes.push(nodeUid);
3012
+ }
3013
+ if (indeterminateIndex !== -1) {
3014
+ this.filterIndeterminateNodes.splice(indeterminateIndex, 1);
3015
+ }
3016
+ }
3017
+ else {
3018
+ if (checkedIndex !== -1) {
3019
+ this.filterCheckedNodes.splice(checkedIndex, 1);
3020
+ }
3021
+ }
3022
+ var parentLi = closest(closest(li, '.' + PARENTITEM), '.' + LISTITEM);
3023
+ while (!isNOU(parentLi)) {
3024
+ var parentUid = parentLi.getAttribute('data-uid');
3025
+ if (!isNOU(parentUid)) {
3026
+ var parentChildren = this.getAllChildNodes(this.DDTTreeData, parentUid, true);
3027
+ if (parentChildren && parentChildren.length > 0) {
3028
+ var firstChildState = void 0;
3029
+ var mismatchFound = false;
3030
+ for (var i = 0; i < parentChildren.length; i++) {
3031
+ var childData = parentChildren[parseInt(i.toString(), 10)];
3032
+ var childId = childData && childData[this.fields.id]
3033
+ ? childData[this.fields.id].toString()
3034
+ : null;
3035
+ if (isNOU(childId)) {
3036
+ continue;
3037
+ }
3038
+ var childCheckedState = this.filterIndeterminateNodes.indexOf(childId) !== -1
3039
+ ? null
3040
+ : this.filterCheckedNodes.indexOf(childId) !== -1;
3041
+ if (firstChildState === void 0) {
3042
+ firstChildState = childCheckedState;
3043
+ }
3044
+ else if (childCheckedState !== firstChildState) {
3045
+ mismatchFound = true;
3046
+ break;
3047
+ }
3048
+ }
3049
+ if (mismatchFound || firstChildState === null) {
3050
+ if (this.filterIndeterminateNodes.indexOf(parentUid) === -1) {
3051
+ this.filterIndeterminateNodes.push(parentUid);
3052
+ }
3053
+ var parentCheckedIndex = this.filterCheckedNodes.indexOf(parentUid);
3054
+ if (parentCheckedIndex !== -1) {
3055
+ this.filterCheckedNodes.splice(parentCheckedIndex, 1);
3056
+ }
3057
+ }
3058
+ else {
3059
+ var parentIndeterminateIndex = this.filterIndeterminateNodes.indexOf(parentUid);
3060
+ if (parentIndeterminateIndex !== -1) {
3061
+ this.filterIndeterminateNodes.splice(parentIndeterminateIndex, 1);
3062
+ }
3063
+ var parentCheckedIndex = this.filterCheckedNodes.indexOf(parentUid);
3064
+ if (firstChildState === true) {
3065
+ if (parentCheckedIndex === -1) {
3066
+ this.filterCheckedNodes.push(parentUid);
3067
+ }
3068
+ }
3069
+ else if (parentCheckedIndex !== -1) {
3070
+ this.filterCheckedNodes.splice(parentCheckedIndex, 1);
3071
+ }
3072
+ }
3073
+ }
3074
+ }
3075
+ parentLi = closest(closest(parentLi, '.' + PARENTITEM), '.' + LISTITEM);
3076
+ }
3077
+ }
3001
3078
  this.changeState(checkWrap, isCheck ? 'uncheck' : 'check', e, true);
3002
3079
  this.updateActiveClass(li, isCheck);
3003
3080
  if (this.autoCheck) {
@@ -3334,7 +3411,6 @@ var TreeView = /** @class */ (function (_super) {
3334
3411
  };
3335
3412
  TreeView.prototype.focusNextNode = function (li, isTowards) {
3336
3413
  if (this.enableVirtualization && !isNOU(this.height)) {
3337
- // Determine the id of the node one step in the requested direction
3338
3414
  var currentId = li ? li.getAttribute('data-uid') : null;
3339
3415
  var currentIdx = -1;
3340
3416
  if (!isNOU(currentId)) {
@@ -3346,26 +3422,14 @@ var TreeView = /** @class */ (function (_super) {
3346
3422
  return;
3347
3423
  }
3348
3424
  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
3425
  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
3426
  var allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
3360
3427
  for (var k = 0; k < allLi.length; k++) {
3361
3428
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
3362
3429
  allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED);
3363
3430
  allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
3364
3431
  }
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
3432
  this.ensureVirtualNodeVisible(targetId);
3368
- // Re-query both nodes after the potential re-render so setFocus gets live DOM elements
3369
3433
  var prevNode = currentId ? this.getElement(currentId) : li;
3370
3434
  var nextNode_1 = this.getElement(targetId);
3371
3435
  if (!isNOU(nextNode_1)) {
@@ -3466,18 +3530,6 @@ var TreeView = /** @class */ (function (_super) {
3466
3530
  if (!focusedElement) {
3467
3531
  return;
3468
3532
  }
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
3533
  var actuallyFocusedElement = document.activeElement;
3482
3534
  var isAlreadyFocusedInVirtualMode = this.enableVirtualization
3483
3535
  && actuallyFocusedElement === focusedElement
@@ -3732,12 +3784,6 @@ var TreeView = /** @class */ (function (_super) {
3732
3784
  else {
3733
3785
  txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
3734
3786
  }
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
3787
  if (this.enableVirtualization) {
3742
3788
  var id = liEle.getAttribute('data-uid');
3743
3789
  var idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
@@ -4185,11 +4231,49 @@ var TreeView = /** @class */ (function (_super) {
4185
4231
  this.fields.dataSource = this.treeData;
4186
4232
  }
4187
4233
  this.triggerEvent('nodeDropped', nodeData);
4234
+ if (this.enableVirtualization && !isNOU(this.height)) {
4235
+ this.setRootData();
4236
+ this.rebuildVirtualCheckCache();
4237
+ this.virtualFlatNodes = this.flattenVisibleNodes();
4238
+ this.rebuildVirtualNodeMap();
4239
+ var scrollTopSnapshot = this.element.scrollTop;
4240
+ var win = this.getVirtualWindowAt(scrollTopSnapshot);
4241
+ this.virtualStartIndex = win.startIndex;
4242
+ this.virtualEndIndex = win.endIndex;
4243
+ this.virtualSkipNextScrollRender = true;
4244
+ this.renderVirtualItems();
4245
+ }
4188
4246
  };
4189
4247
  TreeView.prototype.appendNode = function (dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
4190
4248
  var checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
4191
4249
  var collapse = closest(e.target, '.' + COLLAPSIBLE);
4192
4250
  var expand = closest(e.target, '.' + EXPANDABLE);
4251
+ if (this.enableVirtualization && !isNOU(this.height)) {
4252
+ var routesToSiblingBranch = !dragLi.classList.contains('e-disable') && !checkWrapper &&
4253
+ ((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) ||
4254
+ (expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) ||
4255
+ (!expand && !collapse));
4256
+ if (routesToSiblingBranch) {
4257
+ if (dropTarget.nodeName === 'LI') {
4258
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4259
+ }
4260
+ else if (dropTarget.firstElementChild && dropTarget.classList.contains(ROOT)) {
4261
+ if (dropTarget.firstElementChild.nodeName === 'UL') {
4262
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4263
+ }
4264
+ }
4265
+ else if ((dropTarget.classList.contains('e-icon-collapsible')) || (dropTarget.classList.contains('e-icon-expandable'))) {
4266
+ this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
4267
+ }
4268
+ else {
4269
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
4270
+ }
4271
+ }
4272
+ else {
4273
+ this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
4274
+ }
4275
+ return;
4276
+ }
4193
4277
  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
4278
  if (dropTarget.nodeName === 'LI') {
4195
4279
  this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
@@ -4213,6 +4297,62 @@ var TreeView = /** @class */ (function (_super) {
4213
4297
  this.ensureIndeterminate();
4214
4298
  }
4215
4299
  };
4300
+ TreeView.prototype.virtualMoveAsSibling = function (dragLi, dropLi, e, dragObj) {
4301
+ var dropUl = closest(dropLi, '.' + PARENTITEM);
4302
+ var pre;
4303
+ if (e.target.offsetHeight > 0 && e.event.offsetY > e.target.offsetHeight - 2) {
4304
+ pre = false;
4305
+ }
4306
+ else if (e.event.offsetY < 2) {
4307
+ pre = true;
4308
+ }
4309
+ else if (e.target.classList.contains('e-icon-expandable') || (e.target.classList.contains('e-icon-collapsible'))) {
4310
+ if ((e.event.offsetY < 5) || (e.event.offsetX < 3)) {
4311
+ pre = true;
4312
+ }
4313
+ else if ((e.event.offsetY > 15) || (e.event.offsetX > 17)) {
4314
+ pre = false;
4315
+ }
4316
+ }
4317
+ this.moveData(dragLi, dropLi, dropUl, pre, dragObj);
4318
+ };
4319
+ TreeView.prototype.virtualMoveAsChild = function (dragLi, dropLi, dragObj, offsetY, dropTarget) {
4320
+ var pos = offsetY;
4321
+ var isSiblingMarker = dropTarget.classList.contains('e-sibling');
4322
+ var refLi = (pos < 7 && !isSiblingMarker) || (isSiblingMarker &&
4323
+ dropLi.lastChild && !dropLi.lastChild.classList.contains('e-sibling'))
4324
+ ? dropLi : null;
4325
+ var dropTargetHt = dropTarget;
4326
+ var isAfter = (dropTargetHt.offsetHeight > 0 && pos > dropTargetHt.offsetHeight - 10);
4327
+ if (refLi) {
4328
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
4329
+ this.moveData(dragLi, dropLi, dropParentUl, true, dragObj);
4330
+ }
4331
+ else if (isAfter) {
4332
+ var dropParentUl = closest(dropLi, '.' + PARENTITEM);
4333
+ this.moveData(dragLi, dropLi, dropParentUl, false, dragObj);
4334
+ }
4335
+ else {
4336
+ var dropIcon = select('div.' + EXPANDABLE + ', div.' + COLLAPSIBLE, dropLi);
4337
+ var dropIsCollapsedParent = dropIcon && dropIcon.classList.contains(EXPANDABLE);
4338
+ var dropIsLeaf = isNOU(dropIcon);
4339
+ if (this.preventExpand !== true && (dropIsCollapsedParent || dropIsLeaf)) {
4340
+ var dropLiId = dropLi.getAttribute('data-uid');
4341
+ if (dropLiId && this.expandedNodes.indexOf(dropLiId) === -1) {
4342
+ this.expandedNodes.push(dropLiId);
4343
+ }
4344
+ if (this.virtualExpandedNodesSet && dropLiId) {
4345
+ this.virtualExpandedNodesSet.add(dropLiId);
4346
+ }
4347
+ var dropRecord = this.findNodeInTreeData(dropLiId);
4348
+ if (dropRecord && this.fields && this.fields.expanded) {
4349
+ setValue(this.fields.expanded, true, dropRecord);
4350
+ }
4351
+ }
4352
+ var dropUl = select('.' + PARENTITEM, dropLi) || dropLi;
4353
+ this.moveData(dragLi, null, dropUl, true, dragObj);
4354
+ }
4355
+ };
4216
4356
  TreeView.prototype.dropAsSiblingNode = function (dragLi, dropLi, e, dragObj) {
4217
4357
  var dropUl = closest(dropLi, '.' + PARENTITEM);
4218
4358
  var dragParentUl = closest(dragLi, '.' + PARENTITEM);
@@ -4300,7 +4440,7 @@ var TreeView = /** @class */ (function (_super) {
4300
4440
  delete removedData[0][this.fields.parentID];
4301
4441
  }
4302
4442
  else {
4303
- var currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
4443
+ var currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
4304
4444
  setValue(this.fields.parentID, currPid, removedData[0]);
4305
4445
  }
4306
4446
  this.treeData.splice(pos, 0, removedData[0]);
@@ -4442,7 +4582,7 @@ var TreeView = /** @class */ (function (_super) {
4442
4582
  var draggedNodeData = obj ? obj.dragData : null;
4443
4583
  var newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
4444
4584
  var dragLiParent = obj.dragLi.parentElement;
4445
- var dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
4585
+ var dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
4446
4586
  var targetParent = null;
4447
4587
  var indexValue = null;
4448
4588
  var iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
@@ -4451,7 +4591,7 @@ var TreeView = /** @class */ (function (_super) {
4451
4591
  var index = node ? closest(node, '.e-list-parent') : null;
4452
4592
  var i = 0;
4453
4593
  var position = null;
4454
- dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
4594
+ dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
4455
4595
  dragParent = (drop === true) ? this.dragParent : dragParent;
4456
4596
  if (cloneEle) {
4457
4597
  while (i < 4) {
@@ -5125,26 +5265,10 @@ var TreeView = /** @class */ (function (_super) {
5125
5265
  TreeView.prototype.keyDownHandler = function (e) {
5126
5266
  if (e.key === 'Tab' || e.keyCode === 9) {
5127
5267
  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
5268
  if (this.enableVirtualization) {
5136
5269
  this.virtualSkipNextScrollRender = true;
5137
5270
  }
5138
5271
  }
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
5272
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
5149
5273
  || e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
5150
5274
  if (this.enableVirtualization) {
@@ -5506,10 +5630,6 @@ var TreeView = /** @class */ (function (_super) {
5506
5630
  if (this.dataType === 1) {
5507
5631
  this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
5508
5632
  }
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
5633
  if (this.enableVirtualization && !isNOU(this.height)) {
5514
5634
  this.virtualFlatNodes = this.flattenVisibleNodes();
5515
5635
  this.rebuildVirtualNodeMap();
@@ -5578,16 +5698,6 @@ var TreeView = /** @class */ (function (_super) {
5578
5698
  }
5579
5699
  return parentsID;
5580
5700
  };
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
5701
  TreeView.prototype.parseHeightToPixels = function () {
5592
5702
  var h = this.height;
5593
5703
  if (isNOU(h)) {
@@ -5599,13 +5709,6 @@ var TreeView = /** @class */ (function (_super) {
5599
5709
  var parsed = parseFloat(h);
5600
5710
  return isNaN(parsed) ? 0 : parsed;
5601
5711
  };
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
5712
  TreeView.prototype.measureVirtualItemHeight = function () {
5610
5713
  var firstLi = select('.' + LISTITEM, this.element);
5611
5714
  if (firstLi && firstLi.offsetHeight > 0) {
@@ -5613,13 +5716,6 @@ var TreeView = /** @class */ (function (_super) {
5613
5716
  }
5614
5717
  return this.virtualItemHeight;
5615
5718
  };
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
5719
  TreeView.prototype.rebuildVirtualCheckCache = function () {
5624
5720
  this.virtualExpandedNodesSet.clear();
5625
5721
  this.virtualCheckedNodesSet.clear();
@@ -5640,20 +5736,10 @@ var TreeView = /** @class */ (function (_super) {
5640
5736
  }
5641
5737
  }
5642
5738
  };
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
5739
  TreeView.prototype.flattenVisibleNodes = function () {
5652
- // Build cached Sets for fast lookups
5653
5740
  this.rebuildVirtualCheckCache();
5654
5741
  var result = [];
5655
5742
  if (this.dataType === 1) {
5656
- // Flat data with parentID field — use getChildGroup to find root nodes
5657
5743
  var rootNodes = [];
5658
5744
  if (this.groupedData) {
5659
5745
  rootNodes = this.getChildGroup(this.groupedData, null, true);
@@ -5661,40 +5747,29 @@ var TreeView = /** @class */ (function (_super) {
5661
5747
  if (!rootNodes || rootNodes.length === 0) {
5662
5748
  rootNodes = this.rootData;
5663
5749
  }
5664
- // Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
5665
5750
  rootNodes = this.getSortedData(rootNodes);
5666
5751
  this.flattenNodeRecursive(rootNodes, 1, null, result);
5667
5752
  }
5668
5753
  else {
5669
- // Hierarchical data
5670
5754
  var sorted = this.getSortedData(this.rootData);
5671
5755
  this.flattenNodeRecursive(sorted, 1, null, result);
5672
5756
  }
5673
5757
  return result;
5674
5758
  };
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
5759
  TreeView.prototype.flattenNodeRecursive = function (nodes, level, parentId, result) {
5686
5760
  if (!nodes || nodes.length === 0) {
5687
5761
  return;
5688
5762
  }
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;
5763
+ var levelMapper = level === 1 ? this.fields : this.getChildFields(this.fields, level, 1);
5764
+ var childFieldName = levelMapper.child && levelMapper.child.toString();
5765
+ var expandedFieldName = levelMapper.expanded;
5766
+ var isCheckedFieldName = levelMapper.isChecked;
5767
+ var iconCssFieldName = levelMapper.iconCss;
5768
+ var imageUrlFieldName = levelMapper.imageUrl;
5769
+ var idFieldName = levelMapper.id;
5770
+ var textFieldName = levelMapper.text;
5771
+ var selectedFieldName = levelMapper.selected;
5772
+ var hasChildrenFieldName = levelMapper.hasChildren;
5698
5773
  var needsSorting = this.sortOrder !== 'None';
5699
5774
  var isDataTypeFlat = this.dataType === 1;
5700
5775
  var enableVirtualization = this.enableVirtualization;
@@ -5702,36 +5777,57 @@ var TreeView = /** @class */ (function (_super) {
5702
5777
  var raw = nodes[i];
5703
5778
  var id = isNOU(raw[idFieldName]) ? '' : raw[idFieldName].toString();
5704
5779
  var text = isNOU(raw[textFieldName]) ? '' : raw[textFieldName].toString();
5705
- // Use expandedNodes as the single source of truth.
5706
5780
  var inExpandedNodes = this.virtualExpandedNodesSet.has(id);
5707
- var rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
5781
+ var rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
5708
5782
  var expanded = inExpandedNodes || rawExpanded;
5709
5783
  if (rawExpanded && id) {
5710
5784
  this.expandedNodes.push(id);
5711
5785
  this.virtualExpandedNodesSet.add(id);
5712
5786
  }
5713
- // Inline hasChildRecords check to avoid function call (optimization)
5714
5787
  var hasChildren = false;
5715
- if (isDataTypeFlat) {
5788
+ var explicitHasChildren = hasChildrenFieldName ? getValue(hasChildrenFieldName, raw) : null;
5789
+ if (levelMapper.dataSource instanceof DataManager && !isNOU(explicitHasChildren) && explicitHasChildren) {
5790
+ hasChildren = true;
5791
+ }
5792
+ else if (isDataTypeFlat) {
5716
5793
  if (this.groupedData) {
5717
5794
  var groupedChild = this.getChildGroup(this.groupedData, id, false);
5718
5795
  hasChildren = !isNOU(groupedChild) && groupedChild.length > 0;
5719
5796
  }
5720
5797
  }
5721
5798
  else {
5722
- var childrenObj = getValue(childFieldName, raw);
5799
+ var childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
5723
5800
  hasChildren = !isNOU(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
5724
5801
  }
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') {
5802
+ var parentChecked = null;
5803
+ if (levelMapper.dataSource instanceof DataManager && level > 1 && this.autoCheck && parentId) {
5804
+ if (this.virtualCheckedNodesSet.has(parentId) ||
5805
+ (this.checkedNodes && this.checkedNodes.indexOf(parentId) !== -1)) {
5806
+ parentChecked = true;
5807
+ }
5808
+ else {
5809
+ parentChecked = false;
5810
+ }
5811
+ }
5812
+ var rawChecked = isCheckedFieldName && getValue(isCheckedFieldName, raw);
5813
+ if (enableVirtualization && id && !this.virtualCheckedNodesSet.has(id)) {
5814
+ if ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'true') ||
5815
+ (levelMapper.dataSource instanceof DataManager && parentChecked === true)) {
5729
5816
  this.virtualCheckedNodesSet.add(id);
5730
5817
  if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
5731
5818
  this.checkedNodes.push(id);
5732
5819
  }
5733
5820
  }
5734
5821
  }
5822
+ else if (enableVirtualization && id && this.virtualCheckedNodesSet.has(id) && levelMapper.dataSource instanceof DataManager) {
5823
+ if (level > 1 && !this.virtualIndeterminateNodesSet.has(parentId) && this.autoCheck &&
5824
+ ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'false') || parentChecked === false)) {
5825
+ this.virtualCheckedNodesSet.delete(id);
5826
+ if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
5827
+ this.checkedNodes.splice(this.checkedNodes.indexOf(id), 1);
5828
+ }
5829
+ }
5830
+ }
5735
5831
  if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
5736
5832
  var rawSelected = getValue(selectedFieldName, raw);
5737
5833
  if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
@@ -5741,10 +5837,7 @@ var TreeView = /** @class */ (function (_super) {
5741
5837
  }
5742
5838
  }
5743
5839
  }
5744
- // Use cached Sets for O(1) lookup
5745
5840
  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
5841
  var isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
5749
5842
  ? 'indeterminate'
5750
5843
  : this.virtualCheckedNodesSet.has(id);
@@ -5758,22 +5851,19 @@ var TreeView = /** @class */ (function (_super) {
5758
5851
  };
5759
5852
  result.push(node);
5760
5853
  if (expanded && hasChildren) {
5761
- // Determine children list
5762
5854
  var children = [];
5763
5855
  if (isDataTypeFlat) {
5764
5856
  if (this.groupedData) {
5765
5857
  children = this.getChildGroup(this.groupedData, id, false);
5766
- // Apply sortOrder to flat-data children (same as hierarchical path)
5767
5858
  if (needsSorting) {
5768
5859
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
5769
5860
  }
5770
5861
  }
5771
5862
  }
5772
5863
  else {
5773
- var rawChildren = getValue(childFieldName, raw);
5864
+ var rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
5774
5865
  if (!isNOU(rawChildren) && Array.isArray(rawChildren)) {
5775
5866
  children = rawChildren;
5776
- // Only sort if needed
5777
5867
  if (needsSorting) {
5778
5868
  children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
5779
5869
  }
@@ -5783,35 +5873,6 @@ var TreeView = /** @class */ (function (_super) {
5783
5873
  }
5784
5874
  }
5785
5875
  };
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
5876
  TreeView.prototype.rebuildVirtualNodeMap = function () {
5816
5877
  this.virtualNodeMap.clear();
5817
5878
  for (var i = 0; i < this.virtualFlatNodes.length; i++) {
@@ -5844,49 +5905,12 @@ var TreeView = /** @class */ (function (_super) {
5844
5905
  var paddingBottom = Math.max(0, (total - end) * itemH);
5845
5906
  return { paddingTop: paddingTop, paddingBottom: paddingBottom };
5846
5907
  };
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
5908
  TreeView.prototype.renderVirtualItems = function () {
5879
5909
  if (!this.ulElement) {
5880
5910
  return;
5881
5911
  }
5882
5912
  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
5913
  var focusedUid = this.virtualFocusedId || null;
5889
- // ── Clear previous render (direct children only) ─────────────────────────
5890
5914
  var existingItems = ul.querySelectorAll(':scope > li');
5891
5915
  for (var i = existingItems.length - 1; i >= 0; i--) {
5892
5916
  ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
@@ -5897,26 +5921,22 @@ var TreeView = /** @class */ (function (_super) {
5897
5921
  if (this.virtualStartIndex < this.virtualFlatNodes.length) {
5898
5922
  var firstNode = this.virtualFlatNodes[this.virtualStartIndex];
5899
5923
  if (firstNode.parentId) {
5900
- // Build the ancestor chain from root down to the immediate parent
5901
5924
  var ancestors = [];
5902
5925
  var searchId = firstNode.parentId;
5903
5926
  while (searchId) {
5904
5927
  var ancestorIdx = this.virtualNodeMap.get(searchId);
5905
5928
  if (!isNOU(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
5906
5929
  var ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
5907
- ancestors.unshift(ancestor); // prepend so root comes first
5930
+ ancestors.unshift(ancestor);
5908
5931
  searchId = ancestor.parentId;
5909
5932
  }
5910
5933
  else {
5911
5934
  break;
5912
5935
  }
5913
5936
  }
5914
- // Inject each ancestor as a real <li> into the root <ul>
5915
- // and register it in renderedParentMap for child-nesting below.
5916
5937
  for (var a = 0; a < ancestors.length; a++) {
5917
5938
  var ancestor = ancestors[parseInt(a.toString(), 10)];
5918
5939
  var ancestorLi = this.createVirtualListItem(ancestor, false);
5919
- // Find the correct container: root or a previously injected ancestor's <ul>
5920
5940
  var targetUl = ul;
5921
5941
  if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
5922
5942
  var pe = renderedParentMap.get(ancestor.parentId);
@@ -5935,26 +5955,20 @@ var TreeView = /** @class */ (function (_super) {
5935
5955
  }
5936
5956
  }
5937
5957
  }
5938
- // ── Scroll-space padding (adjusted for injected ancestors) ────────────────
5939
5958
  var total = this.virtualFlatNodes.length;
5940
5959
  var start = this.virtualStartIndex;
5941
5960
  var end = this.virtualEndIndex;
5942
5961
  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
5962
  var adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
5946
5963
  ul.style.paddingTop = adjustedPaddingTop + 'px';
5947
5964
  ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
5948
- // ── Render window items (nested via renderedParentMap) ────────────────────
5949
5965
  var liListBuf = [];
5950
5966
  for (var i = start; i < end; i++) {
5951
5967
  var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
5952
5968
  var li = this.createVirtualListItem(node, i === start);
5953
- // Find the correct parent container
5954
5969
  var targetUl = ul;
5955
5970
  if (node.parentId && renderedParentMap.has(node.parentId)) {
5956
5971
  var parentEntry = renderedParentMap.get(node.parentId);
5957
- // Lazily create the child <ul> the first time a child needs it
5958
5972
  if (!parentEntry.childUl) {
5959
5973
  parentEntry.childUl = this.createElement('ul', {
5960
5974
  className: PARENTITEM + ' e-ul',
@@ -5966,30 +5980,11 @@ var TreeView = /** @class */ (function (_super) {
5966
5980
  }
5967
5981
  targetUl.appendChild(li);
5968
5982
  liListBuf.push(li);
5969
- // Register this node so its children in the same window can nest under it
5970
5983
  if (node.expanded && node.hasChildren) {
5971
5984
  renderedParentMap.set(node.id, { li: li, childUl: null });
5972
5985
  }
5973
5986
  }
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
5987
  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
5988
  if (!focusRestored && focusedUid) {
5994
5989
  for (var k = 0; k < liListBuf.length; k++) {
5995
5990
  var li = liListBuf[parseInt(k.toString(), 10)];
@@ -5997,22 +5992,6 @@ var TreeView = /** @class */ (function (_super) {
5997
5992
  li.setAttribute('tabindex', '0');
5998
5993
  addClass([li], FOCUS);
5999
5994
  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
5995
  EventHandler.add(li, 'blur', this.focusOut, this);
6017
5996
  li.focus({ preventScroll: true });
6018
5997
  focusRestored = true;
@@ -6020,53 +5999,36 @@ var TreeView = /** @class */ (function (_super) {
6020
5999
  }
6021
6000
  }
6022
6001
  }
6023
- // 3. Finally default to the first item in the window.
6024
6002
  if (!focusRestored && liListBuf.length > 0) {
6025
6003
  var firstLi = liListBuf[0];
6026
6004
  firstLi.setAttribute('tabindex', '0');
6027
6005
  }
6028
- // ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
6029
- // This prevents stale tabindex="0" on multiple nodes which breaks Tab order
6030
6006
  for (var k = 0; k < liListBuf.length; k++) {
6031
6007
  var li = liListBuf[parseInt(k.toString(), 10)];
6032
6008
  if (li.getAttribute('tabindex') !== '0') {
6033
6009
  li.setAttribute('tabindex', '-1');
6034
6010
  }
6035
6011
  }
6036
- // Keep liList in sync so keyboard-navigation helpers stay functional.
6037
6012
  this.liList = liListBuf;
6038
6013
  };
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
6014
  TreeView.prototype.createVirtualListItem = function (node, _isFirst) {
6048
6015
  var li = this.createElement('li', { className: LISTITEM });
6049
- // Level class for CSS indentation
6050
6016
  li.classList.add('e-level-' + node.level);
6051
6017
  li.setAttribute('data-uid', node.id);
6052
6018
  li.setAttribute('aria-level', node.level.toString());
6053
6019
  li.setAttribute('role', 'treeitem');
6054
6020
  li.setAttribute('tabindex', '-1');
6055
- // Full-row overlay (required for fullRowSelect mode)
6056
6021
  if (this.fullRowSelect) {
6057
6022
  var fullRow = this.createElement('div', { className: FULLROW });
6058
6023
  li.appendChild(fullRow);
6059
6024
  }
6060
- // Text/icon wrapper
6061
6025
  var textWrap = this.createElement('div', { className: TEXTWRAP });
6062
- // Expand/collapse icon
6063
6026
  if (node.hasChildren) {
6064
6027
  var iconSpan = this.createElement('div', {
6065
6028
  className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
6066
6029
  });
6067
6030
  textWrap.appendChild(iconSpan);
6068
6031
  }
6069
- // Checkbox
6070
6032
  if (this.showCheckBox) {
6071
6033
  var checkWrap = createCheckBox(this.createElement, false, {
6072
6034
  checked: node.isChecked === true,
@@ -6080,28 +6042,24 @@ var TreeView = /** @class */ (function (_super) {
6080
6042
  }
6081
6043
  textWrap.appendChild(checkWrap);
6082
6044
  }
6083
- // Icon
6084
6045
  if (node.iconCss) {
6085
6046
  var iconEl = this.createElement('span', {
6086
6047
  className: ICON + ' ' + node.iconCss
6087
6048
  });
6088
6049
  textWrap.appendChild(iconEl);
6089
6050
  }
6090
- // Image
6091
6051
  if (node.imageUrl) {
6092
6052
  var img = document.createElement('img');
6093
6053
  img.src = node.imageUrl;
6094
6054
  img.classList.add(IMAGE);
6095
6055
  textWrap.appendChild(img);
6096
6056
  }
6097
- // Text anchor
6098
6057
  var anchor = this.createElement('span', {
6099
6058
  className: 'e-list-text',
6100
6059
  innerHTML: node.text
6101
6060
  });
6102
6061
  textWrap.appendChild(anchor);
6103
6062
  li.appendChild(textWrap);
6104
- // nodeTemplate
6105
6063
  if (!isNOU(this.nodeTemplateFn)) {
6106
6064
  var rawData = this.findNodeInTreeData(node.id);
6107
6065
  if (!isNOU(rawData)) {
@@ -6109,7 +6067,6 @@ var TreeView = /** @class */ (function (_super) {
6109
6067
  this.renderNodeTemplate(rawData, anchor, node.id);
6110
6068
  }
6111
6069
  }
6112
- // State classes
6113
6070
  if (node.expanded) {
6114
6071
  li.classList.add(EXPANDED);
6115
6072
  li.setAttribute('aria-expanded', 'true');
@@ -6125,80 +6082,43 @@ var TreeView = /** @class */ (function (_super) {
6125
6082
  if (node.hasChildren) {
6126
6083
  li.classList.add(CHILD);
6127
6084
  }
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
6085
  EventHandler.add(li, 'focus', this.focusIn, this);
6135
6086
  return li;
6136
6087
  };
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
6088
  TreeView.prototype.initVirtualization = function () {
6149
6089
  addClass([this.element], VIRTUALIZATION);
6150
- // Apply container height
6151
6090
  var px = this.parseHeightToPixels();
6152
6091
  if (px > 0) {
6153
6092
  this.element.style.height = px + 'px';
6154
6093
  this.element.style.overflowY = 'auto';
6155
6094
  }
6156
- // Ensure the inner UL is ready
6157
6095
  if (!this.ulElement) {
6158
6096
  this.ulElement = select('.' + PARENTITEM, this.element);
6159
6097
  }
6160
6098
  if (!this.ulElement) {
6161
6099
  return;
6162
6100
  }
6163
- // Make the UL position-relative for the absolute spacers
6164
6101
  this.ulElement.style.position = 'relative';
6165
- // Rebuild flat list
6166
6102
  this.virtualFlatNodes = this.flattenVisibleNodes();
6167
6103
  this.rebuildVirtualNodeMap();
6168
- // Measure dimensions
6169
6104
  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
6105
  if (this.virtualFlatNodes.length > 0) {
6172
6106
  var probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
6173
6107
  this.ulElement.appendChild(probe);
6174
6108
  this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
6175
6109
  this.ulElement.removeChild(probe);
6176
6110
  }
6177
- // Compute window and render all items in the initial viewport
6178
6111
  var win = this.getVirtualWindow();
6179
6112
  this.virtualStartIndex = win.startIndex;
6180
6113
  this.virtualEndIndex = win.endIndex;
6181
6114
  this.renderVirtualItems();
6182
- // Wire scroll
6183
6115
  this.wireVirtualScrollEvents();
6184
6116
  };
6185
- /**
6186
- * Wires the scroll event listener on the TreeView element.
6187
- *
6188
- * @returns {void}
6189
- * @private
6190
- */
6191
6117
  TreeView.prototype.wireVirtualScrollEvents = function () {
6192
6118
  this.unwireVirtualScrollEvents();
6193
6119
  this.virtualScrollBound = this.onVirtualScroll.bind(this);
6194
6120
  this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
6195
6121
  };
6196
- /**
6197
- * Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
6198
- *
6199
- * @returns {void}
6200
- * @private
6201
- */
6202
6122
  TreeView.prototype.unwireVirtualScrollEvents = function () {
6203
6123
  if (this.virtualScrollBound) {
6204
6124
  this.element.removeEventListener('scroll', this.virtualScrollBound);
@@ -6209,29 +6129,13 @@ var TreeView = /** @class */ (function (_super) {
6209
6129
  this.virtualScrollRAF = null;
6210
6130
  }
6211
6131
  };
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
6132
  TreeView.prototype.onVirtualScroll = function () {
6220
6133
  var _this = this;
6221
6134
  if (this.virtualScrollRAF !== null) {
6222
6135
  cancelAnimationFrame(this.virtualScrollRAF);
6223
6136
  }
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
6137
  if (this.virtualSkipNextScrollRender) {
6232
6138
  this.virtualSkipNextScrollRender = false;
6233
- // Update prev scrollTop so a subsequent user-driven scroll still
6234
- // calculates the correct delta.
6235
6139
  this.virtualPrevScrollTop = this.element.scrollTop;
6236
6140
  return;
6237
6141
  }
@@ -6248,30 +6152,16 @@ var TreeView = /** @class */ (function (_super) {
6248
6152
  _this.virtualStartIndex = win.startIndex;
6249
6153
  _this.virtualEndIndex = win.endIndex;
6250
6154
  _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
6155
  setTimeout(function () {
6254
6156
  _this.hideVirtualMask();
6255
- // Keep the (now hidden) mask's coords fresh for the next show.
6256
6157
  _this.updateMaskPosition();
6257
6158
  }, 200);
6258
6159
  });
6259
6160
  };
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
6161
  TreeView.prototype.createVirtualMask = function () {
6268
6162
  if (this.virtualMaskElement) {
6269
6163
  return;
6270
6164
  }
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
6165
  var rect = this.element.getBoundingClientRect();
6276
6166
  var scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
6277
6167
  var maskParent = this.createElement('div', {
@@ -6284,20 +6174,6 @@ var TreeView = /** @class */ (function (_super) {
6284
6174
  className: VIRTUAL_MASK_ITEM,
6285
6175
  styles: "display:flex;align-items:center;height:" + this.virtualItemHeight + "px;padding:0 12px;gap:8px;"
6286
6176
  });
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
6177
  var textHolder = this.createElement('span');
6302
6178
  textHolder.style.flex = '1';
6303
6179
  textHolder.style.height = '14px';
@@ -6315,14 +6191,6 @@ var TreeView = /** @class */ (function (_super) {
6315
6191
  this.virtualMaskElement = maskParent;
6316
6192
  this.element.appendChild(maskParent);
6317
6193
  };
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
6194
  TreeView.prototype.updateMaskPosition = function () {
6327
6195
  if (!this.virtualMaskElement) {
6328
6196
  return;
@@ -6334,12 +6202,6 @@ var TreeView = /** @class */ (function (_super) {
6334
6202
  this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
6335
6203
  this.virtualMaskElement.style.height = rect.height + 'px';
6336
6204
  };
6337
- /**
6338
- * Destroys all skeleton instances and removes the mask overlay from the DOM.
6339
- *
6340
- * @returns {void}
6341
- * @private
6342
- */
6343
6205
  TreeView.prototype.destroyVirtualMask = function () {
6344
6206
  for (var i = this.skeletonInstances.length - 1; i >= 0; i--) {
6345
6207
  this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
@@ -6352,52 +6214,26 @@ var TreeView = /** @class */ (function (_super) {
6352
6214
  this.virtualMaskElement = null;
6353
6215
  }
6354
6216
  };
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
6217
  TreeView.prototype.showVirtualMask = function (_scrollTop) {
6363
6218
  if (!this.virtualMaskElement) {
6364
6219
  this.createVirtualMask();
6365
6220
  }
6366
6221
  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
6222
  this.updateMaskPosition();
6370
6223
  this.virtualMaskElement.style.display = 'block';
6371
- // Visually hide the real list while the skeleton is shown
6372
6224
  if (this.ulElement) {
6373
6225
  this.ulElement.style.visibility = 'hidden';
6374
6226
  }
6375
6227
  }
6376
6228
  };
6377
- /**
6378
- * Hides the skeleton mask overlay after the virtual window has been rendered.
6379
- *
6380
- * @returns {void}
6381
- * @private
6382
- */
6383
6229
  TreeView.prototype.hideVirtualMask = function () {
6384
6230
  if (this.virtualMaskElement) {
6385
6231
  this.virtualMaskElement.style.display = 'none';
6386
- // Restore real list visibility
6387
6232
  if (this.ulElement) {
6388
6233
  this.ulElement.style.visibility = '';
6389
6234
  }
6390
6235
  }
6391
6236
  };
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
6237
  TreeView.prototype.ensureVirtualNodeVisible = function (id) {
6402
6238
  var idx = this.virtualNodeMap.get(id);
6403
6239
  if (isNOU(idx)) {
@@ -6416,25 +6252,13 @@ var TreeView = /** @class */ (function (_super) {
6416
6252
  this.element.scrollTop = nodeBottom - vpH;
6417
6253
  }
6418
6254
  else {
6419
- return; // already visible, no re-render needed
6255
+ return;
6420
6256
  }
6421
- // Re-render the new window synchronously so the <li> is in the DOM
6422
6257
  var win = this.getVirtualWindow();
6423
6258
  this.virtualStartIndex = win.startIndex;
6424
6259
  this.virtualEndIndex = win.endIndex;
6425
6260
  this.renderVirtualItems();
6426
6261
  };
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
6262
  TreeView.prototype.findNodeInTreeData = function (id) {
6439
6263
  if (!this.treeData) {
6440
6264
  return null;
@@ -6462,12 +6286,19 @@ var TreeView = /** @class */ (function (_super) {
6462
6286
  };
6463
6287
  return findRecursive(this.treeData);
6464
6288
  };
6289
+ TreeView.prototype.getNodeLevel = function (id) {
6290
+ if (!this.virtualFlatNodes) {
6291
+ return 1;
6292
+ }
6293
+ for (var i = 0; i < this.virtualFlatNodes.length; i++) {
6294
+ if (this.virtualFlatNodes[parseInt(i.toString(), 10)].id === id) {
6295
+ return this.virtualFlatNodes[parseInt(i.toString(), 10)].level;
6296
+ }
6297
+ }
6298
+ return 1;
6299
+ };
6465
6300
  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.
6301
+ var _this = this;
6471
6302
  if (this.virtualScrollRAF !== null) {
6472
6303
  cancelAnimationFrame(this.virtualScrollRAF);
6473
6304
  this.virtualScrollRAF = null;
@@ -6475,7 +6306,6 @@ var TreeView = /** @class */ (function (_super) {
6475
6306
  if (this.expandedNodes.indexOf(id) === -1) {
6476
6307
  this.expandedNodes.push(id);
6477
6308
  }
6478
- // Also update virtualExpandedNodesSet to keep it in sync
6479
6309
  if (this.virtualExpandedNodesSet) {
6480
6310
  this.virtualExpandedNodesSet.add(id);
6481
6311
  }
@@ -6485,56 +6315,76 @@ var TreeView = /** @class */ (function (_super) {
6485
6315
  setValue(this.fields.expanded, true, acutalRootData);
6486
6316
  }
6487
6317
  }
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
6318
  if (isKeyboardNav) {
6499
6319
  this.virtualFocusedId = id;
6500
6320
  }
6501
6321
  else {
6502
6322
  this.virtualFocusedId = null;
6503
6323
  }
6324
+ if (this.fields.dataSource instanceof DataManager && !this.isOffline) {
6325
+ var parentNode = this.findNodeInTreeData(id);
6326
+ if (parentNode && !isNOU(parentNode)) {
6327
+ var childFieldName = this.fields.child.toString();
6328
+ var childrenObj = getValue(childFieldName, parentNode);
6329
+ var childrenNotLoaded = isNOU(childrenObj) || !Array.isArray(childrenObj) ||
6330
+ childrenObj.length === 0;
6331
+ if (childrenNotLoaded) {
6332
+ this.loadVirtualChildData(id, parentNode, function () {
6333
+ _this.completeVirtualExpand(id, isKeyboardNav);
6334
+ });
6335
+ return;
6336
+ }
6337
+ }
6338
+ }
6339
+ this.completeVirtualExpand(id, isKeyboardNav);
6340
+ };
6341
+ TreeView.prototype.loadVirtualChildData = function (parentId, parentNode, callback) {
6342
+ var _this = this;
6343
+ var level = this.getNodeLevel(parentId);
6344
+ var mapper = this.getChildFields(this.fields, level + 1, 1);
6345
+ if (isNOU(mapper) || isNOU(mapper.dataSource)) {
6346
+ if (callback) {
6347
+ callback();
6348
+ }
6349
+ return;
6350
+ }
6351
+ mapper.dataSource.executeQuery(this.getQuery(mapper, parentId)).then(function (e) {
6352
+ var childItems = e.result;
6353
+ if (!isNOU(childItems) && childItems.length > 0) {
6354
+ var childFieldName = _this.fields.child.toString();
6355
+ setValue(childFieldName, childItems, parentNode);
6356
+ if (_this.dataType === 2) {
6357
+ var nodeInTree = _this.findNodeInTreeData(parentId);
6358
+ if (nodeInTree) {
6359
+ setValue(childFieldName, childItems, nodeInTree);
6360
+ }
6361
+ }
6362
+ }
6363
+ if (callback) {
6364
+ callback();
6365
+ }
6366
+ }).catch(function (e) {
6367
+ _this.trigger('actionFailure', { error: e });
6368
+ if (callback) {
6369
+ callback();
6370
+ }
6371
+ });
6372
+ };
6373
+ TreeView.prototype.completeVirtualExpand = function (id, isKeyboardNav) {
6504
6374
  this.virtualFlatNodes = this.flattenVisibleNodes();
6505
6375
  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
6376
  var scrollTopSnapshot = this.element.scrollTop;
6511
6377
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
6512
6378
  this.virtualStartIndex = win.startIndex;
6513
6379
  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
6380
  this.virtualSkipNextScrollRender = true;
6522
6381
  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
6382
  var newLi = this.getElement(id);
6527
6383
  if (newLi) {
6528
6384
  var icon = select('.' + ICON, newLi);
6529
6385
  if (icon) {
6530
6386
  removeClass([icon], PROCESS);
6531
6387
  }
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
6388
  newLi.focus({ preventScroll: true });
6539
6389
  }
6540
6390
  if (this.isLoaded) {
@@ -6547,9 +6397,6 @@ var TreeView = /** @class */ (function (_super) {
6547
6397
  }
6548
6398
  };
6549
6399
  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
6400
  if (this.virtualScrollRAF !== null) {
6554
6401
  cancelAnimationFrame(this.virtualScrollRAF);
6555
6402
  this.virtualScrollRAF = null;
@@ -6558,7 +6405,6 @@ var TreeView = /** @class */ (function (_super) {
6558
6405
  if (idx !== -1) {
6559
6406
  this.expandedNodes.splice(idx, 1);
6560
6407
  }
6561
- // Also update virtualExpandedNodesSet to keep it in sync
6562
6408
  if (this.virtualExpandedNodesSet) {
6563
6409
  this.virtualExpandedNodesSet.delete(id);
6564
6410
  }
@@ -6568,13 +6414,6 @@ var TreeView = /** @class */ (function (_super) {
6568
6414
  setValue(this.fields.expanded, false, acutalRootData);
6569
6415
  }
6570
6416
  }
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
6417
  if (isKeyboardNav) {
6579
6418
  this.virtualFocusedId = id;
6580
6419
  }
@@ -6583,28 +6422,18 @@ var TreeView = /** @class */ (function (_super) {
6583
6422
  }
6584
6423
  this.virtualFlatNodes = this.flattenVisibleNodes();
6585
6424
  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
6425
  var scrollTopSnapshot = this.element.scrollTop;
6590
6426
  var win = this.getVirtualWindowAt(scrollTopSnapshot);
6591
6427
  this.virtualStartIndex = win.startIndex;
6592
6428
  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
6429
  this.virtualSkipNextScrollRender = true;
6597
6430
  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
6431
  var newLi = this.getElement(id);
6602
6432
  if (newLi) {
6603
6433
  var icon = select('.' + ICON, newLi);
6604
6434
  if (icon) {
6605
6435
  removeClass([icon], PROCESS);
6606
6436
  }
6607
- // ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
6608
6437
  newLi.focus({ preventScroll: true });
6609
6438
  }
6610
6439
  if (this.isLoaded) {
@@ -6616,14 +6445,6 @@ var TreeView = /** @class */ (function (_super) {
6616
6445
  }
6617
6446
  }
6618
6447
  };
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
6448
  TreeView.prototype.updateVirtualNodeSelection = function (id, selected) {
6628
6449
  var idx = this.virtualNodeMap.get(id);
6629
6450
  if (!isNOU(idx)) {
@@ -6631,21 +6452,11 @@ var TreeView = /** @class */ (function (_super) {
6631
6452
  this.renderVirtualItems();
6632
6453
  }
6633
6454
  };
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
6455
  TreeView.prototype.updateVirtualNodeCheckState = function (id, state) {
6643
6456
  var idx = this.virtualNodeMap.get(id);
6644
6457
  if (!isNOU(idx)) {
6645
6458
  this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
6646
6459
  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
6460
  if (state === 'indeterminate') {
6650
6461
  this.virtualIndeterminateNodesSet.add(id);
6651
6462
  }
@@ -6655,9 +6466,6 @@ var TreeView = /** @class */ (function (_super) {
6655
6466
  this.renderVirtualItems();
6656
6467
  }
6657
6468
  };
6658
- // ═══════════════════════════════════════════════════════════════════════════════
6659
- // END VIRTUALIZATION
6660
- // ═══════════════════════════════════════════════════════════════════════════════
6661
6469
  /**
6662
6470
  * Called internally if any of the property value changed.
6663
6471
  *
@@ -6802,7 +6610,6 @@ var TreeView = /** @class */ (function (_super) {
6802
6610
  }
6803
6611
  }
6804
6612
  else {
6805
- // Tear down virtualization and do a full re-render
6806
6613
  this.unwireVirtualScrollEvents();
6807
6614
  this.destroyVirtualMask();
6808
6615
  removeClass([this.element], VIRTUALIZATION);
@@ -6813,7 +6620,6 @@ var TreeView = /** @class */ (function (_super) {
6813
6620
  break;
6814
6621
  case 'height':
6815
6622
  if (this.enableVirtualization) {
6816
- // Re-initialise so the new height takes effect
6817
6623
  this.unwireVirtualScrollEvents();
6818
6624
  this.initVirtualization();
6819
6625
  }
@@ -6831,7 +6637,6 @@ var TreeView = /** @class */ (function (_super) {
6831
6637
  * @returns {void}
6832
6638
  */
6833
6639
  TreeView.prototype.destroy = function () {
6834
- // Tear down virtualization before standard cleanup
6835
6640
  if (this.enableVirtualization) {
6836
6641
  this.unwireVirtualScrollEvents();
6837
6642
  this.destroyVirtualMask();
@@ -6922,10 +6727,6 @@ var TreeView = /** @class */ (function (_super) {
6922
6727
  if (isNOU(fNode)) {
6923
6728
  this.setNodeFocusable();
6924
6729
  }
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
6730
  if (this.enableVirtualization && !isNOU(this.height)) {
6930
6731
  this.setRootData();
6931
6732
  this.rebuildVirtualCheckCache();