@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.
- package/dist/ej2-navigations.min.js +2 -2
- package/dist/ej2-navigations.umd.min.js +2 -2
- package/dist/ej2-navigations.umd.min.js.map +1 -1
- package/dist/es6/ej2-navigations.es2015.js +204 -546
- package/dist/es6/ej2-navigations.es2015.js.map +1 -1
- package/dist/es6/ej2-navigations.es5.js +206 -546
- package/dist/es6/ej2-navigations.es5.js.map +1 -1
- package/dist/global/ej2-navigations.min.js +2 -2
- package/dist/global/ej2-navigations.min.js.map +1 -1
- package/dist/global/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/treeview/treeview.d.ts +5 -221
- package/src/treeview/treeview.js +206 -546
package/src/treeview/treeview.js
CHANGED
|
@@ -228,54 +228,25 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
228
228
|
_this.isDropIn = false;
|
|
229
229
|
_this.OldCheckedData = [];
|
|
230
230
|
_this.isHiddenItem = false;
|
|
231
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
5690
|
-
var childFieldName =
|
|
5691
|
-
var expandedFieldName =
|
|
5692
|
-
var isCheckedFieldName =
|
|
5693
|
-
var iconCssFieldName =
|
|
5694
|
-
var imageUrlFieldName =
|
|
5695
|
-
var idFieldName =
|
|
5696
|
-
var textFieldName =
|
|
5697
|
-
var selectedFieldName =
|
|
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
|
-
|
|
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
|
-
|
|
5726
|
-
if (
|
|
5727
|
-
|
|
5728
|
-
|
|
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);
|
|
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;
|
|
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
|
-
|
|
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();
|