@syncfusion/ej2-navigations 34.2.2 → 34.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +310 -563
- package/dist/es6/ej2-navigations.es2015.js.map +1 -1
- package/dist/es6/ej2-navigations.es5.js +312 -563
- 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/common/menu-base.js +14 -6
- package/src/treeview/treeview-model.d.ts +1 -0
- package/src/treeview/treeview.d.ts +7 -210
- package/src/treeview/treeview.js +298 -557
- package/styles/bds-lite.css +3 -0
- package/styles/bds.css +3 -0
- package/styles/bootstrap-dark-lite.css +3 -0
- package/styles/bootstrap-dark.css +3 -0
- package/styles/bootstrap-lite.css +3 -0
- package/styles/bootstrap.css +3 -0
- package/styles/bootstrap4-lite.css +3 -0
- package/styles/bootstrap4.css +3 -0
- package/styles/bootstrap5-dark-lite.css +3 -0
- package/styles/bootstrap5-dark.css +3 -0
- package/styles/bootstrap5-lite.css +3 -0
- package/styles/bootstrap5.3-lite.css +3 -0
- package/styles/bootstrap5.3.css +3 -0
- package/styles/bootstrap5.css +3 -0
- package/styles/fabric-dark-lite.css +3 -0
- package/styles/fabric-dark.css +3 -0
- package/styles/fabric-lite.css +3 -0
- package/styles/fabric.css +3 -0
- package/styles/fluent-dark-lite.css +3 -0
- package/styles/fluent-dark.css +3 -0
- package/styles/fluent-lite.css +3 -0
- package/styles/fluent.css +3 -0
- package/styles/fluent2-lite.css +3 -0
- package/styles/fluent2.css +3 -0
- package/styles/highcontrast-light-lite.css +3 -0
- package/styles/highcontrast-light.css +3 -0
- package/styles/highcontrast-lite.css +3 -0
- package/styles/highcontrast.css +3 -0
- package/styles/material-dark-lite.css +3 -0
- package/styles/material-dark.css +3 -0
- package/styles/material-lite.css +3 -0
- package/styles/material.css +3 -0
- package/styles/material3-dark-lite.css +3 -0
- package/styles/material3-dark.css +3 -0
- package/styles/material3-lite.css +3 -0
- package/styles/material3.css +3 -0
- package/styles/tailwind-dark-lite.css +3 -0
- package/styles/tailwind-dark.css +3 -0
- package/styles/tailwind-lite.css +3 -0
- package/styles/tailwind.css +3 -0
- package/styles/tailwind3-lite.css +3 -0
- package/styles/tailwind3.css +3 -0
- package/styles/toolbar/_layout.scss +4 -0
- package/styles/toolbar/bds.css +3 -0
- package/styles/toolbar/bootstrap-dark.css +3 -0
- package/styles/toolbar/bootstrap.css +3 -0
- package/styles/toolbar/bootstrap4.css +3 -0
- package/styles/toolbar/bootstrap5-dark.css +3 -0
- package/styles/toolbar/bootstrap5.3.css +3 -0
- package/styles/toolbar/bootstrap5.css +3 -0
- package/styles/toolbar/fabric-dark.css +3 -0
- package/styles/toolbar/fabric.css +3 -0
- package/styles/toolbar/fluent-dark.css +3 -0
- package/styles/toolbar/fluent.css +3 -0
- package/styles/toolbar/fluent2.css +3 -0
- package/styles/toolbar/highcontrast-light.css +3 -0
- package/styles/toolbar/highcontrast.css +3 -0
- package/styles/toolbar/material-dark.css +3 -0
- package/styles/toolbar/material.css +3 -0
- package/styles/toolbar/material3-dark.css +3 -0
- package/styles/toolbar/material3.css +3 -0
- package/styles/toolbar/tailwind-dark.css +3 -0
- package/styles/toolbar/tailwind.css +3 -0
- package/styles/toolbar/tailwind3.css +3 -0
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,19 +1655,16 @@ 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
|
-
this.treeData
|
|
1665
|
+
if (this.treeData && this.fields && this.fields.isChecked) {
|
|
1666
|
+
this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
|
|
1667
|
+
}
|
|
1711
1668
|
if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
|
|
1712
1669
|
this.setVirtualChildCheckState(id, false);
|
|
1713
1670
|
}
|
|
@@ -1752,8 +1709,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1752
1709
|
}
|
|
1753
1710
|
}
|
|
1754
1711
|
}
|
|
1755
|
-
// Collapsed children are not present in virtualFlatNodes, so walk the raw data
|
|
1756
|
-
// to keep this.treeData and checkedNodes in sync for hidden descendants too.
|
|
1757
1712
|
var collapsedChildren = this.getChildNodes(this.treeData, parentId);
|
|
1758
1713
|
if (collapsedChildren) {
|
|
1759
1714
|
for (var i = 0; i < collapsedChildren.length; i++) {
|
|
@@ -1899,7 +1854,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1899
1854
|
}
|
|
1900
1855
|
this.isLoaded = true;
|
|
1901
1856
|
this.isNodeDropped = false;
|
|
1902
|
-
// Initialise (or re-initialise) virtual mode after data binding has settled
|
|
1903
1857
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
1904
1858
|
this.initVirtualization();
|
|
1905
1859
|
}
|
|
@@ -2039,9 +1993,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2039
1993
|
};
|
|
2040
1994
|
TreeView.prototype.expandNode = function (currLi, icon, loaded, isKeyboardNav) {
|
|
2041
1995
|
var _this = this;
|
|
2042
|
-
// Virtual mode fast-path: rebuild the flat list and re-render the window.
|
|
2043
|
-
// Only engage when height is also set; without a fixed height the tree falls
|
|
2044
|
-
// back to normal rendering and the standard expand path must be used.
|
|
2045
1996
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2046
1997
|
var id = currLi ? currLi.getAttribute('data-uid') : null;
|
|
2047
1998
|
if (id) {
|
|
@@ -2170,9 +2121,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2170
2121
|
};
|
|
2171
2122
|
TreeView.prototype.nodeCollapseAction = function (currLi, icon, colArgs, isKeyboardNav) {
|
|
2172
2123
|
var _this = this;
|
|
2173
|
-
// Virtual mode fast-path: rebuild the flat list and re-render the window.
|
|
2174
|
-
// Only engage when height is also set; without a fixed height the tree falls
|
|
2175
|
-
// back to normal rendering and the standard collapse path must be used.
|
|
2176
2124
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2177
2125
|
var id = currLi ? currLi.getAttribute('data-uid') : null;
|
|
2178
2126
|
if (id) {
|
|
@@ -2643,7 +2591,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2643
2591
|
li.setAttribute('tabindex', '0');
|
|
2644
2592
|
EventHandler.add(li, 'blur', this.focusOut, this);
|
|
2645
2593
|
this.updateIdAttr(focusedNode, li);
|
|
2646
|
-
// Track focused id for virtual mode re-render restoration
|
|
2647
2594
|
if (this.enableVirtualization) {
|
|
2648
2595
|
this.virtualFocusedId = li.getAttribute('data-uid');
|
|
2649
2596
|
}
|
|
@@ -2656,6 +2603,13 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2656
2603
|
if (!isNOU(id) && this.selectedNodes.indexOf(id) === -1) {
|
|
2657
2604
|
this.selectedNodes.push(id);
|
|
2658
2605
|
}
|
|
2606
|
+
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2607
|
+
this.virtualSelectedNodesSet.add(id);
|
|
2608
|
+
var actualIndex = this.virtualNodeMap.get(id);
|
|
2609
|
+
if (!isNOU(actualIndex) && actualIndex > -1) {
|
|
2610
|
+
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2659
2613
|
};
|
|
2660
2614
|
TreeView.prototype.removeSelect = function (liEle) {
|
|
2661
2615
|
if (this.allowMultiSelection) {
|
|
@@ -2669,6 +2623,14 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2669
2623
|
if (index > -1) {
|
|
2670
2624
|
this.selectedNodes.splice(index, 1);
|
|
2671
2625
|
}
|
|
2626
|
+
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2627
|
+
var id = liEle.getAttribute('data-uid');
|
|
2628
|
+
this.virtualSelectedNodesSet.delete(id);
|
|
2629
|
+
var actualIndex = this.virtualNodeMap.get(id);
|
|
2630
|
+
if (!isNOU(actualIndex) && actualIndex > -1) {
|
|
2631
|
+
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2672
2634
|
};
|
|
2673
2635
|
TreeView.prototype.removeSelectAll = function () {
|
|
2674
2636
|
var selectedLI = this.element.querySelectorAll('.' + ACTIVE);
|
|
@@ -2683,6 +2645,15 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2683
2645
|
}
|
|
2684
2646
|
removeClass(selectedLI, ACTIVE);
|
|
2685
2647
|
this.setProperties({ selectedNodes: [] }, true);
|
|
2648
|
+
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2649
|
+
this.virtualSelectedNodesSet.clear();
|
|
2650
|
+
for (var i = 0; i < this.virtualFlatNodes.length; i++) {
|
|
2651
|
+
var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
|
|
2652
|
+
if (node.selected) {
|
|
2653
|
+
node.selected = false;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2686
2657
|
};
|
|
2687
2658
|
TreeView.prototype.getSelectEvent = function (currLi, action, e) {
|
|
2688
2659
|
var nodeData = this.getNodeData(currLi);
|
|
@@ -2751,12 +2722,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2751
2722
|
}
|
|
2752
2723
|
};
|
|
2753
2724
|
TreeView.prototype.nodeExpandAction = function (currLi, icon, expandChild, callback, isFromExpandAll, isKeyboardNav) {
|
|
2754
|
-
// In virtual mode (with a fixed height) the flat <li> never has a child <ul>
|
|
2755
|
-
// in the DOM, so the PARENTITEM check below would always fail and fall into
|
|
2756
|
-
// renderChildNodes(), which corrupts the virtual DOM.
|
|
2757
|
-
// Delegate directly to expandNode() which has the handleVirtualExpand fast-path.
|
|
2758
|
-
// When height is not provided, fall through to normal expand logic below since
|
|
2759
|
-
// the tree is rendered with a full nested DOM structure.
|
|
2760
2725
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2761
2726
|
this.expandNode(currLi, icon, undefined, isKeyboardNav);
|
|
2762
2727
|
return;
|
|
@@ -2874,9 +2839,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2874
2839
|
});
|
|
2875
2840
|
};
|
|
2876
2841
|
TreeView.prototype.navigateToFocus = function (isUp) {
|
|
2877
|
-
// In virtual mode (with a fixed height) the element itself is the scroll
|
|
2878
|
-
// container; use ensureVirtualNodeVisible. When height is not provided,
|
|
2879
|
-
// the tree uses normal rendering, so fall through to the standard scroll logic.
|
|
2880
2842
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2881
2843
|
var focusedLi = this.getFocusedNode();
|
|
2882
2844
|
if (focusedLi) {
|
|
@@ -3308,7 +3270,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3308
3270
|
};
|
|
3309
3271
|
TreeView.prototype.focusNextNode = function (li, isTowards) {
|
|
3310
3272
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
3311
|
-
// Determine the id of the node one step in the requested direction
|
|
3312
3273
|
var currentId = li ? li.getAttribute('data-uid') : null;
|
|
3313
3274
|
var currentIdx = -1;
|
|
3314
3275
|
if (!isNOU(currentId)) {
|
|
@@ -3320,26 +3281,14 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3320
3281
|
return;
|
|
3321
3282
|
}
|
|
3322
3283
|
var targetId = this.virtualFlatNodes[parseInt(targetIdx.toString(), 10)].id;
|
|
3323
|
-
// ── Fix 1: set virtualFocusedId BEFORE calling ensureVirtualNodeVisible ──
|
|
3324
|
-
// renderVirtualItems() is called synchronously inside ensureVirtualNodeVisible
|
|
3325
|
-
// when a re-render is needed. If virtualFocusedId is still the old value at
|
|
3326
|
-
// that point, focus restoration falls back to the first item. Setting it here
|
|
3327
|
-
// ensures renderVirtualItems always restores focus to the correct target node.
|
|
3328
3284
|
this.virtualFocusedId = targetId;
|
|
3329
|
-
// ── Fix 2: clear focus classes from ALL nested list items, not just top-level ones ──
|
|
3330
|
-
// With hierarchical virtual DOM, the currently focused <li> may be nested inside
|
|
3331
|
-
// a child <ul>. Using ':scope > li' only cleans up direct children and leaves
|
|
3332
|
-
// stale e-focused / e-node-focus classes on nested nodes across re-renders.
|
|
3333
3285
|
var allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
|
|
3334
3286
|
for (var k = 0; k < allLi.length; k++) {
|
|
3335
3287
|
allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
|
|
3336
3288
|
allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED);
|
|
3337
3289
|
allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
|
|
3338
3290
|
}
|
|
3339
|
-
// Ensure the target <li> is in the DOM window (may trigger a re-render).
|
|
3340
|
-
// virtualFocusedId is already set above so renderVirtualItems restores focus correctly.
|
|
3341
3291
|
this.ensureVirtualNodeVisible(targetId);
|
|
3342
|
-
// Re-query both nodes after the potential re-render so setFocus gets live DOM elements
|
|
3343
3292
|
var prevNode = currentId ? this.getElement(currentId) : li;
|
|
3344
3293
|
var nextNode_1 = this.getElement(targetId);
|
|
3345
3294
|
if (!isNOU(nextNode_1)) {
|
|
@@ -3440,18 +3389,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3440
3389
|
if (!focusedElement) {
|
|
3441
3390
|
return;
|
|
3442
3391
|
}
|
|
3443
|
-
// Virtual-mode guard: when the browser moves focus into the TreeView (e.g. via
|
|
3444
|
-
// the Tab key) it always lands on the single `<li tabindex="0">`. In that
|
|
3445
|
-
// case the element is already the focusable one, FOCUS is already applied
|
|
3446
|
-
// and the only thing left to do is the keyboard focus styling. Skip the
|
|
3447
|
-
// redundant setAttribute/EventHandler.add work so we never re-render the
|
|
3448
|
-
// virtual window as a side effect of focusing the existing `tabindex="0"`
|
|
3449
|
-
// item.
|
|
3450
|
-
// ── Fix: Use document.activeElement to detect browser-focused node ──
|
|
3451
|
-
// After renderVirtualItems() restores focus, the browser-focused element is
|
|
3452
|
-
// the authoritative source, not getFocusedNode(). If they don't match, it means
|
|
3453
|
-
// renderVirtualItems already correctly set up the element, so we must not
|
|
3454
|
-
// override its setup by calling setFocus again.
|
|
3455
3392
|
var actuallyFocusedElement = document.activeElement;
|
|
3456
3393
|
var isAlreadyFocusedInVirtualMode = this.enableVirtualization
|
|
3457
3394
|
&& actuallyFocusedElement === focusedElement
|
|
@@ -3706,12 +3643,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3706
3643
|
else {
|
|
3707
3644
|
txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
|
|
3708
3645
|
}
|
|
3709
|
-
// ── Virtual mode: sync the flat-node snapshot so re-renders show updated text ──
|
|
3710
|
-
// virtualFlatNodes is rebuilt from this.treeData (via flattenVisibleNodes) only on
|
|
3711
|
-
// expand/collapse/scroll. Between those events the snapshot is the single source of
|
|
3712
|
-
// truth used by createVirtualListItem. setValue() above updates this.editData (which
|
|
3713
|
-
// IS the treeData object reference), but virtualFlatNodes[idx].text still holds the
|
|
3714
|
-
// old value. Update it here so the next renderVirtualItems() call shows the new text.
|
|
3715
3646
|
if (this.enableVirtualization) {
|
|
3716
3647
|
var id = liEle.getAttribute('data-uid');
|
|
3717
3648
|
var idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
|
|
@@ -4159,11 +4090,49 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4159
4090
|
this.fields.dataSource = this.treeData;
|
|
4160
4091
|
}
|
|
4161
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
|
+
}
|
|
4162
4105
|
};
|
|
4163
4106
|
TreeView.prototype.appendNode = function (dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
|
|
4164
4107
|
var checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
|
|
4165
4108
|
var collapse = closest(e.target, '.' + COLLAPSIBLE);
|
|
4166
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
|
+
}
|
|
4167
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))) {
|
|
4168
4137
|
if (dropTarget.nodeName === 'LI') {
|
|
4169
4138
|
this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
|
|
@@ -4187,6 +4156,62 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4187
4156
|
this.ensureIndeterminate();
|
|
4188
4157
|
}
|
|
4189
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
|
+
};
|
|
4190
4215
|
TreeView.prototype.dropAsSiblingNode = function (dragLi, dropLi, e, dragObj) {
|
|
4191
4216
|
var dropUl = closest(dropLi, '.' + PARENTITEM);
|
|
4192
4217
|
var dragParentUl = closest(dragLi, '.' + PARENTITEM);
|
|
@@ -4274,7 +4299,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4274
4299
|
delete removedData[0][this.fields.parentID];
|
|
4275
4300
|
}
|
|
4276
4301
|
else {
|
|
4277
|
-
var currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
|
|
4302
|
+
var currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
|
|
4278
4303
|
setValue(this.fields.parentID, currPid, removedData[0]);
|
|
4279
4304
|
}
|
|
4280
4305
|
this.treeData.splice(pos, 0, removedData[0]);
|
|
@@ -4416,7 +4441,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4416
4441
|
var draggedNodeData = obj ? obj.dragData : null;
|
|
4417
4442
|
var newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
|
|
4418
4443
|
var dragLiParent = obj.dragLi.parentElement;
|
|
4419
|
-
var dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
|
|
4444
|
+
var dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
|
|
4420
4445
|
var targetParent = null;
|
|
4421
4446
|
var indexValue = null;
|
|
4422
4447
|
var iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
|
|
@@ -4425,7 +4450,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4425
4450
|
var index = node ? closest(node, '.e-list-parent') : null;
|
|
4426
4451
|
var i = 0;
|
|
4427
4452
|
var position = null;
|
|
4428
|
-
dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
|
|
4453
|
+
dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
|
|
4429
4454
|
dragParent = (drop === true) ? this.dragParent : dragParent;
|
|
4430
4455
|
if (cloneEle) {
|
|
4431
4456
|
while (i < 4) {
|
|
@@ -5099,26 +5124,10 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5099
5124
|
TreeView.prototype.keyDownHandler = function (e) {
|
|
5100
5125
|
if (e.key === 'Tab' || e.keyCode === 9) {
|
|
5101
5126
|
this.isKeyboardFocus = true;
|
|
5102
|
-
// Mark the upcoming scroll event (if any) as Tab-induced so the
|
|
5103
|
-
// virtual scroll handler skips its re-render path. The browser
|
|
5104
|
-
// calls `Element.scrollIntoView` semantics on focus, which scrolls
|
|
5105
|
-
// a `<li tabindex="0">` in the middle of the tree into view and
|
|
5106
|
-
// therefore fires `scroll` on the container. Without this guard
|
|
5107
|
-
// the scroll handler would tear down the focused `<li>` and move
|
|
5108
|
-
// focus elsewhere.
|
|
5109
5127
|
if (this.enableVirtualization) {
|
|
5110
5128
|
this.virtualSkipNextScrollRender = true;
|
|
5111
5129
|
}
|
|
5112
5130
|
}
|
|
5113
|
-
// ── Fix: Also skip scroll render for arrow key navigation ──────────────────
|
|
5114
|
-
// Arrow Down/Up calls focusNextNode() which calls ensureVirtualNodeVisible()
|
|
5115
|
-
// to scroll the target node into view. Without this guard, the scroll event
|
|
5116
|
-
// would trigger onVirtualScroll() which re-renders the window and resets the
|
|
5117
|
-
// focus that focusNextNode() just set. This manifests as tabindex resetting
|
|
5118
|
-
// to the first item instead of staying on the arrow-navigated target.
|
|
5119
|
-
// Arrow Right/Left (expand/collapse) also calls renderVirtualItems() which
|
|
5120
|
-
// calls li.focus() in branch 2; that can trigger a scroll on an edge node,
|
|
5121
|
-
// so we consume it here to prevent a stale RAF re-render.
|
|
5122
5131
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
|
|
5123
5132
|
|| e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
|
|
5124
5133
|
if (this.enableVirtualization) {
|
|
@@ -5480,10 +5489,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5480
5489
|
if (this.dataType === 1) {
|
|
5481
5490
|
this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
|
|
5482
5491
|
}
|
|
5483
|
-
// In virtual mode (with a fixed height), rebuild the flat node list and re-render
|
|
5484
|
-
// after deletion so the removed nodes are correctly reflected in the hierarchical
|
|
5485
|
-
// virtual DOM. When height is not provided, the tree uses normal rendering and
|
|
5486
|
-
// the DOM is already updated by removeNode() above.
|
|
5487
5492
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
5488
5493
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
5489
5494
|
this.rebuildVirtualNodeMap();
|
|
@@ -5552,16 +5557,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5552
5557
|
}
|
|
5553
5558
|
return parentsID;
|
|
5554
5559
|
};
|
|
5555
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5556
|
-
// VIRTUALIZATION — private helpers
|
|
5557
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5558
|
-
/**
|
|
5559
|
-
* Parses the `height` property (string like '400px' or number) to a pixel value.
|
|
5560
|
-
* Returns 0 if the value cannot be determined.
|
|
5561
|
-
*
|
|
5562
|
-
* @returns {number} Pixel height.
|
|
5563
|
-
* @private
|
|
5564
|
-
*/
|
|
5565
5560
|
TreeView.prototype.parseHeightToPixels = function () {
|
|
5566
5561
|
var h = this.height;
|
|
5567
5562
|
if (isNOU(h)) {
|
|
@@ -5573,13 +5568,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5573
5568
|
var parsed = parseFloat(h);
|
|
5574
5569
|
return isNaN(parsed) ? 0 : parsed;
|
|
5575
5570
|
};
|
|
5576
|
-
/**
|
|
5577
|
-
* Measures the height of the first rendered list item so subsequent calculations
|
|
5578
|
-
* use real layout values. Falls back to the stored `virtualItemHeight` default (40).
|
|
5579
|
-
*
|
|
5580
|
-
* @returns {number} Row height in pixels.
|
|
5581
|
-
* @private
|
|
5582
|
-
*/
|
|
5583
5571
|
TreeView.prototype.measureVirtualItemHeight = function () {
|
|
5584
5572
|
var firstLi = select('.' + LISTITEM, this.element);
|
|
5585
5573
|
if (firstLi && firstLi.offsetHeight > 0) {
|
|
@@ -5587,13 +5575,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5587
5575
|
}
|
|
5588
5576
|
return this.virtualItemHeight;
|
|
5589
5577
|
};
|
|
5590
|
-
/**
|
|
5591
|
-
* Rebuilds the cached Sets for checked/selected nodes to enable O(1) lookup
|
|
5592
|
-
* during the flatten operation instead of O(n) array indexOf calls.
|
|
5593
|
-
*
|
|
5594
|
-
* @returns {void}
|
|
5595
|
-
* @private
|
|
5596
|
-
*/
|
|
5597
5578
|
TreeView.prototype.rebuildVirtualCheckCache = function () {
|
|
5598
5579
|
this.virtualExpandedNodesSet.clear();
|
|
5599
5580
|
this.virtualCheckedNodesSet.clear();
|
|
@@ -5614,20 +5595,10 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5614
5595
|
}
|
|
5615
5596
|
}
|
|
5616
5597
|
};
|
|
5617
|
-
/**
|
|
5618
|
-
* Recursively walks the treeData (or groupedData for flat lists) and builds
|
|
5619
|
-
* `virtualFlatNodes` — an ordered list of every node that is currently visible
|
|
5620
|
-
* (i.e. its ancestors are all expanded). Uses cached Sets for O(1) state lookups.
|
|
5621
|
-
*
|
|
5622
|
-
* @returns {VirtualNodeData[]} Ordered flat list of visible nodes.
|
|
5623
|
-
* @private
|
|
5624
|
-
*/
|
|
5625
5598
|
TreeView.prototype.flattenVisibleNodes = function () {
|
|
5626
|
-
// Build cached Sets for fast lookups
|
|
5627
5599
|
this.rebuildVirtualCheckCache();
|
|
5628
5600
|
var result = [];
|
|
5629
5601
|
if (this.dataType === 1) {
|
|
5630
|
-
// Flat data with parentID field — use getChildGroup to find root nodes
|
|
5631
5602
|
var rootNodes = [];
|
|
5632
5603
|
if (this.groupedData) {
|
|
5633
5604
|
rootNodes = this.getChildGroup(this.groupedData, null, true);
|
|
@@ -5635,39 +5606,29 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5635
5606
|
if (!rootNodes || rootNodes.length === 0) {
|
|
5636
5607
|
rootNodes = this.rootData;
|
|
5637
5608
|
}
|
|
5638
|
-
// Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
|
|
5639
5609
|
rootNodes = this.getSortedData(rootNodes);
|
|
5640
5610
|
this.flattenNodeRecursive(rootNodes, 1, null, result);
|
|
5641
5611
|
}
|
|
5642
5612
|
else {
|
|
5643
|
-
// Hierarchical data
|
|
5644
5613
|
var sorted = this.getSortedData(this.rootData);
|
|
5645
5614
|
this.flattenNodeRecursive(sorted, 1, null, result);
|
|
5646
5615
|
}
|
|
5647
5616
|
return result;
|
|
5648
5617
|
};
|
|
5649
|
-
/**
|
|
5650
|
-
* Recursive helper for `flattenVisibleNodes`.
|
|
5651
|
-
*
|
|
5652
|
-
* @param {Object[]} nodes - Array of raw data objects at this level.
|
|
5653
|
-
* @param {number} level - 1-based depth level.
|
|
5654
|
-
* @param {string | null} parentId - The parent node id, or null for root.
|
|
5655
|
-
* @param {VirtualNodeData[]} result - Accumulator array.
|
|
5656
|
-
* @returns {void}
|
|
5657
|
-
* @private
|
|
5658
|
-
*/
|
|
5659
5618
|
TreeView.prototype.flattenNodeRecursive = function (nodes, level, parentId, result) {
|
|
5660
5619
|
if (!nodes || nodes.length === 0) {
|
|
5661
5620
|
return;
|
|
5662
5621
|
}
|
|
5663
|
-
|
|
5664
|
-
var childFieldName =
|
|
5665
|
-
var expandedFieldName =
|
|
5666
|
-
var isCheckedFieldName =
|
|
5667
|
-
var iconCssFieldName =
|
|
5668
|
-
var imageUrlFieldName =
|
|
5669
|
-
var idFieldName =
|
|
5670
|
-
var textFieldName =
|
|
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;
|
|
5671
5632
|
var needsSorting = this.sortOrder !== 'None';
|
|
5672
5633
|
var isDataTypeFlat = this.dataType === 1;
|
|
5673
5634
|
var enableVirtualization = this.enableVirtualization;
|
|
@@ -5675,70 +5636,93 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5675
5636
|
var raw = nodes[i];
|
|
5676
5637
|
var id = isNOU(raw[idFieldName]) ? '' : raw[idFieldName].toString();
|
|
5677
5638
|
var text = isNOU(raw[textFieldName]) ? '' : raw[textFieldName].toString();
|
|
5678
|
-
// Use expandedNodes as the single source of truth.
|
|
5679
5639
|
var inExpandedNodes = this.virtualExpandedNodesSet.has(id);
|
|
5680
|
-
var rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
|
|
5681
|
-
var
|
|
5640
|
+
var rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
|
|
5641
|
+
var expanded = inExpandedNodes || rawExpanded;
|
|
5682
5642
|
if (rawExpanded && id) {
|
|
5683
5643
|
this.expandedNodes.push(id);
|
|
5684
5644
|
this.virtualExpandedNodesSet.add(id);
|
|
5685
5645
|
}
|
|
5686
|
-
// Inline hasChildRecords check to avoid function call (optimization)
|
|
5687
5646
|
var hasChildren = false;
|
|
5688
|
-
|
|
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) {
|
|
5689
5652
|
if (this.groupedData) {
|
|
5690
5653
|
var groupedChild = this.getChildGroup(this.groupedData, id, false);
|
|
5691
5654
|
hasChildren = !isNOU(groupedChild) && groupedChild.length > 0;
|
|
5692
5655
|
}
|
|
5693
5656
|
}
|
|
5694
5657
|
else {
|
|
5695
|
-
var childrenObj = getValue(childFieldName, raw);
|
|
5658
|
+
var childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
|
|
5696
5659
|
hasChildren = !isNOU(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
|
|
5697
5660
|
}
|
|
5698
|
-
|
|
5699
|
-
if (
|
|
5700
|
-
|
|
5701
|
-
|
|
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)) {
|
|
5702
5675
|
this.virtualCheckedNodesSet.add(id);
|
|
5703
5676
|
if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
|
|
5704
5677
|
this.checkedNodes.push(id);
|
|
5705
5678
|
}
|
|
5706
5679
|
}
|
|
5707
5680
|
}
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
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
|
+
}
|
|
5690
|
+
if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
|
|
5691
|
+
var rawSelected = getValue(selectedFieldName, raw);
|
|
5692
|
+
if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
|
|
5693
|
+
this.virtualSelectedNodesSet.add(id);
|
|
5694
|
+
if (this.selectedNodes && this.selectedNodes.indexOf(id) === -1) {
|
|
5695
|
+
this.selectedNodes.push(id);
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
var selected = this.virtualSelectedNodesSet.has(id);
|
|
5712
5700
|
var isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
|
|
5713
5701
|
? 'indeterminate'
|
|
5714
5702
|
: this.virtualCheckedNodesSet.has(id);
|
|
5715
|
-
var isDisabled = false; // TODO: Check if any per-node disabled field exists
|
|
5716
5703
|
var iconCssVal = raw[iconCssFieldName];
|
|
5717
5704
|
var iconCss = isNOU(iconCssVal) ? '' : iconCssVal.toString();
|
|
5718
5705
|
var imageUrlVal = raw[imageUrlFieldName];
|
|
5719
5706
|
var imageUrl = isNOU(imageUrlVal) ? '' : imageUrlVal.toString();
|
|
5720
5707
|
var node = {
|
|
5721
|
-
id: id, text: text, level: level, parentId: parentId, hasChildren: hasChildren,
|
|
5722
|
-
|
|
5708
|
+
id: id, text: text, level: level, parentId: parentId, hasChildren: hasChildren, expanded: expanded,
|
|
5709
|
+
selected: selected, isChecked: isChecked, iconCss: iconCss, imageUrl: imageUrl
|
|
5723
5710
|
};
|
|
5724
5711
|
result.push(node);
|
|
5725
|
-
if (
|
|
5726
|
-
// Determine children list
|
|
5712
|
+
if (expanded && hasChildren) {
|
|
5727
5713
|
var children = [];
|
|
5728
5714
|
if (isDataTypeFlat) {
|
|
5729
5715
|
if (this.groupedData) {
|
|
5730
5716
|
children = this.getChildGroup(this.groupedData, id, false);
|
|
5731
|
-
// Apply sortOrder to flat-data children (same as hierarchical path)
|
|
5732
5717
|
if (needsSorting) {
|
|
5733
5718
|
children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
|
|
5734
5719
|
}
|
|
5735
5720
|
}
|
|
5736
5721
|
}
|
|
5737
5722
|
else {
|
|
5738
|
-
var rawChildren = getValue(childFieldName, raw);
|
|
5723
|
+
var rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
|
|
5739
5724
|
if (!isNOU(rawChildren) && Array.isArray(rawChildren)) {
|
|
5740
5725
|
children = rawChildren;
|
|
5741
|
-
// Only sort if needed
|
|
5742
5726
|
if (needsSorting) {
|
|
5743
5727
|
children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
|
|
5744
5728
|
}
|
|
@@ -5748,35 +5732,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5748
5732
|
}
|
|
5749
5733
|
}
|
|
5750
5734
|
};
|
|
5751
|
-
/**
|
|
5752
|
-
* Checks whether a raw datasource node has child records.
|
|
5753
|
-
*
|
|
5754
|
-
* @param {Object} raw - Raw datasource object.
|
|
5755
|
-
* @returns {boolean} True if children exist.
|
|
5756
|
-
* @private
|
|
5757
|
-
*/
|
|
5758
|
-
TreeView.prototype.hasChildRecords = function (raw) {
|
|
5759
|
-
if (this.dataType === 1) {
|
|
5760
|
-
if (!this.groupedData) {
|
|
5761
|
-
return false;
|
|
5762
|
-
}
|
|
5763
|
-
var rawId = raw[this.fields.id];
|
|
5764
|
-
if (isNOU(rawId)) {
|
|
5765
|
-
return false;
|
|
5766
|
-
}
|
|
5767
|
-
var groupedChild = this.getChildGroup(this.groupedData, rawId.toString(), false);
|
|
5768
|
-
return !isNOU(groupedChild) && groupedChild.length > 0;
|
|
5769
|
-
}
|
|
5770
|
-
var childField = this.fields.child.toString();
|
|
5771
|
-
var children = getValue(childField, raw);
|
|
5772
|
-
return !isNOU(children) && Array.isArray(children) && children.length > 0;
|
|
5773
|
-
};
|
|
5774
|
-
/**
|
|
5775
|
-
* Rebuilds `virtualNodeMap` (id → index) from the current `virtualFlatNodes`.
|
|
5776
|
-
*
|
|
5777
|
-
* @returns {void}
|
|
5778
|
-
* @private
|
|
5779
|
-
*/
|
|
5780
5735
|
TreeView.prototype.rebuildVirtualNodeMap = function () {
|
|
5781
5736
|
this.virtualNodeMap.clear();
|
|
5782
5737
|
for (var i = 0; i < this.virtualFlatNodes.length; i++) {
|
|
@@ -5809,49 +5764,12 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5809
5764
|
var paddingBottom = Math.max(0, (total - end) * itemH);
|
|
5810
5765
|
return { paddingTop: paddingTop, paddingBottom: paddingBottom };
|
|
5811
5766
|
};
|
|
5812
|
-
/**
|
|
5813
|
-
* Renders the virtual window into a **nested** `<ul>/<li>` hierarchy identical to
|
|
5814
|
-
* the non-virtualized DOM structure.
|
|
5815
|
-
*
|
|
5816
|
-
* ## Scroll math
|
|
5817
|
-
* The flat-index ↔ pixel mapping stays unchanged: every entry in
|
|
5818
|
-
* `virtualFlatNodes` represents exactly one `itemHeight` row.
|
|
5819
|
-
* `paddingTop = startIndex * itemHeight` and
|
|
5820
|
-
* `paddingBottom = (total - endIndex) * itemHeight` keep the scrollbar
|
|
5821
|
-
* thumb in the correct position.
|
|
5822
|
-
*
|
|
5823
|
-
* ## Ancestor injection
|
|
5824
|
-
* When `startIndex` points at a child node whose ancestor(s) have already
|
|
5825
|
-
* scrolled above the viewport (their flat indices are < startIndex), the
|
|
5826
|
-
* ancestors would normally be missing from the DOM, forcing the children to
|
|
5827
|
-
* fall back to the root `<ul>` as flat siblings — producing wrong HTML.
|
|
5828
|
-
*
|
|
5829
|
-
* To fix this, before rendering the window we walk the ancestor chain of the
|
|
5830
|
-
* first visible node and collect every ancestor that lies **outside** the
|
|
5831
|
-
* window. Those ancestors are injected as real `<li>` containers at the
|
|
5832
|
-
* very top of the root `<ul>`. To compensate for their presence, `paddingTop`
|
|
5833
|
-
* is reduced by `injectedCount * itemHeight` so the total scroll height
|
|
5834
|
-
* remains the same.
|
|
5835
|
-
*
|
|
5836
|
-
* Once the ancestors are in the DOM they are registered in `renderedParentMap`
|
|
5837
|
-
* so the normal window-rendering loop can nest children inside them exactly
|
|
5838
|
-
* as it does when both parent and child are in the same window.
|
|
5839
|
-
*
|
|
5840
|
-
* @returns {void}
|
|
5841
|
-
* @private
|
|
5842
|
-
*/
|
|
5843
5767
|
TreeView.prototype.renderVirtualItems = function () {
|
|
5844
5768
|
if (!this.ulElement) {
|
|
5845
5769
|
return;
|
|
5846
5770
|
}
|
|
5847
5771
|
var ul = this.ulElement;
|
|
5848
|
-
// ── Snapshot focused ID BEFORE clearing the DOM ───────────────────────────
|
|
5849
|
-
// Removing a focused <li> from the DOM triggers a synchronous `blur` event,
|
|
5850
|
-
// which calls focusOut() and sets virtualFocusedId = null. Capturing the id
|
|
5851
|
-
// here (before any removeChild) ensures focus is correctly restored to the
|
|
5852
|
-
// same node after re-render even when keyboard navigation was in progress.
|
|
5853
5772
|
var focusedUid = this.virtualFocusedId || null;
|
|
5854
|
-
// ── Clear previous render (direct children only) ─────────────────────────
|
|
5855
5773
|
var existingItems = ul.querySelectorAll(':scope > li');
|
|
5856
5774
|
for (var i = existingItems.length - 1; i >= 0; i--) {
|
|
5857
5775
|
ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
|
|
@@ -5862,26 +5780,22 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5862
5780
|
if (this.virtualStartIndex < this.virtualFlatNodes.length) {
|
|
5863
5781
|
var firstNode = this.virtualFlatNodes[this.virtualStartIndex];
|
|
5864
5782
|
if (firstNode.parentId) {
|
|
5865
|
-
// Build the ancestor chain from root down to the immediate parent
|
|
5866
5783
|
var ancestors = [];
|
|
5867
5784
|
var searchId = firstNode.parentId;
|
|
5868
5785
|
while (searchId) {
|
|
5869
5786
|
var ancestorIdx = this.virtualNodeMap.get(searchId);
|
|
5870
5787
|
if (!isNOU(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
|
|
5871
5788
|
var ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
|
|
5872
|
-
ancestors.unshift(ancestor);
|
|
5789
|
+
ancestors.unshift(ancestor);
|
|
5873
5790
|
searchId = ancestor.parentId;
|
|
5874
5791
|
}
|
|
5875
5792
|
else {
|
|
5876
5793
|
break;
|
|
5877
5794
|
}
|
|
5878
5795
|
}
|
|
5879
|
-
// Inject each ancestor as a real <li> into the root <ul>
|
|
5880
|
-
// and register it in renderedParentMap for child-nesting below.
|
|
5881
5796
|
for (var a = 0; a < ancestors.length; a++) {
|
|
5882
5797
|
var ancestor = ancestors[parseInt(a.toString(), 10)];
|
|
5883
5798
|
var ancestorLi = this.createVirtualListItem(ancestor, false);
|
|
5884
|
-
// Find the correct container: root or a previously injected ancestor's <ul>
|
|
5885
5799
|
var targetUl = ul;
|
|
5886
5800
|
if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
|
|
5887
5801
|
var pe = renderedParentMap.get(ancestor.parentId);
|
|
@@ -5900,26 +5814,20 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5900
5814
|
}
|
|
5901
5815
|
}
|
|
5902
5816
|
}
|
|
5903
|
-
// ── Scroll-space padding (adjusted for injected ancestors) ────────────────
|
|
5904
5817
|
var total = this.virtualFlatNodes.length;
|
|
5905
5818
|
var start = this.virtualStartIndex;
|
|
5906
5819
|
var end = this.virtualEndIndex;
|
|
5907
5820
|
var rawPaddingTop = start * itemH;
|
|
5908
|
-
// Subtract the height of injected ancestors so the scroll position is
|
|
5909
|
-
// exact: those ancestor rows are now real DOM elements, not hidden space.
|
|
5910
5821
|
var adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
|
|
5911
5822
|
ul.style.paddingTop = adjustedPaddingTop + 'px';
|
|
5912
5823
|
ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
|
|
5913
|
-
// ── Render window items (nested via renderedParentMap) ────────────────────
|
|
5914
5824
|
var liListBuf = [];
|
|
5915
5825
|
for (var i = start; i < end; i++) {
|
|
5916
5826
|
var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
|
|
5917
5827
|
var li = this.createVirtualListItem(node, i === start);
|
|
5918
|
-
// Find the correct parent container
|
|
5919
5828
|
var targetUl = ul;
|
|
5920
5829
|
if (node.parentId && renderedParentMap.has(node.parentId)) {
|
|
5921
5830
|
var parentEntry = renderedParentMap.get(node.parentId);
|
|
5922
|
-
// Lazily create the child <ul> the first time a child needs it
|
|
5923
5831
|
if (!parentEntry.childUl) {
|
|
5924
5832
|
parentEntry.childUl = this.createElement('ul', {
|
|
5925
5833
|
className: PARENTITEM + ' e-ul',
|
|
@@ -5931,30 +5839,11 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5931
5839
|
}
|
|
5932
5840
|
targetUl.appendChild(li);
|
|
5933
5841
|
liListBuf.push(li);
|
|
5934
|
-
|
|
5935
|
-
if (node.isExpanded && node.hasChildren) {
|
|
5842
|
+
if (node.expanded && node.hasChildren) {
|
|
5936
5843
|
renderedParentMap.set(node.id, { li: li, childUl: null });
|
|
5937
5844
|
}
|
|
5938
5845
|
}
|
|
5939
|
-
// ── Restore focus / tabindex ──────────────────────────────────────────────
|
|
5940
|
-
// In virtual mode only the li that carries the 'e-active' class should be
|
|
5941
|
-
// focusable (tabindex='0'). All other rendered li elements must be set
|
|
5942
|
-
// to tabindex='-1' so the tab order remains unambiguous as the window
|
|
5943
|
-
// changes during scrolling.
|
|
5944
5846
|
var focusRestored = false;
|
|
5945
|
-
// 1. Prefer the currently active node if it is in the rendered window.
|
|
5946
|
-
for (var k = 0; k < liListBuf.length; k++) {
|
|
5947
|
-
var li = liListBuf[parseInt(k.toString(), 10)];
|
|
5948
|
-
if (li.classList.contains(ACTIVE)) {
|
|
5949
|
-
li.setAttribute('tabindex', '0');
|
|
5950
|
-
// ── Fix: ensure FOCUS class set on active node too ──
|
|
5951
|
-
// This ensures focusIn() guard condition is met: tabindex='0' && FOCUS class
|
|
5952
|
-
addClass([li], FOCUS);
|
|
5953
|
-
focusRestored = true;
|
|
5954
|
-
break;
|
|
5955
|
-
}
|
|
5956
|
-
}
|
|
5957
|
-
// 2. Otherwise fall back to the previously focused node (if it is in window).
|
|
5958
5847
|
if (!focusRestored && focusedUid) {
|
|
5959
5848
|
for (var k = 0; k < liListBuf.length; k++) {
|
|
5960
5849
|
var li = liListBuf[parseInt(k.toString(), 10)];
|
|
@@ -5962,22 +5851,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5962
5851
|
li.setAttribute('tabindex', '0');
|
|
5963
5852
|
addClass([li], FOCUS);
|
|
5964
5853
|
addClass([li], FOCUSED);
|
|
5965
|
-
// ── Fix: restore actual browser focus so keyboard events keep working ──
|
|
5966
|
-
// After renderVirtualItems() replaces all <li> elements the old element
|
|
5967
|
-
// is gone from the DOM and document.activeElement falls back to
|
|
5968
|
-
// document.body. Setting tabindex + classes is not enough — the new
|
|
5969
|
-
// element also needs a programmatic .focus() call so subsequent keyboard
|
|
5970
|
-
// events (Arrow keys, Home, End, etc.) are delivered to the TreeView.
|
|
5971
|
-
// Re-attach the blur→focusOut handler so virtualFocusedId is cleared
|
|
5972
|
-
// correctly when the user tabs away.
|
|
5973
|
-
// ── Use preventScroll:true ────────────────────────────────────────────
|
|
5974
|
-
// Without preventScroll the browser scrolls the container to bring this
|
|
5975
|
-
// node into view during the re-render, changing scrollTop AFTER the
|
|
5976
|
-
// correct paddingTop has been applied. This is the root cause of the
|
|
5977
|
-
// KBN expand position-jump bug: the render is correct but the browser
|
|
5978
|
-
// silently scrolls the viewport, making the parent node appear to jump.
|
|
5979
|
-
// ensureVirtualNodeVisible() handles any legitimate scroll-into-view
|
|
5980
|
-
// needs before renderVirtualItems() is called, so scroll here is wrong.
|
|
5981
5854
|
EventHandler.add(li, 'blur', this.focusOut, this);
|
|
5982
5855
|
li.focus({ preventScroll: true });
|
|
5983
5856
|
focusRestored = true;
|
|
@@ -5985,53 +5858,36 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5985
5858
|
}
|
|
5986
5859
|
}
|
|
5987
5860
|
}
|
|
5988
|
-
// 3. Finally default to the first item in the window.
|
|
5989
5861
|
if (!focusRestored && liListBuf.length > 0) {
|
|
5990
5862
|
var firstLi = liListBuf[0];
|
|
5991
5863
|
firstLi.setAttribute('tabindex', '0');
|
|
5992
5864
|
}
|
|
5993
|
-
// ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
|
|
5994
|
-
// This prevents stale tabindex="0" on multiple nodes which breaks Tab order
|
|
5995
5865
|
for (var k = 0; k < liListBuf.length; k++) {
|
|
5996
5866
|
var li = liListBuf[parseInt(k.toString(), 10)];
|
|
5997
5867
|
if (li.getAttribute('tabindex') !== '0') {
|
|
5998
5868
|
li.setAttribute('tabindex', '-1');
|
|
5999
5869
|
}
|
|
6000
5870
|
}
|
|
6001
|
-
// Keep liList in sync so keyboard-navigation helpers stay functional.
|
|
6002
5871
|
this.liList = liListBuf;
|
|
6003
5872
|
};
|
|
6004
|
-
/**
|
|
6005
|
-
* Creates a single `<li>` element for the virtual window representing `node`.
|
|
6006
|
-
*
|
|
6007
|
-
* @param {VirtualNodeData} node - The node data.
|
|
6008
|
-
* @param {boolean} _isFirst - True if this is the first rendered item (unused here but kept for potential override).
|
|
6009
|
-
* @returns {HTMLElement} The constructed `<li>` element.
|
|
6010
|
-
* @private
|
|
6011
|
-
*/
|
|
6012
5873
|
TreeView.prototype.createVirtualListItem = function (node, _isFirst) {
|
|
6013
5874
|
var li = this.createElement('li', { className: LISTITEM });
|
|
6014
|
-
// Level class for CSS indentation
|
|
6015
5875
|
li.classList.add('e-level-' + node.level);
|
|
6016
5876
|
li.setAttribute('data-uid', node.id);
|
|
6017
5877
|
li.setAttribute('aria-level', node.level.toString());
|
|
6018
5878
|
li.setAttribute('role', 'treeitem');
|
|
6019
5879
|
li.setAttribute('tabindex', '-1');
|
|
6020
|
-
// Full-row overlay (required for fullRowSelect mode)
|
|
6021
5880
|
if (this.fullRowSelect) {
|
|
6022
5881
|
var fullRow = this.createElement('div', { className: FULLROW });
|
|
6023
5882
|
li.appendChild(fullRow);
|
|
6024
5883
|
}
|
|
6025
|
-
// Text/icon wrapper
|
|
6026
5884
|
var textWrap = this.createElement('div', { className: TEXTWRAP });
|
|
6027
|
-
// Expand/collapse icon
|
|
6028
5885
|
if (node.hasChildren) {
|
|
6029
5886
|
var iconSpan = this.createElement('div', {
|
|
6030
|
-
className: ICON + ' ' + (node.
|
|
5887
|
+
className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
|
|
6031
5888
|
});
|
|
6032
5889
|
textWrap.appendChild(iconSpan);
|
|
6033
5890
|
}
|
|
6034
|
-
// Checkbox
|
|
6035
5891
|
if (this.showCheckBox) {
|
|
6036
5892
|
var checkWrap = createCheckBox(this.createElement, false, {
|
|
6037
5893
|
checked: node.isChecked === true,
|
|
@@ -6045,29 +5901,32 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6045
5901
|
}
|
|
6046
5902
|
textWrap.appendChild(checkWrap);
|
|
6047
5903
|
}
|
|
6048
|
-
// Icon
|
|
6049
5904
|
if (node.iconCss) {
|
|
6050
5905
|
var iconEl = this.createElement('span', {
|
|
6051
5906
|
className: ICON + ' ' + node.iconCss
|
|
6052
5907
|
});
|
|
6053
5908
|
textWrap.appendChild(iconEl);
|
|
6054
5909
|
}
|
|
6055
|
-
// Image
|
|
6056
5910
|
if (node.imageUrl) {
|
|
6057
5911
|
var img = document.createElement('img');
|
|
6058
5912
|
img.src = node.imageUrl;
|
|
6059
5913
|
img.classList.add(IMAGE);
|
|
6060
5914
|
textWrap.appendChild(img);
|
|
6061
5915
|
}
|
|
6062
|
-
// Text anchor
|
|
6063
5916
|
var anchor = this.createElement('span', {
|
|
6064
5917
|
className: 'e-list-text',
|
|
6065
5918
|
innerHTML: node.text
|
|
6066
5919
|
});
|
|
6067
5920
|
textWrap.appendChild(anchor);
|
|
6068
5921
|
li.appendChild(textWrap);
|
|
6069
|
-
|
|
6070
|
-
|
|
5922
|
+
if (!isNOU(this.nodeTemplateFn)) {
|
|
5923
|
+
var rawData = this.findNodeInTreeData(node.id);
|
|
5924
|
+
if (!isNOU(rawData)) {
|
|
5925
|
+
anchor.innerHTML = '';
|
|
5926
|
+
this.renderNodeTemplate(rawData, anchor, node.id);
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
if (node.expanded) {
|
|
6071
5930
|
li.classList.add(EXPANDED);
|
|
6072
5931
|
li.setAttribute('aria-expanded', 'true');
|
|
6073
5932
|
}
|
|
@@ -6075,91 +5934,50 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6075
5934
|
li.classList.add(NODECOLLAPSED);
|
|
6076
5935
|
li.setAttribute('aria-expanded', 'false');
|
|
6077
5936
|
}
|
|
6078
|
-
if (node.
|
|
5937
|
+
if (node.selected) {
|
|
6079
5938
|
li.classList.add(ACTIVE);
|
|
6080
5939
|
li.setAttribute('aria-selected', 'true');
|
|
6081
5940
|
}
|
|
6082
|
-
if (node.isDisabled) {
|
|
6083
|
-
li.classList.add(DISABLED);
|
|
6084
|
-
li.setAttribute('aria-disabled', 'true');
|
|
6085
|
-
}
|
|
6086
5941
|
if (node.hasChildren) {
|
|
6087
5942
|
li.classList.add(CHILD);
|
|
6088
5943
|
}
|
|
6089
|
-
// Wire the focus event so `focusIn()` fires when the user Tabs into this
|
|
6090
|
-
// item. The non-virtualized path registers this in `beforeNodeCreate()`,
|
|
6091
|
-
// but virtual items never go through that callback, so we must register
|
|
6092
|
-
// it here to keep focus styling (e.g. `e-focused`) consistent between
|
|
6093
|
-
// virtual and non-virtual modes. `unWireEvents()` already removes this
|
|
6094
|
-
// handler during teardown by iterating over the rendered `<li>`s.
|
|
6095
5944
|
EventHandler.add(li, 'focus', this.focusIn, this);
|
|
6096
5945
|
return li;
|
|
6097
5946
|
};
|
|
6098
|
-
/**
|
|
6099
|
-
* Initialises the virtual-scrolling layout:
|
|
6100
|
-
* 1. Adds the `e-virtualization` CSS class and applies a fixed `height` + `overflow-y`.
|
|
6101
|
-
* 2. Flattens all visible nodes into `virtualFlatNodes`.
|
|
6102
|
-
* 3. Measures item height and viewport height.
|
|
6103
|
-
* 4. Computes the initial window and renders it.
|
|
6104
|
-
* 5. Wires the scroll event handler.
|
|
6105
|
-
*
|
|
6106
|
-
* @returns {void}
|
|
6107
|
-
* @private
|
|
6108
|
-
*/
|
|
6109
5947
|
TreeView.prototype.initVirtualization = function () {
|
|
6110
5948
|
addClass([this.element], VIRTUALIZATION);
|
|
6111
|
-
// Apply container height
|
|
6112
5949
|
var px = this.parseHeightToPixels();
|
|
6113
5950
|
if (px > 0) {
|
|
6114
5951
|
this.element.style.height = px + 'px';
|
|
6115
5952
|
this.element.style.overflowY = 'auto';
|
|
6116
5953
|
}
|
|
6117
|
-
// Ensure the inner UL is ready
|
|
6118
5954
|
if (!this.ulElement) {
|
|
6119
5955
|
this.ulElement = select('.' + PARENTITEM, this.element);
|
|
6120
5956
|
}
|
|
6121
5957
|
if (!this.ulElement) {
|
|
6122
5958
|
return;
|
|
6123
5959
|
}
|
|
6124
|
-
// Make the UL position-relative for the absolute spacers
|
|
6125
5960
|
this.ulElement.style.position = 'relative';
|
|
6126
|
-
// Rebuild flat list
|
|
6127
5961
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
6128
5962
|
this.rebuildVirtualNodeMap();
|
|
6129
|
-
// Measure dimensions
|
|
6130
5963
|
this.virtualViewportHeight = px > 0 ? px : this.element.offsetHeight;
|
|
6131
|
-
// Render a single probe item to measure the real item height before the full render
|
|
6132
5964
|
if (this.virtualFlatNodes.length > 0) {
|
|
6133
5965
|
var probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
|
|
6134
5966
|
this.ulElement.appendChild(probe);
|
|
6135
5967
|
this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
|
|
6136
5968
|
this.ulElement.removeChild(probe);
|
|
6137
5969
|
}
|
|
6138
|
-
// Compute window and render all items in the initial viewport
|
|
6139
5970
|
var win = this.getVirtualWindow();
|
|
6140
5971
|
this.virtualStartIndex = win.startIndex;
|
|
6141
5972
|
this.virtualEndIndex = win.endIndex;
|
|
6142
5973
|
this.renderVirtualItems();
|
|
6143
|
-
// Wire scroll
|
|
6144
5974
|
this.wireVirtualScrollEvents();
|
|
6145
5975
|
};
|
|
6146
|
-
/**
|
|
6147
|
-
* Wires the scroll event listener on the TreeView element.
|
|
6148
|
-
*
|
|
6149
|
-
* @returns {void}
|
|
6150
|
-
* @private
|
|
6151
|
-
*/
|
|
6152
5976
|
TreeView.prototype.wireVirtualScrollEvents = function () {
|
|
6153
5977
|
this.unwireVirtualScrollEvents();
|
|
6154
5978
|
this.virtualScrollBound = this.onVirtualScroll.bind(this);
|
|
6155
5979
|
this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
|
|
6156
5980
|
};
|
|
6157
|
-
/**
|
|
6158
|
-
* Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
|
|
6159
|
-
*
|
|
6160
|
-
* @returns {void}
|
|
6161
|
-
* @private
|
|
6162
|
-
*/
|
|
6163
5981
|
TreeView.prototype.unwireVirtualScrollEvents = function () {
|
|
6164
5982
|
if (this.virtualScrollBound) {
|
|
6165
5983
|
this.element.removeEventListener('scroll', this.virtualScrollBound);
|
|
@@ -6170,29 +5988,13 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6170
5988
|
this.virtualScrollRAF = null;
|
|
6171
5989
|
}
|
|
6172
5990
|
};
|
|
6173
|
-
/**
|
|
6174
|
-
* Scroll handler for the virtual TreeView. Throttled via `requestAnimationFrame`.
|
|
6175
|
-
* Shows the loading mask during fast scrolls, re-renders the window, then hides the mask.
|
|
6176
|
-
*
|
|
6177
|
-
* @returns {void}
|
|
6178
|
-
* @private
|
|
6179
|
-
*/
|
|
6180
5991
|
TreeView.prototype.onVirtualScroll = function () {
|
|
6181
5992
|
var _this = this;
|
|
6182
5993
|
if (this.virtualScrollRAF !== null) {
|
|
6183
5994
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
6184
5995
|
}
|
|
6185
|
-
// ── Tab-induced focus guard ────────────────────────────────────────────────
|
|
6186
|
-
// When the user Tabs into a `<li tabindex="0">` that is in the middle of
|
|
6187
|
-
// the treeview, the browser auto-scrolls the container so the focused
|
|
6188
|
-
// element becomes visible. That fires a `scroll` event here, but the
|
|
6189
|
-
// virtual window already contains the focused element — re-rendering
|
|
6190
|
-
// would destroy the focused `<li>` and break focus. Detect this case
|
|
6191
|
-
// via the flag set in `keyDownHandler` and skip the re-render entirely.
|
|
6192
5996
|
if (this.virtualSkipNextScrollRender) {
|
|
6193
5997
|
this.virtualSkipNextScrollRender = false;
|
|
6194
|
-
// Update prev scrollTop so a subsequent user-driven scroll still
|
|
6195
|
-
// calculates the correct delta.
|
|
6196
5998
|
this.virtualPrevScrollTop = this.element.scrollTop;
|
|
6197
5999
|
return;
|
|
6198
6000
|
}
|
|
@@ -6209,30 +6011,16 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6209
6011
|
_this.virtualStartIndex = win.startIndex;
|
|
6210
6012
|
_this.virtualEndIndex = win.endIndex;
|
|
6211
6013
|
_this.renderVirtualItems();
|
|
6212
|
-
// Hide the skeleton mask after a short delay so it stays visible long enough
|
|
6213
|
-
// for the user to perceive the loading indicator even on short scrolls.
|
|
6214
6014
|
setTimeout(function () {
|
|
6215
6015
|
_this.hideVirtualMask();
|
|
6216
|
-
// Keep the (now hidden) mask's coords fresh for the next show.
|
|
6217
6016
|
_this.updateMaskPosition();
|
|
6218
6017
|
}, 200);
|
|
6219
6018
|
});
|
|
6220
6019
|
};
|
|
6221
|
-
/**
|
|
6222
|
-
* Creates the skeleton mask overlay used to indicate loading during fast scrolls.
|
|
6223
|
-
* Skeleton instances are stored in `skeletonInstances` for later disposal.
|
|
6224
|
-
*
|
|
6225
|
-
* @returns {void}
|
|
6226
|
-
* @private
|
|
6227
|
-
*/
|
|
6228
6020
|
TreeView.prototype.createVirtualMask = function () {
|
|
6229
6021
|
if (this.virtualMaskElement) {
|
|
6230
6022
|
return;
|
|
6231
6023
|
}
|
|
6232
|
-
// Use position:fixed so the mask is anchored to the visible viewport (not to the
|
|
6233
|
-
// scrolling container). Width/height/left/top are set/refreshed by
|
|
6234
|
-
// updateMaskPosition() so the mask always covers the TreeView's visible area
|
|
6235
|
-
// regardless of how the page itself is scrolled.
|
|
6236
6024
|
var rect = this.element.getBoundingClientRect();
|
|
6237
6025
|
var scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
|
|
6238
6026
|
var maskParent = this.createElement('div', {
|
|
@@ -6245,20 +6033,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6245
6033
|
className: VIRTUAL_MASK_ITEM,
|
|
6246
6034
|
styles: "display:flex;align-items:center;height:" + this.virtualItemHeight + "px;padding:0 12px;gap:8px;"
|
|
6247
6035
|
});
|
|
6248
|
-
// Skeleton circle for icon placeholder
|
|
6249
|
-
// const iconHolder: HTMLElement = this.createElement('span', {
|
|
6250
|
-
// styles: `width:20px;height:20px;`
|
|
6251
|
-
// });
|
|
6252
|
-
// maskItem.appendChild(iconHolder);
|
|
6253
|
-
// const iconSkel: Skeleton = new Skeleton({
|
|
6254
|
-
// shape: 'Circle',
|
|
6255
|
-
// width: '20px',
|
|
6256
|
-
// height: '20px',
|
|
6257
|
-
// cssClass: 'e-virtual-skel-icon'
|
|
6258
|
-
// });
|
|
6259
|
-
// iconSkel.appendTo(iconHolder);
|
|
6260
|
-
// this.skeletonInstances.push(iconSkel);
|
|
6261
|
-
// Skeleton text bar
|
|
6262
6036
|
var textHolder = this.createElement('span');
|
|
6263
6037
|
textHolder.style.flex = '1';
|
|
6264
6038
|
textHolder.style.height = '14px';
|
|
@@ -6276,14 +6050,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6276
6050
|
this.virtualMaskElement = maskParent;
|
|
6277
6051
|
this.element.appendChild(maskParent);
|
|
6278
6052
|
};
|
|
6279
|
-
/**
|
|
6280
|
-
* Refreshes the mask position so it always overlays the TreeView's viewport.
|
|
6281
|
-
* Called on every show / scroll, since `position:fixed` is relative to the
|
|
6282
|
-
* page viewport, not to the TreeView scroll container.
|
|
6283
|
-
*
|
|
6284
|
-
* @returns {void}
|
|
6285
|
-
* @private
|
|
6286
|
-
*/
|
|
6287
6053
|
TreeView.prototype.updateMaskPosition = function () {
|
|
6288
6054
|
if (!this.virtualMaskElement) {
|
|
6289
6055
|
return;
|
|
@@ -6295,12 +6061,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6295
6061
|
this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
|
|
6296
6062
|
this.virtualMaskElement.style.height = rect.height + 'px';
|
|
6297
6063
|
};
|
|
6298
|
-
/**
|
|
6299
|
-
* Destroys all skeleton instances and removes the mask overlay from the DOM.
|
|
6300
|
-
*
|
|
6301
|
-
* @returns {void}
|
|
6302
|
-
* @private
|
|
6303
|
-
*/
|
|
6304
6064
|
TreeView.prototype.destroyVirtualMask = function () {
|
|
6305
6065
|
for (var i = this.skeletonInstances.length - 1; i >= 0; i--) {
|
|
6306
6066
|
this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
|
|
@@ -6313,52 +6073,26 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6313
6073
|
this.virtualMaskElement = null;
|
|
6314
6074
|
}
|
|
6315
6075
|
};
|
|
6316
|
-
/**
|
|
6317
|
-
* Positions and shows the skeleton mask at the current `scrollTop` position.
|
|
6318
|
-
*
|
|
6319
|
-
* @param {number} _scrollTop - Current scrollTop of the container.
|
|
6320
|
-
* @returns {void}
|
|
6321
|
-
* @private
|
|
6322
|
-
*/
|
|
6323
6076
|
TreeView.prototype.showVirtualMask = function (_scrollTop) {
|
|
6324
6077
|
if (!this.virtualMaskElement) {
|
|
6325
6078
|
this.createVirtualMask();
|
|
6326
6079
|
}
|
|
6327
6080
|
if (this.virtualMaskElement) {
|
|
6328
|
-
// Refresh the fixed-position coords in case the TreeView was moved/resized
|
|
6329
|
-
// since the last time the mask was shown.
|
|
6330
6081
|
this.updateMaskPosition();
|
|
6331
6082
|
this.virtualMaskElement.style.display = 'block';
|
|
6332
|
-
// Visually hide the real list while the skeleton is shown
|
|
6333
6083
|
if (this.ulElement) {
|
|
6334
6084
|
this.ulElement.style.visibility = 'hidden';
|
|
6335
6085
|
}
|
|
6336
6086
|
}
|
|
6337
6087
|
};
|
|
6338
|
-
/**
|
|
6339
|
-
* Hides the skeleton mask overlay after the virtual window has been rendered.
|
|
6340
|
-
*
|
|
6341
|
-
* @returns {void}
|
|
6342
|
-
* @private
|
|
6343
|
-
*/
|
|
6344
6088
|
TreeView.prototype.hideVirtualMask = function () {
|
|
6345
6089
|
if (this.virtualMaskElement) {
|
|
6346
6090
|
this.virtualMaskElement.style.display = 'none';
|
|
6347
|
-
// Restore real list visibility
|
|
6348
6091
|
if (this.ulElement) {
|
|
6349
6092
|
this.ulElement.style.visibility = '';
|
|
6350
6093
|
}
|
|
6351
6094
|
}
|
|
6352
6095
|
};
|
|
6353
|
-
/**
|
|
6354
|
-
* Ensures the node identified by `id` is scrolled into view inside the virtual container.
|
|
6355
|
-
* If the node falls outside the rendered window, the container's `scrollTop` is adjusted so
|
|
6356
|
-
* that the node becomes visible, the window is re-computed and `renderVirtualItems` is called.
|
|
6357
|
-
*
|
|
6358
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
6359
|
-
* @returns {void}
|
|
6360
|
-
* @private
|
|
6361
|
-
*/
|
|
6362
6096
|
TreeView.prototype.ensureVirtualNodeVisible = function (id) {
|
|
6363
6097
|
var idx = this.virtualNodeMap.get(id);
|
|
6364
6098
|
if (isNOU(idx)) {
|
|
@@ -6377,20 +6111,53 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6377
6111
|
this.element.scrollTop = nodeBottom - vpH;
|
|
6378
6112
|
}
|
|
6379
6113
|
else {
|
|
6380
|
-
return;
|
|
6114
|
+
return;
|
|
6381
6115
|
}
|
|
6382
|
-
// Re-render the new window synchronously so the <li> is in the DOM
|
|
6383
6116
|
var win = this.getVirtualWindow();
|
|
6384
6117
|
this.virtualStartIndex = win.startIndex;
|
|
6385
6118
|
this.virtualEndIndex = win.endIndex;
|
|
6386
6119
|
this.renderVirtualItems();
|
|
6387
6120
|
};
|
|
6121
|
+
TreeView.prototype.findNodeInTreeData = function (id) {
|
|
6122
|
+
if (!this.treeData) {
|
|
6123
|
+
return null;
|
|
6124
|
+
}
|
|
6125
|
+
var idFieldName = this.fields.id.toString();
|
|
6126
|
+
var childFieldName = this.fields.child.toString();
|
|
6127
|
+
var findRecursive = function (nodes) {
|
|
6128
|
+
if (!nodes || nodes.length === 0) {
|
|
6129
|
+
return null;
|
|
6130
|
+
}
|
|
6131
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
6132
|
+
var node = nodes[i];
|
|
6133
|
+
if (node[idFieldName] && node[idFieldName].toString() === id) {
|
|
6134
|
+
return node;
|
|
6135
|
+
}
|
|
6136
|
+
var children = getValue(childFieldName, node);
|
|
6137
|
+
if (children && Array.isArray(children)) {
|
|
6138
|
+
var found = findRecursive(children);
|
|
6139
|
+
if (found) {
|
|
6140
|
+
return found;
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
}
|
|
6144
|
+
return null;
|
|
6145
|
+
};
|
|
6146
|
+
return findRecursive(this.treeData);
|
|
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
|
+
};
|
|
6388
6159
|
TreeView.prototype.handleVirtualExpand = function (id, isKeyboardNav) {
|
|
6389
|
-
|
|
6390
|
-
// A scroll RAF queued before the expand click would run asynchronously after
|
|
6391
|
-
// this synchronous expand completes, re-rendering the window from a stale
|
|
6392
|
-
// scrollTop and moving the parent node to the wrong position. Cancel it now
|
|
6393
|
-
// so the expand render is authoritative.
|
|
6160
|
+
var _this = this;
|
|
6394
6161
|
if (this.virtualScrollRAF !== null) {
|
|
6395
6162
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
6396
6163
|
this.virtualScrollRAF = null;
|
|
@@ -6398,64 +6165,85 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6398
6165
|
if (this.expandedNodes.indexOf(id) === -1) {
|
|
6399
6166
|
this.expandedNodes.push(id);
|
|
6400
6167
|
}
|
|
6401
|
-
// Also update virtualExpandedNodesSet to keep it in sync
|
|
6402
6168
|
if (this.virtualExpandedNodesSet) {
|
|
6403
6169
|
this.virtualExpandedNodesSet.add(id);
|
|
6404
6170
|
}
|
|
6405
|
-
if (this.
|
|
6406
|
-
var acutalRootData = this.
|
|
6407
|
-
acutalRootData.
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
// (`id` comes from currLi.getAttribute('data-uid') in expandNode, where currLi
|
|
6412
|
-
// is getFocusedNode()). Set unconditionally — the old conditional guard
|
|
6413
|
-
// `!this.virtualFocusedId` could leave a stale or null value if a previous
|
|
6414
|
-
// RAF render (from arrow-key scroll) hadn't yet completed, causing branch 3
|
|
6415
|
-
// (first item) to fire in renderVirtualItems() instead of branch 2.
|
|
6416
|
-
// For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
|
|
6417
|
-
// renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
|
|
6418
|
-
// mouse-based expand/collapse should never add the e-focused class.
|
|
6171
|
+
if (this.treeData) {
|
|
6172
|
+
var acutalRootData = this.findNodeInTreeData(id);
|
|
6173
|
+
if (acutalRootData && this.fields && this.fields.expanded) {
|
|
6174
|
+
setValue(this.fields.expanded, true, acutalRootData);
|
|
6175
|
+
}
|
|
6176
|
+
}
|
|
6419
6177
|
if (isKeyboardNav) {
|
|
6420
6178
|
this.virtualFocusedId = id;
|
|
6421
6179
|
}
|
|
6422
6180
|
else {
|
|
6423
6181
|
this.virtualFocusedId = null;
|
|
6424
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) {
|
|
6425
6233
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
6426
6234
|
this.rebuildVirtualNodeMap();
|
|
6427
|
-
// ── Snapshot scrollTop before computing the window ────────────────────────
|
|
6428
|
-
// Read scrollTop once here and pass it into getVirtualWindow so that any
|
|
6429
|
-
// browser scroll-anchoring adjustment that happens when the padding values
|
|
6430
|
-
// change cannot affect which window we render.
|
|
6431
6235
|
var scrollTopSnapshot = this.element.scrollTop;
|
|
6432
6236
|
var win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
6433
6237
|
this.virtualStartIndex = win.startIndex;
|
|
6434
6238
|
this.virtualEndIndex = win.endIndex;
|
|
6435
|
-
// ── Block ALL scroll-triggered re-renders during this expand ─────────────
|
|
6436
|
-
// renderVirtualItems() calls li.focus() in branch 2 (keyboard focus restore).
|
|
6437
|
-
// The post-render focus() call below can also trigger browser scroll. Both
|
|
6438
|
-
// can fire the 'scroll' event and cause onVirtualScroll() to queue a RAF.
|
|
6439
|
-
// We set the skip flag ONCE here; it is consumed by the first scroll event.
|
|
6440
|
-
// To cover the second potential scroll (from the focus() call below) we use
|
|
6441
|
-
// preventScroll:true on that call so it never moves the container at all.
|
|
6442
6239
|
this.virtualSkipNextScrollRender = true;
|
|
6443
6240
|
this.renderVirtualItems();
|
|
6444
|
-
// renderVirtualItems() replaces all <li> elements so the old icon element that
|
|
6445
|
-
// had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
|
|
6446
|
-
// so the node can be collapsed/expanded again without being blocked.
|
|
6447
6241
|
var newLi = this.getElement(id);
|
|
6448
6242
|
if (newLi) {
|
|
6449
6243
|
var icon = select('.' + ICON, newLi);
|
|
6450
6244
|
if (icon) {
|
|
6451
6245
|
removeClass([icon], PROCESS);
|
|
6452
6246
|
}
|
|
6453
|
-
// ── Use preventScroll:true ────────────────────────────────────────────
|
|
6454
|
-
// Without preventScroll the browser scrolls the container to bring this
|
|
6455
|
-
// node into view, which fires a second 'scroll' event. The first scroll
|
|
6456
|
-
// was already consumed by virtualSkipNextScrollRender (set above), so the
|
|
6457
|
-
// second scroll would queue a new RAF that re-renders from the
|
|
6458
|
-
// focus-adjusted scrollTop — moving the parent node to the wrong position.
|
|
6459
6247
|
newLi.focus({ preventScroll: true });
|
|
6460
6248
|
}
|
|
6461
6249
|
if (this.isLoaded) {
|
|
@@ -6468,9 +6256,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6468
6256
|
}
|
|
6469
6257
|
};
|
|
6470
6258
|
TreeView.prototype.handleVirtualCollapse = function (id, isKeyboardNav) {
|
|
6471
|
-
// ── Cancel any pending scroll RAF ─────────────────────────────────────────
|
|
6472
|
-
// Mirrors handleVirtualExpand: a queued RAF from a previous scroll event
|
|
6473
|
-
// must not override the collapse render with a stale window.
|
|
6474
6259
|
if (this.virtualScrollRAF !== null) {
|
|
6475
6260
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
6476
6261
|
this.virtualScrollRAF = null;
|
|
@@ -6479,21 +6264,15 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6479
6264
|
if (idx !== -1) {
|
|
6480
6265
|
this.expandedNodes.splice(idx, 1);
|
|
6481
6266
|
}
|
|
6482
|
-
// Also update virtualExpandedNodesSet to keep it in sync
|
|
6483
6267
|
if (this.virtualExpandedNodesSet) {
|
|
6484
6268
|
this.virtualExpandedNodesSet.delete(id);
|
|
6485
6269
|
}
|
|
6486
|
-
if (this.
|
|
6487
|
-
var acutalRootData = this.
|
|
6488
|
-
acutalRootData.
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
// definitively the focused node. Set unconditionally to avoid the stale/null
|
|
6493
|
-
// state that could cause the RAF re-render to fall through to branch 3.
|
|
6494
|
-
// For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
|
|
6495
|
-
// renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
|
|
6496
|
-
// mouse-based expand/collapse should never add the e-focused class.
|
|
6270
|
+
if (this.treeData) {
|
|
6271
|
+
var acutalRootData = this.findNodeInTreeData(id);
|
|
6272
|
+
if (acutalRootData && this.fields && this.fields.expanded) {
|
|
6273
|
+
setValue(this.fields.expanded, false, acutalRootData);
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6497
6276
|
if (isKeyboardNav) {
|
|
6498
6277
|
this.virtualFocusedId = id;
|
|
6499
6278
|
}
|
|
@@ -6502,28 +6281,18 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6502
6281
|
}
|
|
6503
6282
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
6504
6283
|
this.rebuildVirtualNodeMap();
|
|
6505
|
-
// ── Snapshot scrollTop before computing the window ────────────────────────
|
|
6506
|
-
// Same rationale as handleVirtualExpand: capture scrollTop before any focus()
|
|
6507
|
-
// call or padding change can cause the browser to auto-scroll.
|
|
6508
6284
|
var scrollTopSnapshot = this.element.scrollTop;
|
|
6509
6285
|
var win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
6510
6286
|
this.virtualStartIndex = win.startIndex;
|
|
6511
6287
|
this.virtualEndIndex = win.endIndex;
|
|
6512
|
-
// ── Block ALL scroll-triggered re-renders during this collapse ────────────
|
|
6513
|
-
// Same as handleVirtualExpand: use preventScroll:true on the focus() call
|
|
6514
|
-
// below so the second potential scroll event is never fired.
|
|
6515
6288
|
this.virtualSkipNextScrollRender = true;
|
|
6516
6289
|
this.renderVirtualItems();
|
|
6517
|
-
// renderVirtualItems() replaces all <li> elements so the old icon element that
|
|
6518
|
-
// had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
|
|
6519
|
-
// so the node can be expanded/collapsed again without being blocked.
|
|
6520
6290
|
var newLi = this.getElement(id);
|
|
6521
6291
|
if (newLi) {
|
|
6522
6292
|
var icon = select('.' + ICON, newLi);
|
|
6523
6293
|
if (icon) {
|
|
6524
6294
|
removeClass([icon], PROCESS);
|
|
6525
6295
|
}
|
|
6526
|
-
// ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
|
|
6527
6296
|
newLi.focus({ preventScroll: true });
|
|
6528
6297
|
}
|
|
6529
6298
|
if (this.isLoaded) {
|
|
@@ -6535,36 +6304,18 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6535
6304
|
}
|
|
6536
6305
|
}
|
|
6537
6306
|
};
|
|
6538
|
-
|
|
6539
|
-
* Updates the selected state of a node in `virtualFlatNodes` and re-renders the window.
|
|
6540
|
-
*
|
|
6541
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
6542
|
-
* @param {boolean} isSelected - Whether the node is now selected.
|
|
6543
|
-
* @returns {void}
|
|
6544
|
-
* @private
|
|
6545
|
-
*/
|
|
6546
|
-
TreeView.prototype.updateVirtualNodeSelection = function (id, isSelected) {
|
|
6307
|
+
TreeView.prototype.updateVirtualNodeSelection = function (id, selected) {
|
|
6547
6308
|
var idx = this.virtualNodeMap.get(id);
|
|
6548
6309
|
if (!isNOU(idx)) {
|
|
6549
|
-
this.virtualFlatNodes[parseInt(idx.toString(), 10)].
|
|
6310
|
+
this.virtualFlatNodes[parseInt(idx.toString(), 10)].selected = selected;
|
|
6550
6311
|
this.renderVirtualItems();
|
|
6551
6312
|
}
|
|
6552
6313
|
};
|
|
6553
|
-
/**
|
|
6554
|
-
* Updates the checkbox state of a node in `virtualFlatNodes` and re-renders the window.
|
|
6555
|
-
*
|
|
6556
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
6557
|
-
* @param {'check' | 'uncheck' | 'indeterminate'} state - The new check state.
|
|
6558
|
-
* @returns {void}
|
|
6559
|
-
* @private
|
|
6560
|
-
*/
|
|
6561
6314
|
TreeView.prototype.updateVirtualNodeCheckState = function (id, state) {
|
|
6562
6315
|
var idx = this.virtualNodeMap.get(id);
|
|
6563
6316
|
if (!isNOU(idx)) {
|
|
6564
6317
|
this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
|
|
6565
6318
|
state === 'check' ? true : state === 'indeterminate' ? 'indeterminate' : false;
|
|
6566
|
-
// Keep the persistent indeterminate set in sync so flattenVisibleNodes()
|
|
6567
|
-
// correctly restores state after any full list rebuild (e.g. collapse).
|
|
6568
6319
|
if (state === 'indeterminate') {
|
|
6569
6320
|
this.virtualIndeterminateNodesSet.add(id);
|
|
6570
6321
|
}
|
|
@@ -6574,9 +6325,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6574
6325
|
this.renderVirtualItems();
|
|
6575
6326
|
}
|
|
6576
6327
|
};
|
|
6577
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
6578
|
-
// END VIRTUALIZATION
|
|
6579
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
6580
6328
|
/**
|
|
6581
6329
|
* Called internally if any of the property value changed.
|
|
6582
6330
|
*
|
|
@@ -6721,7 +6469,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6721
6469
|
}
|
|
6722
6470
|
}
|
|
6723
6471
|
else {
|
|
6724
|
-
// Tear down virtualization and do a full re-render
|
|
6725
6472
|
this.unwireVirtualScrollEvents();
|
|
6726
6473
|
this.destroyVirtualMask();
|
|
6727
6474
|
removeClass([this.element], VIRTUALIZATION);
|
|
@@ -6732,7 +6479,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6732
6479
|
break;
|
|
6733
6480
|
case 'height':
|
|
6734
6481
|
if (this.enableVirtualization) {
|
|
6735
|
-
// Re-initialise so the new height takes effect
|
|
6736
6482
|
this.unwireVirtualScrollEvents();
|
|
6737
6483
|
this.initVirtualization();
|
|
6738
6484
|
}
|
|
@@ -6750,7 +6496,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6750
6496
|
* @returns {void}
|
|
6751
6497
|
*/
|
|
6752
6498
|
TreeView.prototype.destroy = function () {
|
|
6753
|
-
// Tear down virtualization before standard cleanup
|
|
6754
6499
|
if (this.enableVirtualization) {
|
|
6755
6500
|
this.unwireVirtualScrollEvents();
|
|
6756
6501
|
this.destroyVirtualMask();
|
|
@@ -6841,10 +6586,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6841
6586
|
if (isNOU(fNode)) {
|
|
6842
6587
|
this.setNodeFocusable();
|
|
6843
6588
|
}
|
|
6844
|
-
// In virtual mode (with a fixed height), rebuild the flat node list and re-render
|
|
6845
|
-
// the window so the newly added nodes appear in the correct hierarchical position.
|
|
6846
|
-
// When height is not provided, the tree uses normal rendering and the DOM is
|
|
6847
|
-
// already updated by addGivenNodes() above.
|
|
6848
6589
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
6849
6590
|
this.setRootData();
|
|
6850
6591
|
this.rebuildVirtualCheckCache();
|
|
@@ -6914,7 +6655,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6914
6655
|
}
|
|
6915
6656
|
for (var i = 0; i < nodes.length; i++) {
|
|
6916
6657
|
var node = nodes[parseInt(i.toString(), 10)];
|
|
6917
|
-
node['
|
|
6658
|
+
node['expanded'] = false;
|
|
6918
6659
|
var children = getValue(_this.fields.child.toString(), node);
|
|
6919
6660
|
if (children && Array.isArray(children)) {
|
|
6920
6661
|
setAllCollapsed_1(children);
|
|
@@ -7014,7 +6755,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
7014
6755
|
var nodeId = getValue(idFieldName_1, node);
|
|
7015
6756
|
if (node.hasChild) {
|
|
7016
6757
|
allParentIds_1.push(nodeId);
|
|
7017
|
-
node.
|
|
6758
|
+
node.expanded = true;
|
|
7018
6759
|
}
|
|
7019
6760
|
}
|
|
7020
6761
|
};
|