@syncfusion/ej2-navigations 34.2.3 → 34.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ej2-navigations.min.js +1 -10
- package/dist/ej2-navigations.umd.min.js +1 -10
- package/dist/ej2-navigations.umd.min.js.map +1 -1
- package/dist/es6/ej2-navigations.es2015.js +368 -553
- package/dist/es6/ej2-navigations.es2015.js.map +1 -1
- package/dist/es6/ej2-navigations.es5.js +373 -556
- package/dist/es6/ej2-navigations.es5.js.map +1 -1
- package/dist/global/ej2-navigations.min.js +1 -10
- package/dist/global/ej2-navigations.min.js.map +1 -1
- package/dist/global/index.d.ts +0 -9
- package/package.json +3 -3
- package/src/toolbar/toolbar.d.ts +2 -0
- package/src/toolbar/toolbar.js +16 -0
- package/src/treeview/treeview.d.ts +8 -221
- package/src/treeview/treeview.js +357 -556
package/src/treeview/treeview.js
CHANGED
|
@@ -228,55 +228,28 @@ 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;
|
|
251
|
+
_this.filterCheckedNodes = [];
|
|
252
|
+
_this.filterIndeterminateNodes = [];
|
|
280
253
|
return _this;
|
|
281
254
|
}
|
|
282
255
|
TreeView_1 = TreeView;
|
|
@@ -577,17 +550,12 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
577
550
|
return false;
|
|
578
551
|
}
|
|
579
552
|
};
|
|
553
|
+
TreeView.prototype.isRemoteDataLoadOnDemand = function () {
|
|
554
|
+
var remoteDataSource = (this.fields.dataSource instanceof DataManager) ||
|
|
555
|
+
(this.fields.child && this.fields.child.dataSource instanceof DataManager ? true : false);
|
|
556
|
+
return !remoteDataSource && !this.loadOnDemand;
|
|
557
|
+
};
|
|
580
558
|
TreeView.prototype.renderItems = function (isSorted) {
|
|
581
|
-
// In virtual mode (with a fixed height) create only the container <ul> —
|
|
582
|
-
// initVirtualization will populate the items directly, avoiding the O(n)
|
|
583
|
-
// ListBase.createList cost.
|
|
584
|
-
// updateCheckedStateFromDS / updateParentCheckState are intentionally skipped here:
|
|
585
|
-
// they call getCheckedNodeDetails which traverses the DOM (no DOM exists yet in
|
|
586
|
-
// virtual mode) and would throw on empty recursion results. The initial checked
|
|
587
|
-
// state is derived from the raw data inside flattenNodeRecursive instead.
|
|
588
|
-
//
|
|
589
|
-
// When enableVirtualization is true but height is NOT provided, we fall through
|
|
590
|
-
// to the normal (non-virtualized) rendering path so the tree is still visible.
|
|
591
559
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
592
560
|
this.ulElement = this.createElement('ul', {
|
|
593
561
|
className: PARENTITEM + ' ' + 'e-ul',
|
|
@@ -775,7 +743,9 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
775
743
|
if (this_1.checkedNodes.indexOf(childId) !== -1) {
|
|
776
744
|
count++;
|
|
777
745
|
}
|
|
778
|
-
else if (this_1.element.classList.contains('e-filtering') &&
|
|
746
|
+
else if (this_1.element.classList.contains('e-filtering') && (this_1.isRemoteDataLoadOnDemand() ?
|
|
747
|
+
this_1.filterCheckedNodes.findIndex(function (id) { return id === childId; }) !== -1 :
|
|
748
|
+
OldCheckedNodes.findIndex(function (e) { return e['id'] === childId; }) !== -1)) {
|
|
779
749
|
count++;
|
|
780
750
|
}
|
|
781
751
|
};
|
|
@@ -1094,6 +1064,23 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1094
1064
|
}
|
|
1095
1065
|
});
|
|
1096
1066
|
var parentNodeChecked = false;
|
|
1067
|
+
if (this.isRemoteDataLoadOnDemand() && this.element.classList.contains('e-filtering') && this.autoCheck) {
|
|
1068
|
+
if (this.filterCheckedNodes.indexOf(dataUid_1) !== -1) {
|
|
1069
|
+
this.changeState(checkBoxEle, 'check', null, true, true);
|
|
1070
|
+
}
|
|
1071
|
+
else if (this.filterIndeterminateNodes.indexOf(dataUid_1) !== -1) {
|
|
1072
|
+
this.changeState(checkBoxEle, 'indeterminate', null, true, true);
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
this.changeState(checkBoxEle, 'uncheck', null, true, true);
|
|
1076
|
+
}
|
|
1077
|
+
var parentUL_1 = closest(element, '.' + PARENTITEM);
|
|
1078
|
+
if (!isNOU(parentUL_1)) {
|
|
1079
|
+
var currentParent = closest(parentUL_1, '.' + LISTITEM);
|
|
1080
|
+
this.ensureParentCheckState(currentParent);
|
|
1081
|
+
}
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1097
1084
|
if (this.element.classList.contains('e-filtering')) {
|
|
1098
1085
|
var oldCheckedNodes = new DataManager(this.OldCheckedData).executeLocal(new Query().where('parentID', 'equal', dataUid_1, true));
|
|
1099
1086
|
checkedCount = oldCheckedNodes.length;
|
|
@@ -1201,7 +1188,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1201
1188
|
}
|
|
1202
1189
|
});
|
|
1203
1190
|
};
|
|
1204
|
-
TreeView.prototype.getAllChildNodes = function (data, parentId) {
|
|
1191
|
+
TreeView.prototype.getAllChildNodes = function (data, parentId, includeAllDescendants) {
|
|
1205
1192
|
if (isNOU(data) || isNOU(parentId)) {
|
|
1206
1193
|
return [];
|
|
1207
1194
|
}
|
|
@@ -1218,17 +1205,55 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1218
1205
|
}
|
|
1219
1206
|
if (this.dataType === 2) {
|
|
1220
1207
|
var parentFieldId_1 = this.fields.id;
|
|
1221
|
-
var
|
|
1222
|
-
var
|
|
1223
|
-
var
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1208
|
+
var childField_1 = this.fields.child.toString();
|
|
1209
|
+
var target = parentId.toString();
|
|
1210
|
+
var findNodeInTree_1 = function (nodes, id) {
|
|
1211
|
+
if (!Array.isArray(nodes)) {
|
|
1212
|
+
return undefined;
|
|
1213
|
+
}
|
|
1214
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
1215
|
+
var n = nodes[parseInt(i.toString(), 10)];
|
|
1216
|
+
var idVal = getValue(parentFieldId_1, n);
|
|
1217
|
+
if (!isNOU(idVal) && idVal.toString() === id) {
|
|
1218
|
+
return n;
|
|
1219
|
+
}
|
|
1220
|
+
var subs = getValue(childField_1, n);
|
|
1221
|
+
var found = findNodeInTree_1(subs, id);
|
|
1222
|
+
if (found) {
|
|
1223
|
+
return found;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
return undefined;
|
|
1227
|
+
};
|
|
1228
|
+
var node = findNodeInTree_1(data, target);
|
|
1227
1229
|
if (!node) {
|
|
1228
1230
|
return [];
|
|
1229
1231
|
}
|
|
1230
|
-
var children = getValue(
|
|
1231
|
-
|
|
1232
|
+
var children = getValue(childField_1, node);
|
|
1233
|
+
if (!Array.isArray(children)) {
|
|
1234
|
+
return [];
|
|
1235
|
+
}
|
|
1236
|
+
if (!includeAllDescendants) {
|
|
1237
|
+
return children;
|
|
1238
|
+
}
|
|
1239
|
+
var result_1 = children.slice();
|
|
1240
|
+
var collectNested_1 = function (nodes) {
|
|
1241
|
+
if (!Array.isArray(nodes)) {
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
1245
|
+
var n = nodes[parseInt(i.toString(), 10)];
|
|
1246
|
+
var grandChildren = getValue(childField_1, n);
|
|
1247
|
+
if (Array.isArray(grandChildren) && grandChildren.length) {
|
|
1248
|
+
for (var j = 0; j < grandChildren.length; j++) {
|
|
1249
|
+
result_1.push(grandChildren[parseInt(j.toString(), 10)]);
|
|
1250
|
+
}
|
|
1251
|
+
collectNested_1(grandChildren);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
collectNested_1(children);
|
|
1256
|
+
return result_1;
|
|
1232
1257
|
}
|
|
1233
1258
|
return [];
|
|
1234
1259
|
};
|
|
@@ -1674,7 +1699,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1674
1699
|
this.checkDisabledState(id);
|
|
1675
1700
|
}
|
|
1676
1701
|
if (this.enableVirtualization) {
|
|
1677
|
-
// Clear any indeterminate state — node is now fully checked
|
|
1678
1702
|
this.virtualIndeterminateNodesSet.delete(id);
|
|
1679
1703
|
var actualIndex = this.virtualNodeMap.get(id);
|
|
1680
1704
|
if (!isNOU(actualIndex) && actualIndex > -1) {
|
|
@@ -1695,20 +1719,15 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1695
1719
|
var actualIndex = this.virtualNodeMap.get(id);
|
|
1696
1720
|
if (!isNOU(actualIndex) && actualIndex > -1) {
|
|
1697
1721
|
if (isIndeterminate) {
|
|
1698
|
-
// Parent is transitioning to indeterminate — preserve children states
|
|
1699
|
-
// and mark this node as 'indeterminate' so scroll/collapse re-renders correctly.
|
|
1700
1722
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = 'indeterminate';
|
|
1701
|
-
// Also record it in the persistent indeterminate set so flattenVisibleNodes()
|
|
1702
|
-
// can restore this state after a full rebuild (e.g. on collapse).
|
|
1703
1723
|
this.virtualIndeterminateNodesSet.add(id);
|
|
1704
1724
|
}
|
|
1705
1725
|
else {
|
|
1706
|
-
// Node is being fully unchecked — clear indeterminate tracking too
|
|
1707
1726
|
this.virtualIndeterminateNodesSet.delete(id);
|
|
1708
1727
|
this.virtualCheckedNodesSet.delete(id);
|
|
1709
1728
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = false;
|
|
1710
1729
|
if (this.treeData && this.fields && this.fields.isChecked) {
|
|
1711
|
-
|
|
1730
|
+
this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
|
|
1712
1731
|
}
|
|
1713
1732
|
if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
|
|
1714
1733
|
this.setVirtualChildCheckState(id, false);
|
|
@@ -1754,8 +1773,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1754
1773
|
}
|
|
1755
1774
|
}
|
|
1756
1775
|
}
|
|
1757
|
-
// Collapsed children are not present in virtualFlatNodes, so walk the raw data
|
|
1758
|
-
// to keep this.treeData and checkedNodes in sync for hidden descendants too.
|
|
1759
1776
|
var collapsedChildren = this.getChildNodes(this.treeData, parentId);
|
|
1760
1777
|
if (collapsedChildren) {
|
|
1761
1778
|
for (var i = 0; i < collapsedChildren.length; i++) {
|
|
@@ -1901,7 +1918,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
1901
1918
|
}
|
|
1902
1919
|
this.isLoaded = true;
|
|
1903
1920
|
this.isNodeDropped = false;
|
|
1904
|
-
// Initialise (or re-initialise) virtual mode after data binding has settled
|
|
1905
1921
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
1906
1922
|
this.initVirtualization();
|
|
1907
1923
|
}
|
|
@@ -2041,9 +2057,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2041
2057
|
};
|
|
2042
2058
|
TreeView.prototype.expandNode = function (currLi, icon, loaded, isKeyboardNav) {
|
|
2043
2059
|
var _this = this;
|
|
2044
|
-
// Virtual mode fast-path: rebuild the flat list and re-render the window.
|
|
2045
|
-
// Only engage when height is also set; without a fixed height the tree falls
|
|
2046
|
-
// back to normal rendering and the standard expand path must be used.
|
|
2047
2060
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2048
2061
|
var id = currLi ? currLi.getAttribute('data-uid') : null;
|
|
2049
2062
|
if (id) {
|
|
@@ -2172,9 +2185,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2172
2185
|
};
|
|
2173
2186
|
TreeView.prototype.nodeCollapseAction = function (currLi, icon, colArgs, isKeyboardNav) {
|
|
2174
2187
|
var _this = this;
|
|
2175
|
-
// Virtual mode fast-path: rebuild the flat list and re-render the window.
|
|
2176
|
-
// Only engage when height is also set; without a fixed height the tree falls
|
|
2177
|
-
// back to normal rendering and the standard collapse path must be used.
|
|
2178
2188
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2179
2189
|
var id = currLi ? currLi.getAttribute('data-uid') : null;
|
|
2180
2190
|
if (id) {
|
|
@@ -2645,7 +2655,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2645
2655
|
li.setAttribute('tabindex', '0');
|
|
2646
2656
|
EventHandler.add(li, 'blur', this.focusOut, this);
|
|
2647
2657
|
this.updateIdAttr(focusedNode, li);
|
|
2648
|
-
// Track focused id for virtual mode re-render restoration
|
|
2649
2658
|
if (this.enableVirtualization) {
|
|
2650
2659
|
this.virtualFocusedId = li.getAttribute('data-uid');
|
|
2651
2660
|
}
|
|
@@ -2661,7 +2670,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2661
2670
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2662
2671
|
this.virtualSelectedNodesSet.add(id);
|
|
2663
2672
|
var actualIndex = this.virtualNodeMap.get(id);
|
|
2664
|
-
if (actualIndex && actualIndex > -1) {
|
|
2673
|
+
if (!isNOU(actualIndex) && actualIndex > -1) {
|
|
2665
2674
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
|
|
2666
2675
|
}
|
|
2667
2676
|
}
|
|
@@ -2682,7 +2691,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2682
2691
|
var id = liEle.getAttribute('data-uid');
|
|
2683
2692
|
this.virtualSelectedNodesSet.delete(id);
|
|
2684
2693
|
var actualIndex = this.virtualNodeMap.get(id);
|
|
2685
|
-
if (actualIndex && actualIndex > -1) {
|
|
2694
|
+
if (!isNOU(actualIndex) && actualIndex > -1) {
|
|
2686
2695
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
|
|
2687
2696
|
}
|
|
2688
2697
|
}
|
|
@@ -2777,12 +2786,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2777
2786
|
}
|
|
2778
2787
|
};
|
|
2779
2788
|
TreeView.prototype.nodeExpandAction = function (currLi, icon, expandChild, callback, isFromExpandAll, isKeyboardNav) {
|
|
2780
|
-
// In virtual mode (with a fixed height) the flat <li> never has a child <ul>
|
|
2781
|
-
// in the DOM, so the PARENTITEM check below would always fail and fall into
|
|
2782
|
-
// renderChildNodes(), which corrupts the virtual DOM.
|
|
2783
|
-
// Delegate directly to expandNode() which has the handleVirtualExpand fast-path.
|
|
2784
|
-
// When height is not provided, fall through to normal expand logic below since
|
|
2785
|
-
// the tree is rendered with a full nested DOM structure.
|
|
2786
2789
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2787
2790
|
this.expandNode(currLi, icon, undefined, isKeyboardNav);
|
|
2788
2791
|
return;
|
|
@@ -2900,9 +2903,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2900
2903
|
});
|
|
2901
2904
|
};
|
|
2902
2905
|
TreeView.prototype.navigateToFocus = function (isUp) {
|
|
2903
|
-
// In virtual mode (with a fixed height) the element itself is the scroll
|
|
2904
|
-
// container; use ensureVirtualNodeVisible. When height is not provided,
|
|
2905
|
-
// the tree uses normal rendering, so fall through to the standard scroll logic.
|
|
2906
2906
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
2907
2907
|
var focusedLi = this.getFocusedNode();
|
|
2908
2908
|
if (focusedLi) {
|
|
@@ -2998,6 +2998,83 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
2998
2998
|
}
|
|
2999
2999
|
}
|
|
3000
3000
|
}
|
|
3001
|
+
if (this.isRemoteDataLoadOnDemand() && this.element.classList.contains('e-filtering') && this.autoCheck) {
|
|
3002
|
+
var nodeUid = li.getAttribute('data-uid');
|
|
3003
|
+
if (isNOU(nodeUid)) {
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
var checkedIndex = this.filterCheckedNodes.indexOf(nodeUid);
|
|
3007
|
+
var indeterminateIndex = this.filterIndeterminateNodes.indexOf(nodeUid);
|
|
3008
|
+
var shouldCheck = !isCheck;
|
|
3009
|
+
if (shouldCheck) {
|
|
3010
|
+
if (checkedIndex === -1) {
|
|
3011
|
+
this.filterCheckedNodes.push(nodeUid);
|
|
3012
|
+
}
|
|
3013
|
+
if (indeterminateIndex !== -1) {
|
|
3014
|
+
this.filterIndeterminateNodes.splice(indeterminateIndex, 1);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
else {
|
|
3018
|
+
if (checkedIndex !== -1) {
|
|
3019
|
+
this.filterCheckedNodes.splice(checkedIndex, 1);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
var parentLi = closest(closest(li, '.' + PARENTITEM), '.' + LISTITEM);
|
|
3023
|
+
while (!isNOU(parentLi)) {
|
|
3024
|
+
var parentUid = parentLi.getAttribute('data-uid');
|
|
3025
|
+
if (!isNOU(parentUid)) {
|
|
3026
|
+
var parentChildren = this.getAllChildNodes(this.DDTTreeData, parentUid, true);
|
|
3027
|
+
if (parentChildren && parentChildren.length > 0) {
|
|
3028
|
+
var firstChildState = void 0;
|
|
3029
|
+
var mismatchFound = false;
|
|
3030
|
+
for (var i = 0; i < parentChildren.length; i++) {
|
|
3031
|
+
var childData = parentChildren[parseInt(i.toString(), 10)];
|
|
3032
|
+
var childId = childData && childData[this.fields.id]
|
|
3033
|
+
? childData[this.fields.id].toString()
|
|
3034
|
+
: null;
|
|
3035
|
+
if (isNOU(childId)) {
|
|
3036
|
+
continue;
|
|
3037
|
+
}
|
|
3038
|
+
var childCheckedState = this.filterIndeterminateNodes.indexOf(childId) !== -1
|
|
3039
|
+
? null
|
|
3040
|
+
: this.filterCheckedNodes.indexOf(childId) !== -1;
|
|
3041
|
+
if (firstChildState === void 0) {
|
|
3042
|
+
firstChildState = childCheckedState;
|
|
3043
|
+
}
|
|
3044
|
+
else if (childCheckedState !== firstChildState) {
|
|
3045
|
+
mismatchFound = true;
|
|
3046
|
+
break;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
if (mismatchFound || firstChildState === null) {
|
|
3050
|
+
if (this.filterIndeterminateNodes.indexOf(parentUid) === -1) {
|
|
3051
|
+
this.filterIndeterminateNodes.push(parentUid);
|
|
3052
|
+
}
|
|
3053
|
+
var parentCheckedIndex = this.filterCheckedNodes.indexOf(parentUid);
|
|
3054
|
+
if (parentCheckedIndex !== -1) {
|
|
3055
|
+
this.filterCheckedNodes.splice(parentCheckedIndex, 1);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
else {
|
|
3059
|
+
var parentIndeterminateIndex = this.filterIndeterminateNodes.indexOf(parentUid);
|
|
3060
|
+
if (parentIndeterminateIndex !== -1) {
|
|
3061
|
+
this.filterIndeterminateNodes.splice(parentIndeterminateIndex, 1);
|
|
3062
|
+
}
|
|
3063
|
+
var parentCheckedIndex = this.filterCheckedNodes.indexOf(parentUid);
|
|
3064
|
+
if (firstChildState === true) {
|
|
3065
|
+
if (parentCheckedIndex === -1) {
|
|
3066
|
+
this.filterCheckedNodes.push(parentUid);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
else if (parentCheckedIndex !== -1) {
|
|
3070
|
+
this.filterCheckedNodes.splice(parentCheckedIndex, 1);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
parentLi = closest(closest(parentLi, '.' + PARENTITEM), '.' + LISTITEM);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3001
3078
|
this.changeState(checkWrap, isCheck ? 'uncheck' : 'check', e, true);
|
|
3002
3079
|
this.updateActiveClass(li, isCheck);
|
|
3003
3080
|
if (this.autoCheck) {
|
|
@@ -3334,7 +3411,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3334
3411
|
};
|
|
3335
3412
|
TreeView.prototype.focusNextNode = function (li, isTowards) {
|
|
3336
3413
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
3337
|
-
// Determine the id of the node one step in the requested direction
|
|
3338
3414
|
var currentId = li ? li.getAttribute('data-uid') : null;
|
|
3339
3415
|
var currentIdx = -1;
|
|
3340
3416
|
if (!isNOU(currentId)) {
|
|
@@ -3346,26 +3422,14 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3346
3422
|
return;
|
|
3347
3423
|
}
|
|
3348
3424
|
var targetId = this.virtualFlatNodes[parseInt(targetIdx.toString(), 10)].id;
|
|
3349
|
-
// ── Fix 1: set virtualFocusedId BEFORE calling ensureVirtualNodeVisible ──
|
|
3350
|
-
// renderVirtualItems() is called synchronously inside ensureVirtualNodeVisible
|
|
3351
|
-
// when a re-render is needed. If virtualFocusedId is still the old value at
|
|
3352
|
-
// that point, focus restoration falls back to the first item. Setting it here
|
|
3353
|
-
// ensures renderVirtualItems always restores focus to the correct target node.
|
|
3354
3425
|
this.virtualFocusedId = targetId;
|
|
3355
|
-
// ── Fix 2: clear focus classes from ALL nested list items, not just top-level ones ──
|
|
3356
|
-
// With hierarchical virtual DOM, the currently focused <li> may be nested inside
|
|
3357
|
-
// a child <ul>. Using ':scope > li' only cleans up direct children and leaves
|
|
3358
|
-
// stale e-focused / e-node-focus classes on nested nodes across re-renders.
|
|
3359
3426
|
var allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
|
|
3360
3427
|
for (var k = 0; k < allLi.length; k++) {
|
|
3361
3428
|
allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
|
|
3362
3429
|
allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED);
|
|
3363
3430
|
allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
|
|
3364
3431
|
}
|
|
3365
|
-
// Ensure the target <li> is in the DOM window (may trigger a re-render).
|
|
3366
|
-
// virtualFocusedId is already set above so renderVirtualItems restores focus correctly.
|
|
3367
3432
|
this.ensureVirtualNodeVisible(targetId);
|
|
3368
|
-
// Re-query both nodes after the potential re-render so setFocus gets live DOM elements
|
|
3369
3433
|
var prevNode = currentId ? this.getElement(currentId) : li;
|
|
3370
3434
|
var nextNode_1 = this.getElement(targetId);
|
|
3371
3435
|
if (!isNOU(nextNode_1)) {
|
|
@@ -3466,18 +3530,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3466
3530
|
if (!focusedElement) {
|
|
3467
3531
|
return;
|
|
3468
3532
|
}
|
|
3469
|
-
// Virtual-mode guard: when the browser moves focus into the TreeView (e.g. via
|
|
3470
|
-
// the Tab key) it always lands on the single `<li tabindex="0">`. In that
|
|
3471
|
-
// case the element is already the focusable one, FOCUS is already applied
|
|
3472
|
-
// and the only thing left to do is the keyboard focus styling. Skip the
|
|
3473
|
-
// redundant setAttribute/EventHandler.add work so we never re-render the
|
|
3474
|
-
// virtual window as a side effect of focusing the existing `tabindex="0"`
|
|
3475
|
-
// item.
|
|
3476
|
-
// ── Fix: Use document.activeElement to detect browser-focused node ──
|
|
3477
|
-
// After renderVirtualItems() restores focus, the browser-focused element is
|
|
3478
|
-
// the authoritative source, not getFocusedNode(). If they don't match, it means
|
|
3479
|
-
// renderVirtualItems already correctly set up the element, so we must not
|
|
3480
|
-
// override its setup by calling setFocus again.
|
|
3481
3533
|
var actuallyFocusedElement = document.activeElement;
|
|
3482
3534
|
var isAlreadyFocusedInVirtualMode = this.enableVirtualization
|
|
3483
3535
|
&& actuallyFocusedElement === focusedElement
|
|
@@ -3732,12 +3784,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
3732
3784
|
else {
|
|
3733
3785
|
txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
|
|
3734
3786
|
}
|
|
3735
|
-
// ── Virtual mode: sync the flat-node snapshot so re-renders show updated text ──
|
|
3736
|
-
// virtualFlatNodes is rebuilt from this.treeData (via flattenVisibleNodes) only on
|
|
3737
|
-
// expand/collapse/scroll. Between those events the snapshot is the single source of
|
|
3738
|
-
// truth used by createVirtualListItem. setValue() above updates this.editData (which
|
|
3739
|
-
// IS the treeData object reference), but virtualFlatNodes[idx].text still holds the
|
|
3740
|
-
// old value. Update it here so the next renderVirtualItems() call shows the new text.
|
|
3741
3787
|
if (this.enableVirtualization) {
|
|
3742
3788
|
var id = liEle.getAttribute('data-uid');
|
|
3743
3789
|
var idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
|
|
@@ -4185,11 +4231,49 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4185
4231
|
this.fields.dataSource = this.treeData;
|
|
4186
4232
|
}
|
|
4187
4233
|
this.triggerEvent('nodeDropped', nodeData);
|
|
4234
|
+
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
4235
|
+
this.setRootData();
|
|
4236
|
+
this.rebuildVirtualCheckCache();
|
|
4237
|
+
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
4238
|
+
this.rebuildVirtualNodeMap();
|
|
4239
|
+
var scrollTopSnapshot = this.element.scrollTop;
|
|
4240
|
+
var win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
4241
|
+
this.virtualStartIndex = win.startIndex;
|
|
4242
|
+
this.virtualEndIndex = win.endIndex;
|
|
4243
|
+
this.virtualSkipNextScrollRender = true;
|
|
4244
|
+
this.renderVirtualItems();
|
|
4245
|
+
}
|
|
4188
4246
|
};
|
|
4189
4247
|
TreeView.prototype.appendNode = function (dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
|
|
4190
4248
|
var checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
|
|
4191
4249
|
var collapse = closest(e.target, '.' + COLLAPSIBLE);
|
|
4192
4250
|
var expand = closest(e.target, '.' + EXPANDABLE);
|
|
4251
|
+
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
4252
|
+
var routesToSiblingBranch = !dragLi.classList.contains('e-disable') && !checkWrapper &&
|
|
4253
|
+
((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) ||
|
|
4254
|
+
(expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) ||
|
|
4255
|
+
(!expand && !collapse));
|
|
4256
|
+
if (routesToSiblingBranch) {
|
|
4257
|
+
if (dropTarget.nodeName === 'LI') {
|
|
4258
|
+
this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
|
|
4259
|
+
}
|
|
4260
|
+
else if (dropTarget.firstElementChild && dropTarget.classList.contains(ROOT)) {
|
|
4261
|
+
if (dropTarget.firstElementChild.nodeName === 'UL') {
|
|
4262
|
+
this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
else if ((dropTarget.classList.contains('e-icon-collapsible')) || (dropTarget.classList.contains('e-icon-expandable'))) {
|
|
4266
|
+
this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
|
|
4267
|
+
}
|
|
4268
|
+
else {
|
|
4269
|
+
this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
else {
|
|
4273
|
+
this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
|
|
4274
|
+
}
|
|
4275
|
+
return;
|
|
4276
|
+
}
|
|
4193
4277
|
if (!dragLi.classList.contains('e-disable') && !checkWrapper && ((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) || (expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) || (!expand && !collapse))) {
|
|
4194
4278
|
if (dropTarget.nodeName === 'LI') {
|
|
4195
4279
|
this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
|
|
@@ -4213,6 +4297,62 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4213
4297
|
this.ensureIndeterminate();
|
|
4214
4298
|
}
|
|
4215
4299
|
};
|
|
4300
|
+
TreeView.prototype.virtualMoveAsSibling = function (dragLi, dropLi, e, dragObj) {
|
|
4301
|
+
var dropUl = closest(dropLi, '.' + PARENTITEM);
|
|
4302
|
+
var pre;
|
|
4303
|
+
if (e.target.offsetHeight > 0 && e.event.offsetY > e.target.offsetHeight - 2) {
|
|
4304
|
+
pre = false;
|
|
4305
|
+
}
|
|
4306
|
+
else if (e.event.offsetY < 2) {
|
|
4307
|
+
pre = true;
|
|
4308
|
+
}
|
|
4309
|
+
else if (e.target.classList.contains('e-icon-expandable') || (e.target.classList.contains('e-icon-collapsible'))) {
|
|
4310
|
+
if ((e.event.offsetY < 5) || (e.event.offsetX < 3)) {
|
|
4311
|
+
pre = true;
|
|
4312
|
+
}
|
|
4313
|
+
else if ((e.event.offsetY > 15) || (e.event.offsetX > 17)) {
|
|
4314
|
+
pre = false;
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
this.moveData(dragLi, dropLi, dropUl, pre, dragObj);
|
|
4318
|
+
};
|
|
4319
|
+
TreeView.prototype.virtualMoveAsChild = function (dragLi, dropLi, dragObj, offsetY, dropTarget) {
|
|
4320
|
+
var pos = offsetY;
|
|
4321
|
+
var isSiblingMarker = dropTarget.classList.contains('e-sibling');
|
|
4322
|
+
var refLi = (pos < 7 && !isSiblingMarker) || (isSiblingMarker &&
|
|
4323
|
+
dropLi.lastChild && !dropLi.lastChild.classList.contains('e-sibling'))
|
|
4324
|
+
? dropLi : null;
|
|
4325
|
+
var dropTargetHt = dropTarget;
|
|
4326
|
+
var isAfter = (dropTargetHt.offsetHeight > 0 && pos > dropTargetHt.offsetHeight - 10);
|
|
4327
|
+
if (refLi) {
|
|
4328
|
+
var dropParentUl = closest(dropLi, '.' + PARENTITEM);
|
|
4329
|
+
this.moveData(dragLi, dropLi, dropParentUl, true, dragObj);
|
|
4330
|
+
}
|
|
4331
|
+
else if (isAfter) {
|
|
4332
|
+
var dropParentUl = closest(dropLi, '.' + PARENTITEM);
|
|
4333
|
+
this.moveData(dragLi, dropLi, dropParentUl, false, dragObj);
|
|
4334
|
+
}
|
|
4335
|
+
else {
|
|
4336
|
+
var dropIcon = select('div.' + EXPANDABLE + ', div.' + COLLAPSIBLE, dropLi);
|
|
4337
|
+
var dropIsCollapsedParent = dropIcon && dropIcon.classList.contains(EXPANDABLE);
|
|
4338
|
+
var dropIsLeaf = isNOU(dropIcon);
|
|
4339
|
+
if (this.preventExpand !== true && (dropIsCollapsedParent || dropIsLeaf)) {
|
|
4340
|
+
var dropLiId = dropLi.getAttribute('data-uid');
|
|
4341
|
+
if (dropLiId && this.expandedNodes.indexOf(dropLiId) === -1) {
|
|
4342
|
+
this.expandedNodes.push(dropLiId);
|
|
4343
|
+
}
|
|
4344
|
+
if (this.virtualExpandedNodesSet && dropLiId) {
|
|
4345
|
+
this.virtualExpandedNodesSet.add(dropLiId);
|
|
4346
|
+
}
|
|
4347
|
+
var dropRecord = this.findNodeInTreeData(dropLiId);
|
|
4348
|
+
if (dropRecord && this.fields && this.fields.expanded) {
|
|
4349
|
+
setValue(this.fields.expanded, true, dropRecord);
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
var dropUl = select('.' + PARENTITEM, dropLi) || dropLi;
|
|
4353
|
+
this.moveData(dragLi, null, dropUl, true, dragObj);
|
|
4354
|
+
}
|
|
4355
|
+
};
|
|
4216
4356
|
TreeView.prototype.dropAsSiblingNode = function (dragLi, dropLi, e, dragObj) {
|
|
4217
4357
|
var dropUl = closest(dropLi, '.' + PARENTITEM);
|
|
4218
4358
|
var dragParentUl = closest(dragLi, '.' + PARENTITEM);
|
|
@@ -4300,7 +4440,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4300
4440
|
delete removedData[0][this.fields.parentID];
|
|
4301
4441
|
}
|
|
4302
4442
|
else {
|
|
4303
|
-
var currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
|
|
4443
|
+
var currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
|
|
4304
4444
|
setValue(this.fields.parentID, currPid, removedData[0]);
|
|
4305
4445
|
}
|
|
4306
4446
|
this.treeData.splice(pos, 0, removedData[0]);
|
|
@@ -4442,7 +4582,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4442
4582
|
var draggedNodeData = obj ? obj.dragData : null;
|
|
4443
4583
|
var newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
|
|
4444
4584
|
var dragLiParent = obj.dragLi.parentElement;
|
|
4445
|
-
var dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
|
|
4585
|
+
var dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
|
|
4446
4586
|
var targetParent = null;
|
|
4447
4587
|
var indexValue = null;
|
|
4448
4588
|
var iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
|
|
@@ -4451,7 +4591,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
4451
4591
|
var index = node ? closest(node, '.e-list-parent') : null;
|
|
4452
4592
|
var i = 0;
|
|
4453
4593
|
var position = null;
|
|
4454
|
-
dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
|
|
4594
|
+
dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
|
|
4455
4595
|
dragParent = (drop === true) ? this.dragParent : dragParent;
|
|
4456
4596
|
if (cloneEle) {
|
|
4457
4597
|
while (i < 4) {
|
|
@@ -5125,26 +5265,10 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5125
5265
|
TreeView.prototype.keyDownHandler = function (e) {
|
|
5126
5266
|
if (e.key === 'Tab' || e.keyCode === 9) {
|
|
5127
5267
|
this.isKeyboardFocus = true;
|
|
5128
|
-
// Mark the upcoming scroll event (if any) as Tab-induced so the
|
|
5129
|
-
// virtual scroll handler skips its re-render path. The browser
|
|
5130
|
-
// calls `Element.scrollIntoView` semantics on focus, which scrolls
|
|
5131
|
-
// a `<li tabindex="0">` in the middle of the tree into view and
|
|
5132
|
-
// therefore fires `scroll` on the container. Without this guard
|
|
5133
|
-
// the scroll handler would tear down the focused `<li>` and move
|
|
5134
|
-
// focus elsewhere.
|
|
5135
5268
|
if (this.enableVirtualization) {
|
|
5136
5269
|
this.virtualSkipNextScrollRender = true;
|
|
5137
5270
|
}
|
|
5138
5271
|
}
|
|
5139
|
-
// ── Fix: Also skip scroll render for arrow key navigation ──────────────────
|
|
5140
|
-
// Arrow Down/Up calls focusNextNode() which calls ensureVirtualNodeVisible()
|
|
5141
|
-
// to scroll the target node into view. Without this guard, the scroll event
|
|
5142
|
-
// would trigger onVirtualScroll() which re-renders the window and resets the
|
|
5143
|
-
// focus that focusNextNode() just set. This manifests as tabindex resetting
|
|
5144
|
-
// to the first item instead of staying on the arrow-navigated target.
|
|
5145
|
-
// Arrow Right/Left (expand/collapse) also calls renderVirtualItems() which
|
|
5146
|
-
// calls li.focus() in branch 2; that can trigger a scroll on an edge node,
|
|
5147
|
-
// so we consume it here to prevent a stale RAF re-render.
|
|
5148
5272
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
|
|
5149
5273
|
|| e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
|
|
5150
5274
|
if (this.enableVirtualization) {
|
|
@@ -5506,10 +5630,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5506
5630
|
if (this.dataType === 1) {
|
|
5507
5631
|
this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
|
|
5508
5632
|
}
|
|
5509
|
-
// In virtual mode (with a fixed height), rebuild the flat node list and re-render
|
|
5510
|
-
// after deletion so the removed nodes are correctly reflected in the hierarchical
|
|
5511
|
-
// virtual DOM. When height is not provided, the tree uses normal rendering and
|
|
5512
|
-
// the DOM is already updated by removeNode() above.
|
|
5513
5633
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
5514
5634
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
5515
5635
|
this.rebuildVirtualNodeMap();
|
|
@@ -5578,16 +5698,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5578
5698
|
}
|
|
5579
5699
|
return parentsID;
|
|
5580
5700
|
};
|
|
5581
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5582
|
-
// VIRTUALIZATION — private helpers
|
|
5583
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5584
|
-
/**
|
|
5585
|
-
* Parses the `height` property (string like '400px' or number) to a pixel value.
|
|
5586
|
-
* Returns 0 if the value cannot be determined.
|
|
5587
|
-
*
|
|
5588
|
-
* @returns {number} Pixel height.
|
|
5589
|
-
* @private
|
|
5590
|
-
*/
|
|
5591
5701
|
TreeView.prototype.parseHeightToPixels = function () {
|
|
5592
5702
|
var h = this.height;
|
|
5593
5703
|
if (isNOU(h)) {
|
|
@@ -5599,13 +5709,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5599
5709
|
var parsed = parseFloat(h);
|
|
5600
5710
|
return isNaN(parsed) ? 0 : parsed;
|
|
5601
5711
|
};
|
|
5602
|
-
/**
|
|
5603
|
-
* Measures the height of the first rendered list item so subsequent calculations
|
|
5604
|
-
* use real layout values. Falls back to the stored `virtualItemHeight` default (40).
|
|
5605
|
-
*
|
|
5606
|
-
* @returns {number} Row height in pixels.
|
|
5607
|
-
* @private
|
|
5608
|
-
*/
|
|
5609
5712
|
TreeView.prototype.measureVirtualItemHeight = function () {
|
|
5610
5713
|
var firstLi = select('.' + LISTITEM, this.element);
|
|
5611
5714
|
if (firstLi && firstLi.offsetHeight > 0) {
|
|
@@ -5613,13 +5716,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5613
5716
|
}
|
|
5614
5717
|
return this.virtualItemHeight;
|
|
5615
5718
|
};
|
|
5616
|
-
/**
|
|
5617
|
-
* Rebuilds the cached Sets for checked/selected nodes to enable O(1) lookup
|
|
5618
|
-
* during the flatten operation instead of O(n) array indexOf calls.
|
|
5619
|
-
*
|
|
5620
|
-
* @returns {void}
|
|
5621
|
-
* @private
|
|
5622
|
-
*/
|
|
5623
5719
|
TreeView.prototype.rebuildVirtualCheckCache = function () {
|
|
5624
5720
|
this.virtualExpandedNodesSet.clear();
|
|
5625
5721
|
this.virtualCheckedNodesSet.clear();
|
|
@@ -5640,20 +5736,10 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5640
5736
|
}
|
|
5641
5737
|
}
|
|
5642
5738
|
};
|
|
5643
|
-
/**
|
|
5644
|
-
* Recursively walks the treeData (or groupedData for flat lists) and builds
|
|
5645
|
-
* `virtualFlatNodes` — an ordered list of every node that is currently visible
|
|
5646
|
-
* (i.e. its ancestors are all expanded). Uses cached Sets for O(1) state lookups.
|
|
5647
|
-
*
|
|
5648
|
-
* @returns {VirtualNodeData[]} Ordered flat list of visible nodes.
|
|
5649
|
-
* @private
|
|
5650
|
-
*/
|
|
5651
5739
|
TreeView.prototype.flattenVisibleNodes = function () {
|
|
5652
|
-
// Build cached Sets for fast lookups
|
|
5653
5740
|
this.rebuildVirtualCheckCache();
|
|
5654
5741
|
var result = [];
|
|
5655
5742
|
if (this.dataType === 1) {
|
|
5656
|
-
// Flat data with parentID field — use getChildGroup to find root nodes
|
|
5657
5743
|
var rootNodes = [];
|
|
5658
5744
|
if (this.groupedData) {
|
|
5659
5745
|
rootNodes = this.getChildGroup(this.groupedData, null, true);
|
|
@@ -5661,40 +5747,29 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5661
5747
|
if (!rootNodes || rootNodes.length === 0) {
|
|
5662
5748
|
rootNodes = this.rootData;
|
|
5663
5749
|
}
|
|
5664
|
-
// Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
|
|
5665
5750
|
rootNodes = this.getSortedData(rootNodes);
|
|
5666
5751
|
this.flattenNodeRecursive(rootNodes, 1, null, result);
|
|
5667
5752
|
}
|
|
5668
5753
|
else {
|
|
5669
|
-
// Hierarchical data
|
|
5670
5754
|
var sorted = this.getSortedData(this.rootData);
|
|
5671
5755
|
this.flattenNodeRecursive(sorted, 1, null, result);
|
|
5672
5756
|
}
|
|
5673
5757
|
return result;
|
|
5674
5758
|
};
|
|
5675
|
-
/**
|
|
5676
|
-
* Recursive helper for `flattenVisibleNodes`.
|
|
5677
|
-
*
|
|
5678
|
-
* @param {Object[]} nodes - Array of raw data objects at this level.
|
|
5679
|
-
* @param {number} level - 1-based depth level.
|
|
5680
|
-
* @param {string | null} parentId - The parent node id, or null for root.
|
|
5681
|
-
* @param {VirtualNodeData[]} result - Accumulator array.
|
|
5682
|
-
* @returns {void}
|
|
5683
|
-
* @private
|
|
5684
|
-
*/
|
|
5685
5759
|
TreeView.prototype.flattenNodeRecursive = function (nodes, level, parentId, result) {
|
|
5686
5760
|
if (!nodes || nodes.length === 0) {
|
|
5687
5761
|
return;
|
|
5688
5762
|
}
|
|
5689
|
-
|
|
5690
|
-
var childFieldName =
|
|
5691
|
-
var expandedFieldName =
|
|
5692
|
-
var isCheckedFieldName =
|
|
5693
|
-
var iconCssFieldName =
|
|
5694
|
-
var imageUrlFieldName =
|
|
5695
|
-
var idFieldName =
|
|
5696
|
-
var textFieldName =
|
|
5697
|
-
var selectedFieldName =
|
|
5763
|
+
var levelMapper = level === 1 ? this.fields : this.getChildFields(this.fields, level, 1);
|
|
5764
|
+
var childFieldName = levelMapper.child && levelMapper.child.toString();
|
|
5765
|
+
var expandedFieldName = levelMapper.expanded;
|
|
5766
|
+
var isCheckedFieldName = levelMapper.isChecked;
|
|
5767
|
+
var iconCssFieldName = levelMapper.iconCss;
|
|
5768
|
+
var imageUrlFieldName = levelMapper.imageUrl;
|
|
5769
|
+
var idFieldName = levelMapper.id;
|
|
5770
|
+
var textFieldName = levelMapper.text;
|
|
5771
|
+
var selectedFieldName = levelMapper.selected;
|
|
5772
|
+
var hasChildrenFieldName = levelMapper.hasChildren;
|
|
5698
5773
|
var needsSorting = this.sortOrder !== 'None';
|
|
5699
5774
|
var isDataTypeFlat = this.dataType === 1;
|
|
5700
5775
|
var enableVirtualization = this.enableVirtualization;
|
|
@@ -5702,36 +5777,57 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5702
5777
|
var raw = nodes[i];
|
|
5703
5778
|
var id = isNOU(raw[idFieldName]) ? '' : raw[idFieldName].toString();
|
|
5704
5779
|
var text = isNOU(raw[textFieldName]) ? '' : raw[textFieldName].toString();
|
|
5705
|
-
// Use expandedNodes as the single source of truth.
|
|
5706
5780
|
var inExpandedNodes = this.virtualExpandedNodesSet.has(id);
|
|
5707
|
-
var rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
|
|
5781
|
+
var rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
|
|
5708
5782
|
var expanded = inExpandedNodes || rawExpanded;
|
|
5709
5783
|
if (rawExpanded && id) {
|
|
5710
5784
|
this.expandedNodes.push(id);
|
|
5711
5785
|
this.virtualExpandedNodesSet.add(id);
|
|
5712
5786
|
}
|
|
5713
|
-
// Inline hasChildRecords check to avoid function call (optimization)
|
|
5714
5787
|
var hasChildren = false;
|
|
5715
|
-
|
|
5788
|
+
var explicitHasChildren = hasChildrenFieldName ? getValue(hasChildrenFieldName, raw) : null;
|
|
5789
|
+
if (levelMapper.dataSource instanceof DataManager && !isNOU(explicitHasChildren) && explicitHasChildren) {
|
|
5790
|
+
hasChildren = true;
|
|
5791
|
+
}
|
|
5792
|
+
else if (isDataTypeFlat) {
|
|
5716
5793
|
if (this.groupedData) {
|
|
5717
5794
|
var groupedChild = this.getChildGroup(this.groupedData, id, false);
|
|
5718
5795
|
hasChildren = !isNOU(groupedChild) && groupedChild.length > 0;
|
|
5719
5796
|
}
|
|
5720
5797
|
}
|
|
5721
5798
|
else {
|
|
5722
|
-
var childrenObj = getValue(childFieldName, raw);
|
|
5799
|
+
var childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
|
|
5723
5800
|
hasChildren = !isNOU(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
|
|
5724
5801
|
}
|
|
5725
|
-
|
|
5726
|
-
if (
|
|
5727
|
-
|
|
5728
|
-
|
|
5802
|
+
var parentChecked = null;
|
|
5803
|
+
if (levelMapper.dataSource instanceof DataManager && level > 1 && this.autoCheck && parentId) {
|
|
5804
|
+
if (this.virtualCheckedNodesSet.has(parentId) ||
|
|
5805
|
+
(this.checkedNodes && this.checkedNodes.indexOf(parentId) !== -1)) {
|
|
5806
|
+
parentChecked = true;
|
|
5807
|
+
}
|
|
5808
|
+
else {
|
|
5809
|
+
parentChecked = false;
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
var rawChecked = isCheckedFieldName && getValue(isCheckedFieldName, raw);
|
|
5813
|
+
if (enableVirtualization && id && !this.virtualCheckedNodesSet.has(id)) {
|
|
5814
|
+
if ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'true') ||
|
|
5815
|
+
(levelMapper.dataSource instanceof DataManager && parentChecked === true)) {
|
|
5729
5816
|
this.virtualCheckedNodesSet.add(id);
|
|
5730
5817
|
if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
|
|
5731
5818
|
this.checkedNodes.push(id);
|
|
5732
5819
|
}
|
|
5733
5820
|
}
|
|
5734
5821
|
}
|
|
5822
|
+
else if (enableVirtualization && id && this.virtualCheckedNodesSet.has(id) && levelMapper.dataSource instanceof DataManager) {
|
|
5823
|
+
if (level > 1 && !this.virtualIndeterminateNodesSet.has(parentId) && this.autoCheck &&
|
|
5824
|
+
((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'false') || parentChecked === false)) {
|
|
5825
|
+
this.virtualCheckedNodesSet.delete(id);
|
|
5826
|
+
if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
|
|
5827
|
+
this.checkedNodes.splice(this.checkedNodes.indexOf(id), 1);
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5735
5831
|
if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
|
|
5736
5832
|
var rawSelected = getValue(selectedFieldName, raw);
|
|
5737
5833
|
if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
|
|
@@ -5741,10 +5837,7 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5741
5837
|
}
|
|
5742
5838
|
}
|
|
5743
5839
|
}
|
|
5744
|
-
// Use cached Sets for O(1) lookup
|
|
5745
5840
|
var selected = this.virtualSelectedNodesSet.has(id);
|
|
5746
|
-
// Resolve checkbox state: indeterminate takes precedence over the checked set,
|
|
5747
|
-
// because a node can be in the indeterminate state without being in checkedNodes.
|
|
5748
5841
|
var isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
|
|
5749
5842
|
? 'indeterminate'
|
|
5750
5843
|
: this.virtualCheckedNodesSet.has(id);
|
|
@@ -5758,22 +5851,19 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5758
5851
|
};
|
|
5759
5852
|
result.push(node);
|
|
5760
5853
|
if (expanded && hasChildren) {
|
|
5761
|
-
// Determine children list
|
|
5762
5854
|
var children = [];
|
|
5763
5855
|
if (isDataTypeFlat) {
|
|
5764
5856
|
if (this.groupedData) {
|
|
5765
5857
|
children = this.getChildGroup(this.groupedData, id, false);
|
|
5766
|
-
// Apply sortOrder to flat-data children (same as hierarchical path)
|
|
5767
5858
|
if (needsSorting) {
|
|
5768
5859
|
children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
|
|
5769
5860
|
}
|
|
5770
5861
|
}
|
|
5771
5862
|
}
|
|
5772
5863
|
else {
|
|
5773
|
-
var rawChildren = getValue(childFieldName, raw);
|
|
5864
|
+
var rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
|
|
5774
5865
|
if (!isNOU(rawChildren) && Array.isArray(rawChildren)) {
|
|
5775
5866
|
children = rawChildren;
|
|
5776
|
-
// Only sort if needed
|
|
5777
5867
|
if (needsSorting) {
|
|
5778
5868
|
children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
|
|
5779
5869
|
}
|
|
@@ -5783,35 +5873,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5783
5873
|
}
|
|
5784
5874
|
}
|
|
5785
5875
|
};
|
|
5786
|
-
/**
|
|
5787
|
-
* Checks whether a raw datasource node has child records.
|
|
5788
|
-
*
|
|
5789
|
-
* @param {Object} raw - Raw datasource object.
|
|
5790
|
-
* @returns {boolean} True if children exist.
|
|
5791
|
-
* @private
|
|
5792
|
-
*/
|
|
5793
|
-
TreeView.prototype.hasChildRecords = function (raw) {
|
|
5794
|
-
if (this.dataType === 1) {
|
|
5795
|
-
if (!this.groupedData) {
|
|
5796
|
-
return false;
|
|
5797
|
-
}
|
|
5798
|
-
var rawId = raw[this.fields.id];
|
|
5799
|
-
if (isNOU(rawId)) {
|
|
5800
|
-
return false;
|
|
5801
|
-
}
|
|
5802
|
-
var groupedChild = this.getChildGroup(this.groupedData, rawId.toString(), false);
|
|
5803
|
-
return !isNOU(groupedChild) && groupedChild.length > 0;
|
|
5804
|
-
}
|
|
5805
|
-
var childField = this.fields.child.toString();
|
|
5806
|
-
var children = getValue(childField, raw);
|
|
5807
|
-
return !isNOU(children) && Array.isArray(children) && children.length > 0;
|
|
5808
|
-
};
|
|
5809
|
-
/**
|
|
5810
|
-
* Rebuilds `virtualNodeMap` (id → index) from the current `virtualFlatNodes`.
|
|
5811
|
-
*
|
|
5812
|
-
* @returns {void}
|
|
5813
|
-
* @private
|
|
5814
|
-
*/
|
|
5815
5876
|
TreeView.prototype.rebuildVirtualNodeMap = function () {
|
|
5816
5877
|
this.virtualNodeMap.clear();
|
|
5817
5878
|
for (var i = 0; i < this.virtualFlatNodes.length; i++) {
|
|
@@ -5844,49 +5905,12 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5844
5905
|
var paddingBottom = Math.max(0, (total - end) * itemH);
|
|
5845
5906
|
return { paddingTop: paddingTop, paddingBottom: paddingBottom };
|
|
5846
5907
|
};
|
|
5847
|
-
/**
|
|
5848
|
-
* Renders the virtual window into a **nested** `<ul>/<li>` hierarchy identical to
|
|
5849
|
-
* the non-virtualized DOM structure.
|
|
5850
|
-
*
|
|
5851
|
-
* ## Scroll math
|
|
5852
|
-
* The flat-index ↔ pixel mapping stays unchanged: every entry in
|
|
5853
|
-
* `virtualFlatNodes` represents exactly one `itemHeight` row.
|
|
5854
|
-
* `paddingTop = startIndex * itemHeight` and
|
|
5855
|
-
* `paddingBottom = (total - endIndex) * itemHeight` keep the scrollbar
|
|
5856
|
-
* thumb in the correct position.
|
|
5857
|
-
*
|
|
5858
|
-
* ## Ancestor injection
|
|
5859
|
-
* When `startIndex` points at a child node whose ancestor(s) have already
|
|
5860
|
-
* scrolled above the viewport (their flat indices are < startIndex), the
|
|
5861
|
-
* ancestors would normally be missing from the DOM, forcing the children to
|
|
5862
|
-
* fall back to the root `<ul>` as flat siblings — producing wrong HTML.
|
|
5863
|
-
*
|
|
5864
|
-
* To fix this, before rendering the window we walk the ancestor chain of the
|
|
5865
|
-
* first visible node and collect every ancestor that lies **outside** the
|
|
5866
|
-
* window. Those ancestors are injected as real `<li>` containers at the
|
|
5867
|
-
* very top of the root `<ul>`. To compensate for their presence, `paddingTop`
|
|
5868
|
-
* is reduced by `injectedCount * itemHeight` so the total scroll height
|
|
5869
|
-
* remains the same.
|
|
5870
|
-
*
|
|
5871
|
-
* Once the ancestors are in the DOM they are registered in `renderedParentMap`
|
|
5872
|
-
* so the normal window-rendering loop can nest children inside them exactly
|
|
5873
|
-
* as it does when both parent and child are in the same window.
|
|
5874
|
-
*
|
|
5875
|
-
* @returns {void}
|
|
5876
|
-
* @private
|
|
5877
|
-
*/
|
|
5878
5908
|
TreeView.prototype.renderVirtualItems = function () {
|
|
5879
5909
|
if (!this.ulElement) {
|
|
5880
5910
|
return;
|
|
5881
5911
|
}
|
|
5882
5912
|
var ul = this.ulElement;
|
|
5883
|
-
// ── Snapshot focused ID BEFORE clearing the DOM ───────────────────────────
|
|
5884
|
-
// Removing a focused <li> from the DOM triggers a synchronous `blur` event,
|
|
5885
|
-
// which calls focusOut() and sets virtualFocusedId = null. Capturing the id
|
|
5886
|
-
// here (before any removeChild) ensures focus is correctly restored to the
|
|
5887
|
-
// same node after re-render even when keyboard navigation was in progress.
|
|
5888
5913
|
var focusedUid = this.virtualFocusedId || null;
|
|
5889
|
-
// ── Clear previous render (direct children only) ─────────────────────────
|
|
5890
5914
|
var existingItems = ul.querySelectorAll(':scope > li');
|
|
5891
5915
|
for (var i = existingItems.length - 1; i >= 0; i--) {
|
|
5892
5916
|
ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
|
|
@@ -5897,26 +5921,22 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5897
5921
|
if (this.virtualStartIndex < this.virtualFlatNodes.length) {
|
|
5898
5922
|
var firstNode = this.virtualFlatNodes[this.virtualStartIndex];
|
|
5899
5923
|
if (firstNode.parentId) {
|
|
5900
|
-
// Build the ancestor chain from root down to the immediate parent
|
|
5901
5924
|
var ancestors = [];
|
|
5902
5925
|
var searchId = firstNode.parentId;
|
|
5903
5926
|
while (searchId) {
|
|
5904
5927
|
var ancestorIdx = this.virtualNodeMap.get(searchId);
|
|
5905
5928
|
if (!isNOU(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
|
|
5906
5929
|
var ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
|
|
5907
|
-
ancestors.unshift(ancestor);
|
|
5930
|
+
ancestors.unshift(ancestor);
|
|
5908
5931
|
searchId = ancestor.parentId;
|
|
5909
5932
|
}
|
|
5910
5933
|
else {
|
|
5911
5934
|
break;
|
|
5912
5935
|
}
|
|
5913
5936
|
}
|
|
5914
|
-
// Inject each ancestor as a real <li> into the root <ul>
|
|
5915
|
-
// and register it in renderedParentMap for child-nesting below.
|
|
5916
5937
|
for (var a = 0; a < ancestors.length; a++) {
|
|
5917
5938
|
var ancestor = ancestors[parseInt(a.toString(), 10)];
|
|
5918
5939
|
var ancestorLi = this.createVirtualListItem(ancestor, false);
|
|
5919
|
-
// Find the correct container: root or a previously injected ancestor's <ul>
|
|
5920
5940
|
var targetUl = ul;
|
|
5921
5941
|
if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
|
|
5922
5942
|
var pe = renderedParentMap.get(ancestor.parentId);
|
|
@@ -5935,26 +5955,20 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5935
5955
|
}
|
|
5936
5956
|
}
|
|
5937
5957
|
}
|
|
5938
|
-
// ── Scroll-space padding (adjusted for injected ancestors) ────────────────
|
|
5939
5958
|
var total = this.virtualFlatNodes.length;
|
|
5940
5959
|
var start = this.virtualStartIndex;
|
|
5941
5960
|
var end = this.virtualEndIndex;
|
|
5942
5961
|
var rawPaddingTop = start * itemH;
|
|
5943
|
-
// Subtract the height of injected ancestors so the scroll position is
|
|
5944
|
-
// exact: those ancestor rows are now real DOM elements, not hidden space.
|
|
5945
5962
|
var adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
|
|
5946
5963
|
ul.style.paddingTop = adjustedPaddingTop + 'px';
|
|
5947
5964
|
ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
|
|
5948
|
-
// ── Render window items (nested via renderedParentMap) ────────────────────
|
|
5949
5965
|
var liListBuf = [];
|
|
5950
5966
|
for (var i = start; i < end; i++) {
|
|
5951
5967
|
var node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
|
|
5952
5968
|
var li = this.createVirtualListItem(node, i === start);
|
|
5953
|
-
// Find the correct parent container
|
|
5954
5969
|
var targetUl = ul;
|
|
5955
5970
|
if (node.parentId && renderedParentMap.has(node.parentId)) {
|
|
5956
5971
|
var parentEntry = renderedParentMap.get(node.parentId);
|
|
5957
|
-
// Lazily create the child <ul> the first time a child needs it
|
|
5958
5972
|
if (!parentEntry.childUl) {
|
|
5959
5973
|
parentEntry.childUl = this.createElement('ul', {
|
|
5960
5974
|
className: PARENTITEM + ' e-ul',
|
|
@@ -5966,30 +5980,11 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5966
5980
|
}
|
|
5967
5981
|
targetUl.appendChild(li);
|
|
5968
5982
|
liListBuf.push(li);
|
|
5969
|
-
// Register this node so its children in the same window can nest under it
|
|
5970
5983
|
if (node.expanded && node.hasChildren) {
|
|
5971
5984
|
renderedParentMap.set(node.id, { li: li, childUl: null });
|
|
5972
5985
|
}
|
|
5973
5986
|
}
|
|
5974
|
-
// ── Restore focus / tabindex ──────────────────────────────────────────────
|
|
5975
|
-
// In virtual mode only the li that carries the 'e-active' class should be
|
|
5976
|
-
// focusable (tabindex='0'). All other rendered li elements must be set
|
|
5977
|
-
// to tabindex='-1' so the tab order remains unambiguous as the window
|
|
5978
|
-
// changes during scrolling.
|
|
5979
5987
|
var focusRestored = false;
|
|
5980
|
-
// 1. Prefer the currently active node if it is in the rendered window.
|
|
5981
|
-
for (var k = 0; k < liListBuf.length; k++) {
|
|
5982
|
-
var li = liListBuf[parseInt(k.toString(), 10)];
|
|
5983
|
-
if (li.classList.contains(ACTIVE)) {
|
|
5984
|
-
li.setAttribute('tabindex', '0');
|
|
5985
|
-
// ── Fix: ensure FOCUS class set on active node too ──
|
|
5986
|
-
// This ensures focusIn() guard condition is met: tabindex='0' && FOCUS class
|
|
5987
|
-
addClass([li], FOCUS);
|
|
5988
|
-
focusRestored = true;
|
|
5989
|
-
break;
|
|
5990
|
-
}
|
|
5991
|
-
}
|
|
5992
|
-
// 2. Otherwise fall back to the previously focused node (if it is in window).
|
|
5993
5988
|
if (!focusRestored && focusedUid) {
|
|
5994
5989
|
for (var k = 0; k < liListBuf.length; k++) {
|
|
5995
5990
|
var li = liListBuf[parseInt(k.toString(), 10)];
|
|
@@ -5997,22 +5992,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
5997
5992
|
li.setAttribute('tabindex', '0');
|
|
5998
5993
|
addClass([li], FOCUS);
|
|
5999
5994
|
addClass([li], FOCUSED);
|
|
6000
|
-
// ── Fix: restore actual browser focus so keyboard events keep working ──
|
|
6001
|
-
// After renderVirtualItems() replaces all <li> elements the old element
|
|
6002
|
-
// is gone from the DOM and document.activeElement falls back to
|
|
6003
|
-
// document.body. Setting tabindex + classes is not enough — the new
|
|
6004
|
-
// element also needs a programmatic .focus() call so subsequent keyboard
|
|
6005
|
-
// events (Arrow keys, Home, End, etc.) are delivered to the TreeView.
|
|
6006
|
-
// Re-attach the blur→focusOut handler so virtualFocusedId is cleared
|
|
6007
|
-
// correctly when the user tabs away.
|
|
6008
|
-
// ── Use preventScroll:true ────────────────────────────────────────────
|
|
6009
|
-
// Without preventScroll the browser scrolls the container to bring this
|
|
6010
|
-
// node into view during the re-render, changing scrollTop AFTER the
|
|
6011
|
-
// correct paddingTop has been applied. This is the root cause of the
|
|
6012
|
-
// KBN expand position-jump bug: the render is correct but the browser
|
|
6013
|
-
// silently scrolls the viewport, making the parent node appear to jump.
|
|
6014
|
-
// ensureVirtualNodeVisible() handles any legitimate scroll-into-view
|
|
6015
|
-
// needs before renderVirtualItems() is called, so scroll here is wrong.
|
|
6016
5995
|
EventHandler.add(li, 'blur', this.focusOut, this);
|
|
6017
5996
|
li.focus({ preventScroll: true });
|
|
6018
5997
|
focusRestored = true;
|
|
@@ -6020,53 +5999,36 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6020
5999
|
}
|
|
6021
6000
|
}
|
|
6022
6001
|
}
|
|
6023
|
-
// 3. Finally default to the first item in the window.
|
|
6024
6002
|
if (!focusRestored && liListBuf.length > 0) {
|
|
6025
6003
|
var firstLi = liListBuf[0];
|
|
6026
6004
|
firstLi.setAttribute('tabindex', '0');
|
|
6027
6005
|
}
|
|
6028
|
-
// ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
|
|
6029
|
-
// This prevents stale tabindex="0" on multiple nodes which breaks Tab order
|
|
6030
6006
|
for (var k = 0; k < liListBuf.length; k++) {
|
|
6031
6007
|
var li = liListBuf[parseInt(k.toString(), 10)];
|
|
6032
6008
|
if (li.getAttribute('tabindex') !== '0') {
|
|
6033
6009
|
li.setAttribute('tabindex', '-1');
|
|
6034
6010
|
}
|
|
6035
6011
|
}
|
|
6036
|
-
// Keep liList in sync so keyboard-navigation helpers stay functional.
|
|
6037
6012
|
this.liList = liListBuf;
|
|
6038
6013
|
};
|
|
6039
|
-
/**
|
|
6040
|
-
* Creates a single `<li>` element for the virtual window representing `node`.
|
|
6041
|
-
*
|
|
6042
|
-
* @param {VirtualNodeData} node - The node data.
|
|
6043
|
-
* @param {boolean} _isFirst - True if this is the first rendered item (unused here but kept for potential override).
|
|
6044
|
-
* @returns {HTMLElement} The constructed `<li>` element.
|
|
6045
|
-
* @private
|
|
6046
|
-
*/
|
|
6047
6014
|
TreeView.prototype.createVirtualListItem = function (node, _isFirst) {
|
|
6048
6015
|
var li = this.createElement('li', { className: LISTITEM });
|
|
6049
|
-
// Level class for CSS indentation
|
|
6050
6016
|
li.classList.add('e-level-' + node.level);
|
|
6051
6017
|
li.setAttribute('data-uid', node.id);
|
|
6052
6018
|
li.setAttribute('aria-level', node.level.toString());
|
|
6053
6019
|
li.setAttribute('role', 'treeitem');
|
|
6054
6020
|
li.setAttribute('tabindex', '-1');
|
|
6055
|
-
// Full-row overlay (required for fullRowSelect mode)
|
|
6056
6021
|
if (this.fullRowSelect) {
|
|
6057
6022
|
var fullRow = this.createElement('div', { className: FULLROW });
|
|
6058
6023
|
li.appendChild(fullRow);
|
|
6059
6024
|
}
|
|
6060
|
-
// Text/icon wrapper
|
|
6061
6025
|
var textWrap = this.createElement('div', { className: TEXTWRAP });
|
|
6062
|
-
// Expand/collapse icon
|
|
6063
6026
|
if (node.hasChildren) {
|
|
6064
6027
|
var iconSpan = this.createElement('div', {
|
|
6065
6028
|
className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
|
|
6066
6029
|
});
|
|
6067
6030
|
textWrap.appendChild(iconSpan);
|
|
6068
6031
|
}
|
|
6069
|
-
// Checkbox
|
|
6070
6032
|
if (this.showCheckBox) {
|
|
6071
6033
|
var checkWrap = createCheckBox(this.createElement, false, {
|
|
6072
6034
|
checked: node.isChecked === true,
|
|
@@ -6080,28 +6042,24 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6080
6042
|
}
|
|
6081
6043
|
textWrap.appendChild(checkWrap);
|
|
6082
6044
|
}
|
|
6083
|
-
// Icon
|
|
6084
6045
|
if (node.iconCss) {
|
|
6085
6046
|
var iconEl = this.createElement('span', {
|
|
6086
6047
|
className: ICON + ' ' + node.iconCss
|
|
6087
6048
|
});
|
|
6088
6049
|
textWrap.appendChild(iconEl);
|
|
6089
6050
|
}
|
|
6090
|
-
// Image
|
|
6091
6051
|
if (node.imageUrl) {
|
|
6092
6052
|
var img = document.createElement('img');
|
|
6093
6053
|
img.src = node.imageUrl;
|
|
6094
6054
|
img.classList.add(IMAGE);
|
|
6095
6055
|
textWrap.appendChild(img);
|
|
6096
6056
|
}
|
|
6097
|
-
// Text anchor
|
|
6098
6057
|
var anchor = this.createElement('span', {
|
|
6099
6058
|
className: 'e-list-text',
|
|
6100
6059
|
innerHTML: node.text
|
|
6101
6060
|
});
|
|
6102
6061
|
textWrap.appendChild(anchor);
|
|
6103
6062
|
li.appendChild(textWrap);
|
|
6104
|
-
// nodeTemplate
|
|
6105
6063
|
if (!isNOU(this.nodeTemplateFn)) {
|
|
6106
6064
|
var rawData = this.findNodeInTreeData(node.id);
|
|
6107
6065
|
if (!isNOU(rawData)) {
|
|
@@ -6109,7 +6067,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6109
6067
|
this.renderNodeTemplate(rawData, anchor, node.id);
|
|
6110
6068
|
}
|
|
6111
6069
|
}
|
|
6112
|
-
// State classes
|
|
6113
6070
|
if (node.expanded) {
|
|
6114
6071
|
li.classList.add(EXPANDED);
|
|
6115
6072
|
li.setAttribute('aria-expanded', 'true');
|
|
@@ -6125,80 +6082,43 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6125
6082
|
if (node.hasChildren) {
|
|
6126
6083
|
li.classList.add(CHILD);
|
|
6127
6084
|
}
|
|
6128
|
-
// Wire the focus event so `focusIn()` fires when the user Tabs into this
|
|
6129
|
-
// item. The non-virtualized path registers this in `beforeNodeCreate()`,
|
|
6130
|
-
// but virtual items never go through that callback, so we must register
|
|
6131
|
-
// it here to keep focus styling (e.g. `e-focused`) consistent between
|
|
6132
|
-
// virtual and non-virtual modes. `unWireEvents()` already removes this
|
|
6133
|
-
// handler during teardown by iterating over the rendered `<li>`s.
|
|
6134
6085
|
EventHandler.add(li, 'focus', this.focusIn, this);
|
|
6135
6086
|
return li;
|
|
6136
6087
|
};
|
|
6137
|
-
/**
|
|
6138
|
-
* Initialises the virtual-scrolling layout:
|
|
6139
|
-
* 1. Adds the `e-virtualization` CSS class and applies a fixed `height` + `overflow-y`.
|
|
6140
|
-
* 2. Flattens all visible nodes into `virtualFlatNodes`.
|
|
6141
|
-
* 3. Measures item height and viewport height.
|
|
6142
|
-
* 4. Computes the initial window and renders it.
|
|
6143
|
-
* 5. Wires the scroll event handler.
|
|
6144
|
-
*
|
|
6145
|
-
* @returns {void}
|
|
6146
|
-
* @private
|
|
6147
|
-
*/
|
|
6148
6088
|
TreeView.prototype.initVirtualization = function () {
|
|
6149
6089
|
addClass([this.element], VIRTUALIZATION);
|
|
6150
|
-
// Apply container height
|
|
6151
6090
|
var px = this.parseHeightToPixels();
|
|
6152
6091
|
if (px > 0) {
|
|
6153
6092
|
this.element.style.height = px + 'px';
|
|
6154
6093
|
this.element.style.overflowY = 'auto';
|
|
6155
6094
|
}
|
|
6156
|
-
// Ensure the inner UL is ready
|
|
6157
6095
|
if (!this.ulElement) {
|
|
6158
6096
|
this.ulElement = select('.' + PARENTITEM, this.element);
|
|
6159
6097
|
}
|
|
6160
6098
|
if (!this.ulElement) {
|
|
6161
6099
|
return;
|
|
6162
6100
|
}
|
|
6163
|
-
// Make the UL position-relative for the absolute spacers
|
|
6164
6101
|
this.ulElement.style.position = 'relative';
|
|
6165
|
-
// Rebuild flat list
|
|
6166
6102
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
6167
6103
|
this.rebuildVirtualNodeMap();
|
|
6168
|
-
// Measure dimensions
|
|
6169
6104
|
this.virtualViewportHeight = px > 0 ? px : this.element.offsetHeight;
|
|
6170
|
-
// Render a single probe item to measure the real item height before the full render
|
|
6171
6105
|
if (this.virtualFlatNodes.length > 0) {
|
|
6172
6106
|
var probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
|
|
6173
6107
|
this.ulElement.appendChild(probe);
|
|
6174
6108
|
this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
|
|
6175
6109
|
this.ulElement.removeChild(probe);
|
|
6176
6110
|
}
|
|
6177
|
-
// Compute window and render all items in the initial viewport
|
|
6178
6111
|
var win = this.getVirtualWindow();
|
|
6179
6112
|
this.virtualStartIndex = win.startIndex;
|
|
6180
6113
|
this.virtualEndIndex = win.endIndex;
|
|
6181
6114
|
this.renderVirtualItems();
|
|
6182
|
-
// Wire scroll
|
|
6183
6115
|
this.wireVirtualScrollEvents();
|
|
6184
6116
|
};
|
|
6185
|
-
/**
|
|
6186
|
-
* Wires the scroll event listener on the TreeView element.
|
|
6187
|
-
*
|
|
6188
|
-
* @returns {void}
|
|
6189
|
-
* @private
|
|
6190
|
-
*/
|
|
6191
6117
|
TreeView.prototype.wireVirtualScrollEvents = function () {
|
|
6192
6118
|
this.unwireVirtualScrollEvents();
|
|
6193
6119
|
this.virtualScrollBound = this.onVirtualScroll.bind(this);
|
|
6194
6120
|
this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
|
|
6195
6121
|
};
|
|
6196
|
-
/**
|
|
6197
|
-
* Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
|
|
6198
|
-
*
|
|
6199
|
-
* @returns {void}
|
|
6200
|
-
* @private
|
|
6201
|
-
*/
|
|
6202
6122
|
TreeView.prototype.unwireVirtualScrollEvents = function () {
|
|
6203
6123
|
if (this.virtualScrollBound) {
|
|
6204
6124
|
this.element.removeEventListener('scroll', this.virtualScrollBound);
|
|
@@ -6209,29 +6129,13 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6209
6129
|
this.virtualScrollRAF = null;
|
|
6210
6130
|
}
|
|
6211
6131
|
};
|
|
6212
|
-
/**
|
|
6213
|
-
* Scroll handler for the virtual TreeView. Throttled via `requestAnimationFrame`.
|
|
6214
|
-
* Shows the loading mask during fast scrolls, re-renders the window, then hides the mask.
|
|
6215
|
-
*
|
|
6216
|
-
* @returns {void}
|
|
6217
|
-
* @private
|
|
6218
|
-
*/
|
|
6219
6132
|
TreeView.prototype.onVirtualScroll = function () {
|
|
6220
6133
|
var _this = this;
|
|
6221
6134
|
if (this.virtualScrollRAF !== null) {
|
|
6222
6135
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
6223
6136
|
}
|
|
6224
|
-
// ── Tab-induced focus guard ────────────────────────────────────────────────
|
|
6225
|
-
// When the user Tabs into a `<li tabindex="0">` that is in the middle of
|
|
6226
|
-
// the treeview, the browser auto-scrolls the container so the focused
|
|
6227
|
-
// element becomes visible. That fires a `scroll` event here, but the
|
|
6228
|
-
// virtual window already contains the focused element — re-rendering
|
|
6229
|
-
// would destroy the focused `<li>` and break focus. Detect this case
|
|
6230
|
-
// via the flag set in `keyDownHandler` and skip the re-render entirely.
|
|
6231
6137
|
if (this.virtualSkipNextScrollRender) {
|
|
6232
6138
|
this.virtualSkipNextScrollRender = false;
|
|
6233
|
-
// Update prev scrollTop so a subsequent user-driven scroll still
|
|
6234
|
-
// calculates the correct delta.
|
|
6235
6139
|
this.virtualPrevScrollTop = this.element.scrollTop;
|
|
6236
6140
|
return;
|
|
6237
6141
|
}
|
|
@@ -6248,30 +6152,16 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6248
6152
|
_this.virtualStartIndex = win.startIndex;
|
|
6249
6153
|
_this.virtualEndIndex = win.endIndex;
|
|
6250
6154
|
_this.renderVirtualItems();
|
|
6251
|
-
// Hide the skeleton mask after a short delay so it stays visible long enough
|
|
6252
|
-
// for the user to perceive the loading indicator even on short scrolls.
|
|
6253
6155
|
setTimeout(function () {
|
|
6254
6156
|
_this.hideVirtualMask();
|
|
6255
|
-
// Keep the (now hidden) mask's coords fresh for the next show.
|
|
6256
6157
|
_this.updateMaskPosition();
|
|
6257
6158
|
}, 200);
|
|
6258
6159
|
});
|
|
6259
6160
|
};
|
|
6260
|
-
/**
|
|
6261
|
-
* Creates the skeleton mask overlay used to indicate loading during fast scrolls.
|
|
6262
|
-
* Skeleton instances are stored in `skeletonInstances` for later disposal.
|
|
6263
|
-
*
|
|
6264
|
-
* @returns {void}
|
|
6265
|
-
* @private
|
|
6266
|
-
*/
|
|
6267
6161
|
TreeView.prototype.createVirtualMask = function () {
|
|
6268
6162
|
if (this.virtualMaskElement) {
|
|
6269
6163
|
return;
|
|
6270
6164
|
}
|
|
6271
|
-
// Use position:fixed so the mask is anchored to the visible viewport (not to the
|
|
6272
|
-
// scrolling container). Width/height/left/top are set/refreshed by
|
|
6273
|
-
// updateMaskPosition() so the mask always covers the TreeView's visible area
|
|
6274
|
-
// regardless of how the page itself is scrolled.
|
|
6275
6165
|
var rect = this.element.getBoundingClientRect();
|
|
6276
6166
|
var scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
|
|
6277
6167
|
var maskParent = this.createElement('div', {
|
|
@@ -6284,20 +6174,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6284
6174
|
className: VIRTUAL_MASK_ITEM,
|
|
6285
6175
|
styles: "display:flex;align-items:center;height:" + this.virtualItemHeight + "px;padding:0 12px;gap:8px;"
|
|
6286
6176
|
});
|
|
6287
|
-
// Skeleton circle for icon placeholder
|
|
6288
|
-
// const iconHolder: HTMLElement = this.createElement('span', {
|
|
6289
|
-
// styles: `width:20px;height:20px;`
|
|
6290
|
-
// });
|
|
6291
|
-
// maskItem.appendChild(iconHolder);
|
|
6292
|
-
// const iconSkel: Skeleton = new Skeleton({
|
|
6293
|
-
// shape: 'Circle',
|
|
6294
|
-
// width: '20px',
|
|
6295
|
-
// height: '20px',
|
|
6296
|
-
// cssClass: 'e-virtual-skel-icon'
|
|
6297
|
-
// });
|
|
6298
|
-
// iconSkel.appendTo(iconHolder);
|
|
6299
|
-
// this.skeletonInstances.push(iconSkel);
|
|
6300
|
-
// Skeleton text bar
|
|
6301
6177
|
var textHolder = this.createElement('span');
|
|
6302
6178
|
textHolder.style.flex = '1';
|
|
6303
6179
|
textHolder.style.height = '14px';
|
|
@@ -6315,14 +6191,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6315
6191
|
this.virtualMaskElement = maskParent;
|
|
6316
6192
|
this.element.appendChild(maskParent);
|
|
6317
6193
|
};
|
|
6318
|
-
/**
|
|
6319
|
-
* Refreshes the mask position so it always overlays the TreeView's viewport.
|
|
6320
|
-
* Called on every show / scroll, since `position:fixed` is relative to the
|
|
6321
|
-
* page viewport, not to the TreeView scroll container.
|
|
6322
|
-
*
|
|
6323
|
-
* @returns {void}
|
|
6324
|
-
* @private
|
|
6325
|
-
*/
|
|
6326
6194
|
TreeView.prototype.updateMaskPosition = function () {
|
|
6327
6195
|
if (!this.virtualMaskElement) {
|
|
6328
6196
|
return;
|
|
@@ -6334,12 +6202,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6334
6202
|
this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
|
|
6335
6203
|
this.virtualMaskElement.style.height = rect.height + 'px';
|
|
6336
6204
|
};
|
|
6337
|
-
/**
|
|
6338
|
-
* Destroys all skeleton instances and removes the mask overlay from the DOM.
|
|
6339
|
-
*
|
|
6340
|
-
* @returns {void}
|
|
6341
|
-
* @private
|
|
6342
|
-
*/
|
|
6343
6205
|
TreeView.prototype.destroyVirtualMask = function () {
|
|
6344
6206
|
for (var i = this.skeletonInstances.length - 1; i >= 0; i--) {
|
|
6345
6207
|
this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
|
|
@@ -6352,52 +6214,26 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6352
6214
|
this.virtualMaskElement = null;
|
|
6353
6215
|
}
|
|
6354
6216
|
};
|
|
6355
|
-
/**
|
|
6356
|
-
* Positions and shows the skeleton mask at the current `scrollTop` position.
|
|
6357
|
-
*
|
|
6358
|
-
* @param {number} _scrollTop - Current scrollTop of the container.
|
|
6359
|
-
* @returns {void}
|
|
6360
|
-
* @private
|
|
6361
|
-
*/
|
|
6362
6217
|
TreeView.prototype.showVirtualMask = function (_scrollTop) {
|
|
6363
6218
|
if (!this.virtualMaskElement) {
|
|
6364
6219
|
this.createVirtualMask();
|
|
6365
6220
|
}
|
|
6366
6221
|
if (this.virtualMaskElement) {
|
|
6367
|
-
// Refresh the fixed-position coords in case the TreeView was moved/resized
|
|
6368
|
-
// since the last time the mask was shown.
|
|
6369
6222
|
this.updateMaskPosition();
|
|
6370
6223
|
this.virtualMaskElement.style.display = 'block';
|
|
6371
|
-
// Visually hide the real list while the skeleton is shown
|
|
6372
6224
|
if (this.ulElement) {
|
|
6373
6225
|
this.ulElement.style.visibility = 'hidden';
|
|
6374
6226
|
}
|
|
6375
6227
|
}
|
|
6376
6228
|
};
|
|
6377
|
-
/**
|
|
6378
|
-
* Hides the skeleton mask overlay after the virtual window has been rendered.
|
|
6379
|
-
*
|
|
6380
|
-
* @returns {void}
|
|
6381
|
-
* @private
|
|
6382
|
-
*/
|
|
6383
6229
|
TreeView.prototype.hideVirtualMask = function () {
|
|
6384
6230
|
if (this.virtualMaskElement) {
|
|
6385
6231
|
this.virtualMaskElement.style.display = 'none';
|
|
6386
|
-
// Restore real list visibility
|
|
6387
6232
|
if (this.ulElement) {
|
|
6388
6233
|
this.ulElement.style.visibility = '';
|
|
6389
6234
|
}
|
|
6390
6235
|
}
|
|
6391
6236
|
};
|
|
6392
|
-
/**
|
|
6393
|
-
* Ensures the node identified by `id` is scrolled into view inside the virtual container.
|
|
6394
|
-
* If the node falls outside the rendered window, the container's `scrollTop` is adjusted so
|
|
6395
|
-
* that the node becomes visible, the window is re-computed and `renderVirtualItems` is called.
|
|
6396
|
-
*
|
|
6397
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
6398
|
-
* @returns {void}
|
|
6399
|
-
* @private
|
|
6400
|
-
*/
|
|
6401
6237
|
TreeView.prototype.ensureVirtualNodeVisible = function (id) {
|
|
6402
6238
|
var idx = this.virtualNodeMap.get(id);
|
|
6403
6239
|
if (isNOU(idx)) {
|
|
@@ -6416,25 +6252,13 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6416
6252
|
this.element.scrollTop = nodeBottom - vpH;
|
|
6417
6253
|
}
|
|
6418
6254
|
else {
|
|
6419
|
-
return;
|
|
6255
|
+
return;
|
|
6420
6256
|
}
|
|
6421
|
-
// Re-render the new window synchronously so the <li> is in the DOM
|
|
6422
6257
|
var win = this.getVirtualWindow();
|
|
6423
6258
|
this.virtualStartIndex = win.startIndex;
|
|
6424
6259
|
this.virtualEndIndex = win.endIndex;
|
|
6425
6260
|
this.renderVirtualItems();
|
|
6426
6261
|
};
|
|
6427
|
-
/**
|
|
6428
|
-
* Recursively walks the treeData (and any nested `childFieldName` arrays) to
|
|
6429
|
-
* find the node whose `idFieldName` matches the given `id` (compared as
|
|
6430
|
-
* strings). Used by the virtual-mode expand/collapse handlers so they can
|
|
6431
|
-
* locate nodes at any depth — not just the root level — before writing the
|
|
6432
|
-
* `expanded` flag back to the datasource.
|
|
6433
|
-
*
|
|
6434
|
-
* @param {string} id - The id (as a string) of the node to locate.
|
|
6435
|
-
* @returns {Object | null} The matching raw datasource node, or null if not found.
|
|
6436
|
-
* @private
|
|
6437
|
-
*/
|
|
6438
6262
|
TreeView.prototype.findNodeInTreeData = function (id) {
|
|
6439
6263
|
if (!this.treeData) {
|
|
6440
6264
|
return null;
|
|
@@ -6462,12 +6286,19 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6462
6286
|
};
|
|
6463
6287
|
return findRecursive(this.treeData);
|
|
6464
6288
|
};
|
|
6289
|
+
TreeView.prototype.getNodeLevel = function (id) {
|
|
6290
|
+
if (!this.virtualFlatNodes) {
|
|
6291
|
+
return 1;
|
|
6292
|
+
}
|
|
6293
|
+
for (var i = 0; i < this.virtualFlatNodes.length; i++) {
|
|
6294
|
+
if (this.virtualFlatNodes[parseInt(i.toString(), 10)].id === id) {
|
|
6295
|
+
return this.virtualFlatNodes[parseInt(i.toString(), 10)].level;
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
return 1;
|
|
6299
|
+
};
|
|
6465
6300
|
TreeView.prototype.handleVirtualExpand = function (id, isKeyboardNav) {
|
|
6466
|
-
|
|
6467
|
-
// A scroll RAF queued before the expand click would run asynchronously after
|
|
6468
|
-
// this synchronous expand completes, re-rendering the window from a stale
|
|
6469
|
-
// scrollTop and moving the parent node to the wrong position. Cancel it now
|
|
6470
|
-
// so the expand render is authoritative.
|
|
6301
|
+
var _this = this;
|
|
6471
6302
|
if (this.virtualScrollRAF !== null) {
|
|
6472
6303
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
6473
6304
|
this.virtualScrollRAF = null;
|
|
@@ -6475,7 +6306,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6475
6306
|
if (this.expandedNodes.indexOf(id) === -1) {
|
|
6476
6307
|
this.expandedNodes.push(id);
|
|
6477
6308
|
}
|
|
6478
|
-
// Also update virtualExpandedNodesSet to keep it in sync
|
|
6479
6309
|
if (this.virtualExpandedNodesSet) {
|
|
6480
6310
|
this.virtualExpandedNodesSet.add(id);
|
|
6481
6311
|
}
|
|
@@ -6485,56 +6315,76 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6485
6315
|
setValue(this.fields.expanded, true, acutalRootData);
|
|
6486
6316
|
}
|
|
6487
6317
|
}
|
|
6488
|
-
// ── Fix: Pin focused ID to the expanding node for keyboard navigation ──
|
|
6489
|
-
// When keyboard-triggered, the expanded node IS definitively the focused node
|
|
6490
|
-
// (`id` comes from currLi.getAttribute('data-uid') in expandNode, where currLi
|
|
6491
|
-
// is getFocusedNode()). Set unconditionally — the old conditional guard
|
|
6492
|
-
// `!this.virtualFocusedId` could leave a stale or null value if a previous
|
|
6493
|
-
// RAF render (from arrow-key scroll) hadn't yet completed, causing branch 3
|
|
6494
|
-
// (first item) to fire in renderVirtualItems() instead of branch 2.
|
|
6495
|
-
// For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
|
|
6496
|
-
// renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
|
|
6497
|
-
// mouse-based expand/collapse should never add the e-focused class.
|
|
6498
6318
|
if (isKeyboardNav) {
|
|
6499
6319
|
this.virtualFocusedId = id;
|
|
6500
6320
|
}
|
|
6501
6321
|
else {
|
|
6502
6322
|
this.virtualFocusedId = null;
|
|
6503
6323
|
}
|
|
6324
|
+
if (this.fields.dataSource instanceof DataManager && !this.isOffline) {
|
|
6325
|
+
var parentNode = this.findNodeInTreeData(id);
|
|
6326
|
+
if (parentNode && !isNOU(parentNode)) {
|
|
6327
|
+
var childFieldName = this.fields.child.toString();
|
|
6328
|
+
var childrenObj = getValue(childFieldName, parentNode);
|
|
6329
|
+
var childrenNotLoaded = isNOU(childrenObj) || !Array.isArray(childrenObj) ||
|
|
6330
|
+
childrenObj.length === 0;
|
|
6331
|
+
if (childrenNotLoaded) {
|
|
6332
|
+
this.loadVirtualChildData(id, parentNode, function () {
|
|
6333
|
+
_this.completeVirtualExpand(id, isKeyboardNav);
|
|
6334
|
+
});
|
|
6335
|
+
return;
|
|
6336
|
+
}
|
|
6337
|
+
}
|
|
6338
|
+
}
|
|
6339
|
+
this.completeVirtualExpand(id, isKeyboardNav);
|
|
6340
|
+
};
|
|
6341
|
+
TreeView.prototype.loadVirtualChildData = function (parentId, parentNode, callback) {
|
|
6342
|
+
var _this = this;
|
|
6343
|
+
var level = this.getNodeLevel(parentId);
|
|
6344
|
+
var mapper = this.getChildFields(this.fields, level + 1, 1);
|
|
6345
|
+
if (isNOU(mapper) || isNOU(mapper.dataSource)) {
|
|
6346
|
+
if (callback) {
|
|
6347
|
+
callback();
|
|
6348
|
+
}
|
|
6349
|
+
return;
|
|
6350
|
+
}
|
|
6351
|
+
mapper.dataSource.executeQuery(this.getQuery(mapper, parentId)).then(function (e) {
|
|
6352
|
+
var childItems = e.result;
|
|
6353
|
+
if (!isNOU(childItems) && childItems.length > 0) {
|
|
6354
|
+
var childFieldName = _this.fields.child.toString();
|
|
6355
|
+
setValue(childFieldName, childItems, parentNode);
|
|
6356
|
+
if (_this.dataType === 2) {
|
|
6357
|
+
var nodeInTree = _this.findNodeInTreeData(parentId);
|
|
6358
|
+
if (nodeInTree) {
|
|
6359
|
+
setValue(childFieldName, childItems, nodeInTree);
|
|
6360
|
+
}
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
if (callback) {
|
|
6364
|
+
callback();
|
|
6365
|
+
}
|
|
6366
|
+
}).catch(function (e) {
|
|
6367
|
+
_this.trigger('actionFailure', { error: e });
|
|
6368
|
+
if (callback) {
|
|
6369
|
+
callback();
|
|
6370
|
+
}
|
|
6371
|
+
});
|
|
6372
|
+
};
|
|
6373
|
+
TreeView.prototype.completeVirtualExpand = function (id, isKeyboardNav) {
|
|
6504
6374
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
6505
6375
|
this.rebuildVirtualNodeMap();
|
|
6506
|
-
// ── Snapshot scrollTop before computing the window ────────────────────────
|
|
6507
|
-
// Read scrollTop once here and pass it into getVirtualWindow so that any
|
|
6508
|
-
// browser scroll-anchoring adjustment that happens when the padding values
|
|
6509
|
-
// change cannot affect which window we render.
|
|
6510
6376
|
var scrollTopSnapshot = this.element.scrollTop;
|
|
6511
6377
|
var win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
6512
6378
|
this.virtualStartIndex = win.startIndex;
|
|
6513
6379
|
this.virtualEndIndex = win.endIndex;
|
|
6514
|
-
// ── Block ALL scroll-triggered re-renders during this expand ─────────────
|
|
6515
|
-
// renderVirtualItems() calls li.focus() in branch 2 (keyboard focus restore).
|
|
6516
|
-
// The post-render focus() call below can also trigger browser scroll. Both
|
|
6517
|
-
// can fire the 'scroll' event and cause onVirtualScroll() to queue a RAF.
|
|
6518
|
-
// We set the skip flag ONCE here; it is consumed by the first scroll event.
|
|
6519
|
-
// To cover the second potential scroll (from the focus() call below) we use
|
|
6520
|
-
// preventScroll:true on that call so it never moves the container at all.
|
|
6521
6380
|
this.virtualSkipNextScrollRender = true;
|
|
6522
6381
|
this.renderVirtualItems();
|
|
6523
|
-
// renderVirtualItems() replaces all <li> elements so the old icon element that
|
|
6524
|
-
// had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
|
|
6525
|
-
// so the node can be collapsed/expanded again without being blocked.
|
|
6526
6382
|
var newLi = this.getElement(id);
|
|
6527
6383
|
if (newLi) {
|
|
6528
6384
|
var icon = select('.' + ICON, newLi);
|
|
6529
6385
|
if (icon) {
|
|
6530
6386
|
removeClass([icon], PROCESS);
|
|
6531
6387
|
}
|
|
6532
|
-
// ── Use preventScroll:true ────────────────────────────────────────────
|
|
6533
|
-
// Without preventScroll the browser scrolls the container to bring this
|
|
6534
|
-
// node into view, which fires a second 'scroll' event. The first scroll
|
|
6535
|
-
// was already consumed by virtualSkipNextScrollRender (set above), so the
|
|
6536
|
-
// second scroll would queue a new RAF that re-renders from the
|
|
6537
|
-
// focus-adjusted scrollTop — moving the parent node to the wrong position.
|
|
6538
6388
|
newLi.focus({ preventScroll: true });
|
|
6539
6389
|
}
|
|
6540
6390
|
if (this.isLoaded) {
|
|
@@ -6547,9 +6397,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6547
6397
|
}
|
|
6548
6398
|
};
|
|
6549
6399
|
TreeView.prototype.handleVirtualCollapse = function (id, isKeyboardNav) {
|
|
6550
|
-
// ── Cancel any pending scroll RAF ─────────────────────────────────────────
|
|
6551
|
-
// Mirrors handleVirtualExpand: a queued RAF from a previous scroll event
|
|
6552
|
-
// must not override the collapse render with a stale window.
|
|
6553
6400
|
if (this.virtualScrollRAF !== null) {
|
|
6554
6401
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
6555
6402
|
this.virtualScrollRAF = null;
|
|
@@ -6558,7 +6405,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6558
6405
|
if (idx !== -1) {
|
|
6559
6406
|
this.expandedNodes.splice(idx, 1);
|
|
6560
6407
|
}
|
|
6561
|
-
// Also update virtualExpandedNodesSet to keep it in sync
|
|
6562
6408
|
if (this.virtualExpandedNodesSet) {
|
|
6563
6409
|
this.virtualExpandedNodesSet.delete(id);
|
|
6564
6410
|
}
|
|
@@ -6568,13 +6414,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6568
6414
|
setValue(this.fields.expanded, false, acutalRootData);
|
|
6569
6415
|
}
|
|
6570
6416
|
}
|
|
6571
|
-
// ── Fix: Pin focused ID to the collapsing node for keyboard navigation ──
|
|
6572
|
-
// Same reasoning as handleVirtualExpand: when keyboard-triggered, `id` is
|
|
6573
|
-
// definitively the focused node. Set unconditionally to avoid the stale/null
|
|
6574
|
-
// state that could cause the RAF re-render to fall through to branch 3.
|
|
6575
|
-
// For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
|
|
6576
|
-
// renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
|
|
6577
|
-
// mouse-based expand/collapse should never add the e-focused class.
|
|
6578
6417
|
if (isKeyboardNav) {
|
|
6579
6418
|
this.virtualFocusedId = id;
|
|
6580
6419
|
}
|
|
@@ -6583,28 +6422,18 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6583
6422
|
}
|
|
6584
6423
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
6585
6424
|
this.rebuildVirtualNodeMap();
|
|
6586
|
-
// ── Snapshot scrollTop before computing the window ────────────────────────
|
|
6587
|
-
// Same rationale as handleVirtualExpand: capture scrollTop before any focus()
|
|
6588
|
-
// call or padding change can cause the browser to auto-scroll.
|
|
6589
6425
|
var scrollTopSnapshot = this.element.scrollTop;
|
|
6590
6426
|
var win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
6591
6427
|
this.virtualStartIndex = win.startIndex;
|
|
6592
6428
|
this.virtualEndIndex = win.endIndex;
|
|
6593
|
-
// ── Block ALL scroll-triggered re-renders during this collapse ────────────
|
|
6594
|
-
// Same as handleVirtualExpand: use preventScroll:true on the focus() call
|
|
6595
|
-
// below so the second potential scroll event is never fired.
|
|
6596
6429
|
this.virtualSkipNextScrollRender = true;
|
|
6597
6430
|
this.renderVirtualItems();
|
|
6598
|
-
// renderVirtualItems() replaces all <li> elements so the old icon element that
|
|
6599
|
-
// had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
|
|
6600
|
-
// so the node can be expanded/collapsed again without being blocked.
|
|
6601
6431
|
var newLi = this.getElement(id);
|
|
6602
6432
|
if (newLi) {
|
|
6603
6433
|
var icon = select('.' + ICON, newLi);
|
|
6604
6434
|
if (icon) {
|
|
6605
6435
|
removeClass([icon], PROCESS);
|
|
6606
6436
|
}
|
|
6607
|
-
// ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
|
|
6608
6437
|
newLi.focus({ preventScroll: true });
|
|
6609
6438
|
}
|
|
6610
6439
|
if (this.isLoaded) {
|
|
@@ -6616,14 +6445,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6616
6445
|
}
|
|
6617
6446
|
}
|
|
6618
6447
|
};
|
|
6619
|
-
/**
|
|
6620
|
-
* Updates the selected state of a node in `virtualFlatNodes` and re-renders the window.
|
|
6621
|
-
*
|
|
6622
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
6623
|
-
* @param {boolean} selected - Whether the node is now selected.
|
|
6624
|
-
* @returns {void}
|
|
6625
|
-
* @private
|
|
6626
|
-
*/
|
|
6627
6448
|
TreeView.prototype.updateVirtualNodeSelection = function (id, selected) {
|
|
6628
6449
|
var idx = this.virtualNodeMap.get(id);
|
|
6629
6450
|
if (!isNOU(idx)) {
|
|
@@ -6631,21 +6452,11 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6631
6452
|
this.renderVirtualItems();
|
|
6632
6453
|
}
|
|
6633
6454
|
};
|
|
6634
|
-
/**
|
|
6635
|
-
* Updates the checkbox state of a node in `virtualFlatNodes` and re-renders the window.
|
|
6636
|
-
*
|
|
6637
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
6638
|
-
* @param {'check' | 'uncheck' | 'indeterminate'} state - The new check state.
|
|
6639
|
-
* @returns {void}
|
|
6640
|
-
* @private
|
|
6641
|
-
*/
|
|
6642
6455
|
TreeView.prototype.updateVirtualNodeCheckState = function (id, state) {
|
|
6643
6456
|
var idx = this.virtualNodeMap.get(id);
|
|
6644
6457
|
if (!isNOU(idx)) {
|
|
6645
6458
|
this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
|
|
6646
6459
|
state === 'check' ? true : state === 'indeterminate' ? 'indeterminate' : false;
|
|
6647
|
-
// Keep the persistent indeterminate set in sync so flattenVisibleNodes()
|
|
6648
|
-
// correctly restores state after any full list rebuild (e.g. collapse).
|
|
6649
6460
|
if (state === 'indeterminate') {
|
|
6650
6461
|
this.virtualIndeterminateNodesSet.add(id);
|
|
6651
6462
|
}
|
|
@@ -6655,9 +6466,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6655
6466
|
this.renderVirtualItems();
|
|
6656
6467
|
}
|
|
6657
6468
|
};
|
|
6658
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
6659
|
-
// END VIRTUALIZATION
|
|
6660
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
6661
6469
|
/**
|
|
6662
6470
|
* Called internally if any of the property value changed.
|
|
6663
6471
|
*
|
|
@@ -6802,7 +6610,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6802
6610
|
}
|
|
6803
6611
|
}
|
|
6804
6612
|
else {
|
|
6805
|
-
// Tear down virtualization and do a full re-render
|
|
6806
6613
|
this.unwireVirtualScrollEvents();
|
|
6807
6614
|
this.destroyVirtualMask();
|
|
6808
6615
|
removeClass([this.element], VIRTUALIZATION);
|
|
@@ -6813,7 +6620,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6813
6620
|
break;
|
|
6814
6621
|
case 'height':
|
|
6815
6622
|
if (this.enableVirtualization) {
|
|
6816
|
-
// Re-initialise so the new height takes effect
|
|
6817
6623
|
this.unwireVirtualScrollEvents();
|
|
6818
6624
|
this.initVirtualization();
|
|
6819
6625
|
}
|
|
@@ -6831,7 +6637,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6831
6637
|
* @returns {void}
|
|
6832
6638
|
*/
|
|
6833
6639
|
TreeView.prototype.destroy = function () {
|
|
6834
|
-
// Tear down virtualization before standard cleanup
|
|
6835
6640
|
if (this.enableVirtualization) {
|
|
6836
6641
|
this.unwireVirtualScrollEvents();
|
|
6837
6642
|
this.destroyVirtualMask();
|
|
@@ -6922,10 +6727,6 @@ var TreeView = /** @class */ (function (_super) {
|
|
|
6922
6727
|
if (isNOU(fNode)) {
|
|
6923
6728
|
this.setNodeFocusable();
|
|
6924
6729
|
}
|
|
6925
|
-
// In virtual mode (with a fixed height), rebuild the flat node list and re-render
|
|
6926
|
-
// the window so the newly added nodes appear in the correct hierarchical position.
|
|
6927
|
-
// When height is not provided, the tree uses normal rendering and the DOM is
|
|
6928
|
-
// already updated by addGivenNodes() above.
|
|
6929
6730
|
if (this.enableVirtualization && !isNOU(this.height)) {
|
|
6930
6731
|
this.setRootData();
|
|
6931
6732
|
this.rebuildVirtualCheckCache();
|