@syncfusion/ej2-navigations 34.2.3 → 34.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ej2-navigations.min.js +2 -2
- package/dist/ej2-navigations.umd.min.js +2 -2
- package/dist/ej2-navigations.umd.min.js.map +1 -1
- package/dist/es6/ej2-navigations.es2015.js +204 -546
- package/dist/es6/ej2-navigations.es2015.js.map +1 -1
- package/dist/es6/ej2-navigations.es5.js +206 -546
- package/dist/es6/ej2-navigations.es5.js.map +1 -1
- package/dist/global/ej2-navigations.min.js +2 -2
- package/dist/global/ej2-navigations.min.js.map +1 -1
- package/dist/global/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/treeview/treeview.d.ts +5 -221
- package/src/treeview/treeview.js +206 -546
|
@@ -10698,54 +10698,25 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
10698
10698
|
this.isDropIn = false;
|
|
10699
10699
|
this.OldCheckedData = [];
|
|
10700
10700
|
this.isHiddenItem = false;
|
|
10701
|
-
//
|
|
10702
|
-
/** Flat ordered list of all currently-visible (non-collapsed) tree nodes. */
|
|
10701
|
+
// Virtualization state
|
|
10703
10702
|
this.virtualFlatNodes = [];
|
|
10704
|
-
/** Maps node id → index in virtualFlatNodes for O(1) lookups. */
|
|
10705
10703
|
this.virtualNodeMap = new Map();
|
|
10706
|
-
/** Set of expanded node IDs for O(1) lookup performance during flattening (avoids array indexOf). */
|
|
10707
10704
|
this.virtualExpandedNodesSet = new Set();
|
|
10708
|
-
/** Set of checked node IDs for O(1) lookup performance during flattening. */
|
|
10709
10705
|
this.virtualCheckedNodesSet = new Set();
|
|
10710
|
-
/** Set of indeterminate node IDs — persists across flattenVisibleNodes() rebuilds. */
|
|
10711
10706
|
this.virtualIndeterminateNodesSet = new Set();
|
|
10712
|
-
/** Set of selected node IDs for O(1) lookup performance during flattening. */
|
|
10713
10707
|
this.virtualSelectedNodesSet = new Set();
|
|
10714
|
-
/** Row height in pixels, measured from the first rendered item or set to 40 as a default. */
|
|
10715
10708
|
this.virtualItemHeight = 40;
|
|
10716
|
-
/** Height of the scrollable container in pixels. */
|
|
10717
10709
|
this.virtualViewportHeight = 0;
|
|
10718
|
-
/** scrollTop captured during the previous scroll handler invocation. */
|
|
10719
10710
|
this.virtualPrevScrollTop = 0;
|
|
10720
|
-
/** Index of the first rendered row in the current window. */
|
|
10721
10711
|
this.virtualStartIndex = 0;
|
|
10722
|
-
/** Index one past the last rendered row in the current window. */
|
|
10723
10712
|
this.virtualEndIndex = 0;
|
|
10724
|
-
/** Extra rows rendered above and below the visible window to smooth scrolling. */
|
|
10725
10713
|
this.virtualOverscanCount = 3;
|
|
10726
|
-
/** RequestAnimationFrame handle used for throttling scroll events. */
|
|
10727
10714
|
this.virtualScrollRAF = null;
|
|
10728
|
-
/** The `.e-mask-parent` overlay element shown during fast scroll. */
|
|
10729
10715
|
this.virtualMaskElement = null;
|
|
10730
|
-
/** Bound scroll handler stored for later removeEventListener. */
|
|
10731
10716
|
this.virtualScrollBound = null;
|
|
10732
|
-
/** Skeleton component instances inside the mask overlay. */
|
|
10733
10717
|
this.skeletonInstances = [];
|
|
10734
|
-
/** Set to true whenever the flat node list must be rebuilt on next render. */
|
|
10735
10718
|
this.virtualTreeDirty = false;
|
|
10736
|
-
/** The data-uid of the currently keyboard-focused node in virtual mode (persists across re-renders). */
|
|
10737
10719
|
this.virtualFocusedId = null;
|
|
10738
|
-
/**
|
|
10739
|
-
* Set to true by the document-level `keydown` handler when the user presses
|
|
10740
|
-
* the Tab key. The browser will auto-scroll the focused element into view
|
|
10741
|
-
* immediately after focus, which fires a `scroll` event on the TreeView
|
|
10742
|
-
* container. We want to skip the re-render that the virtual scroll handler
|
|
10743
|
-
* would otherwise trigger, because the element being focused is already
|
|
10744
|
-
* rendered (it is the single `<li tabindex="0">` of the current window) and
|
|
10745
|
-
* re-rendering would tear down the DOM element that is currently holding
|
|
10746
|
-
* focus. The flag is consumed (reset to `false`) inside `onVirtualScroll`
|
|
10747
|
-
* after the auto-scroll has been handled.
|
|
10748
|
-
*/
|
|
10749
10720
|
this.virtualSkipNextScrollRender = false;
|
|
10750
10721
|
}
|
|
10751
10722
|
/**
|
|
@@ -11042,16 +11013,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
11042
11013
|
}
|
|
11043
11014
|
}
|
|
11044
11015
|
renderItems(isSorted) {
|
|
11045
|
-
// In virtual mode (with a fixed height) create only the container <ul> —
|
|
11046
|
-
// initVirtualization will populate the items directly, avoiding the O(n)
|
|
11047
|
-
// ListBase.createList cost.
|
|
11048
|
-
// updateCheckedStateFromDS / updateParentCheckState are intentionally skipped here:
|
|
11049
|
-
// they call getCheckedNodeDetails which traverses the DOM (no DOM exists yet in
|
|
11050
|
-
// virtual mode) and would throw on empty recursion results. The initial checked
|
|
11051
|
-
// state is derived from the raw data inside flattenNodeRecursive instead.
|
|
11052
|
-
//
|
|
11053
|
-
// When enableVirtualization is true but height is NOT provided, we fall through
|
|
11054
|
-
// to the normal (non-virtualized) rendering path so the tree is still visible.
|
|
11055
11016
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
11056
11017
|
this.ulElement = this.createElement('ul', {
|
|
11057
11018
|
className: PARENTITEM + ' ' + 'e-ul',
|
|
@@ -12127,7 +12088,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
12127
12088
|
this.checkDisabledState(id);
|
|
12128
12089
|
}
|
|
12129
12090
|
if (this.enableVirtualization) {
|
|
12130
|
-
// Clear any indeterminate state — node is now fully checked
|
|
12131
12091
|
this.virtualIndeterminateNodesSet.delete(id);
|
|
12132
12092
|
const actualIndex = this.virtualNodeMap.get(id);
|
|
12133
12093
|
if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
|
|
@@ -12148,20 +12108,15 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
12148
12108
|
const actualIndex = this.virtualNodeMap.get(id);
|
|
12149
12109
|
if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
|
|
12150
12110
|
if (isIndeterminate) {
|
|
12151
|
-
// Parent is transitioning to indeterminate — preserve children states
|
|
12152
|
-
// and mark this node as 'indeterminate' so scroll/collapse re-renders correctly.
|
|
12153
12111
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = 'indeterminate';
|
|
12154
|
-
// Also record it in the persistent indeterminate set so flattenVisibleNodes()
|
|
12155
|
-
// can restore this state after a full rebuild (e.g. on collapse).
|
|
12156
12112
|
this.virtualIndeterminateNodesSet.add(id);
|
|
12157
12113
|
}
|
|
12158
12114
|
else {
|
|
12159
|
-
// Node is being fully unchecked — clear indeterminate tracking too
|
|
12160
12115
|
this.virtualIndeterminateNodesSet.delete(id);
|
|
12161
12116
|
this.virtualCheckedNodesSet.delete(id);
|
|
12162
12117
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].isChecked = false;
|
|
12163
12118
|
if (this.treeData && this.fields && this.fields.isChecked) {
|
|
12164
|
-
|
|
12119
|
+
this.updateField(this.treeData, this.fields, id, this.fields.isChecked, false);
|
|
12165
12120
|
}
|
|
12166
12121
|
if (this.autoCheck && this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].hasChildren) {
|
|
12167
12122
|
this.setVirtualChildCheckState(id, false);
|
|
@@ -12207,8 +12162,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
12207
12162
|
}
|
|
12208
12163
|
}
|
|
12209
12164
|
}
|
|
12210
|
-
// Collapsed children are not present in virtualFlatNodes, so walk the raw data
|
|
12211
|
-
// to keep this.treeData and checkedNodes in sync for hidden descendants too.
|
|
12212
12165
|
const collapsedChildren = this.getChildNodes(this.treeData, parentId);
|
|
12213
12166
|
if (collapsedChildren) {
|
|
12214
12167
|
for (let i = 0; i < collapsedChildren.length; i++) {
|
|
@@ -12352,7 +12305,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
12352
12305
|
}
|
|
12353
12306
|
this.isLoaded = true;
|
|
12354
12307
|
this.isNodeDropped = false;
|
|
12355
|
-
// Initialise (or re-initialise) virtual mode after data binding has settled
|
|
12356
12308
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
12357
12309
|
this.initVirtualization();
|
|
12358
12310
|
}
|
|
@@ -12490,9 +12442,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
12490
12442
|
this.trigger('nodeClicked', eventArgs);
|
|
12491
12443
|
}
|
|
12492
12444
|
expandNode(currLi, icon, loaded, isKeyboardNav) {
|
|
12493
|
-
// Virtual mode fast-path: rebuild the flat list and re-render the window.
|
|
12494
|
-
// Only engage when height is also set; without a fixed height the tree falls
|
|
12495
|
-
// back to normal rendering and the standard expand path must be used.
|
|
12496
12445
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
12497
12446
|
const id = currLi ? currLi.getAttribute('data-uid') : null;
|
|
12498
12447
|
if (id) {
|
|
@@ -12619,9 +12568,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
12619
12568
|
}
|
|
12620
12569
|
}
|
|
12621
12570
|
nodeCollapseAction(currLi, icon, colArgs, isKeyboardNav) {
|
|
12622
|
-
// Virtual mode fast-path: rebuild the flat list and re-render the window.
|
|
12623
|
-
// Only engage when height is also set; without a fixed height the tree falls
|
|
12624
|
-
// back to normal rendering and the standard collapse path must be used.
|
|
12625
12571
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
12626
12572
|
const id = currLi ? currLi.getAttribute('data-uid') : null;
|
|
12627
12573
|
if (id) {
|
|
@@ -13082,7 +13028,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13082
13028
|
li.setAttribute('tabindex', '0');
|
|
13083
13029
|
EventHandler.add(li, 'blur', this.focusOut, this);
|
|
13084
13030
|
this.updateIdAttr(focusedNode, li);
|
|
13085
|
-
// Track focused id for virtual mode re-render restoration
|
|
13086
13031
|
if (this.enableVirtualization) {
|
|
13087
13032
|
this.virtualFocusedId = li.getAttribute('data-uid');
|
|
13088
13033
|
}
|
|
@@ -13098,7 +13043,7 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13098
13043
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
13099
13044
|
this.virtualSelectedNodesSet.add(id);
|
|
13100
13045
|
const actualIndex = this.virtualNodeMap.get(id);
|
|
13101
|
-
if (actualIndex && actualIndex > -1) {
|
|
13046
|
+
if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
|
|
13102
13047
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = true;
|
|
13103
13048
|
}
|
|
13104
13049
|
}
|
|
@@ -13119,7 +13064,7 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13119
13064
|
const id = liEle.getAttribute('data-uid');
|
|
13120
13065
|
this.virtualSelectedNodesSet.delete(id);
|
|
13121
13066
|
const actualIndex = this.virtualNodeMap.get(id);
|
|
13122
|
-
if (actualIndex && actualIndex > -1) {
|
|
13067
|
+
if (!isNullOrUndefined(actualIndex) && actualIndex > -1) {
|
|
13123
13068
|
this.virtualFlatNodes[parseInt(actualIndex.toString(), 10)].selected = false;
|
|
13124
13069
|
}
|
|
13125
13070
|
}
|
|
@@ -13212,12 +13157,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13212
13157
|
}
|
|
13213
13158
|
}
|
|
13214
13159
|
nodeExpandAction(currLi, icon, expandChild, callback, isFromExpandAll, isKeyboardNav) {
|
|
13215
|
-
// In virtual mode (with a fixed height) the flat <li> never has a child <ul>
|
|
13216
|
-
// in the DOM, so the PARENTITEM check below would always fail and fall into
|
|
13217
|
-
// renderChildNodes(), which corrupts the virtual DOM.
|
|
13218
|
-
// Delegate directly to expandNode() which has the handleVirtualExpand fast-path.
|
|
13219
|
-
// When height is not provided, fall through to normal expand logic below since
|
|
13220
|
-
// the tree is rendered with a full nested DOM structure.
|
|
13221
13160
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
13222
13161
|
this.expandNode(currLi, icon, undefined, isKeyboardNav);
|
|
13223
13162
|
return;
|
|
@@ -13334,9 +13273,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13334
13273
|
});
|
|
13335
13274
|
}
|
|
13336
13275
|
navigateToFocus(isUp) {
|
|
13337
|
-
// In virtual mode (with a fixed height) the element itself is the scroll
|
|
13338
|
-
// container; use ensureVirtualNodeVisible. When height is not provided,
|
|
13339
|
-
// the tree uses normal rendering, so fall through to the standard scroll logic.
|
|
13340
13276
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
13341
13277
|
const focusedLi = this.getFocusedNode();
|
|
13342
13278
|
if (focusedLi) {
|
|
@@ -13766,7 +13702,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13766
13702
|
}
|
|
13767
13703
|
focusNextNode(li, isTowards) {
|
|
13768
13704
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
13769
|
-
// Determine the id of the node one step in the requested direction
|
|
13770
13705
|
const currentId = li ? li.getAttribute('data-uid') : null;
|
|
13771
13706
|
let currentIdx = -1;
|
|
13772
13707
|
if (!isNullOrUndefined(currentId)) {
|
|
@@ -13778,26 +13713,14 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13778
13713
|
return;
|
|
13779
13714
|
}
|
|
13780
13715
|
const targetId = this.virtualFlatNodes[parseInt(targetIdx.toString(), 10)].id;
|
|
13781
|
-
// ── Fix 1: set virtualFocusedId BEFORE calling ensureVirtualNodeVisible ──
|
|
13782
|
-
// renderVirtualItems() is called synchronously inside ensureVirtualNodeVisible
|
|
13783
|
-
// when a re-render is needed. If virtualFocusedId is still the old value at
|
|
13784
|
-
// that point, focus restoration falls back to the first item. Setting it here
|
|
13785
|
-
// ensures renderVirtualItems always restores focus to the correct target node.
|
|
13786
13716
|
this.virtualFocusedId = targetId;
|
|
13787
|
-
// ── Fix 2: clear focus classes from ALL nested list items, not just top-level ones ──
|
|
13788
|
-
// With hierarchical virtual DOM, the currently focused <li> may be nested inside
|
|
13789
|
-
// a child <ul>. Using ':scope > li' only cleans up direct children and leaves
|
|
13790
|
-
// stale e-focused / e-node-focus classes on nested nodes across re-renders.
|
|
13791
13717
|
const allLi = this.element.querySelectorAll('.' + LISTITEM + '.' + FOCUS);
|
|
13792
13718
|
for (let k = 0; k < allLi.length; k++) {
|
|
13793
13719
|
allLi[parseInt(k.toString(), 10)].classList.remove(FOCUS);
|
|
13794
13720
|
allLi[parseInt(k.toString(), 10)].classList.remove(FOCUSED$1);
|
|
13795
13721
|
allLi[parseInt(k.toString(), 10)].setAttribute('tabindex', '-1');
|
|
13796
13722
|
}
|
|
13797
|
-
// Ensure the target <li> is in the DOM window (may trigger a re-render).
|
|
13798
|
-
// virtualFocusedId is already set above so renderVirtualItems restores focus correctly.
|
|
13799
13723
|
this.ensureVirtualNodeVisible(targetId);
|
|
13800
|
-
// Re-query both nodes after the potential re-render so setFocus gets live DOM elements
|
|
13801
13724
|
const prevNode = currentId ? this.getElement(currentId) : li;
|
|
13802
13725
|
const nextNode = this.getElement(targetId);
|
|
13803
13726
|
if (!isNullOrUndefined(nextNode)) {
|
|
@@ -13898,18 +13821,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
13898
13821
|
if (!focusedElement) {
|
|
13899
13822
|
return;
|
|
13900
13823
|
}
|
|
13901
|
-
// Virtual-mode guard: when the browser moves focus into the TreeView (e.g. via
|
|
13902
|
-
// the Tab key) it always lands on the single `<li tabindex="0">`. In that
|
|
13903
|
-
// case the element is already the focusable one, FOCUS is already applied
|
|
13904
|
-
// and the only thing left to do is the keyboard focus styling. Skip the
|
|
13905
|
-
// redundant setAttribute/EventHandler.add work so we never re-render the
|
|
13906
|
-
// virtual window as a side effect of focusing the existing `tabindex="0"`
|
|
13907
|
-
// item.
|
|
13908
|
-
// ── Fix: Use document.activeElement to detect browser-focused node ──
|
|
13909
|
-
// After renderVirtualItems() restores focus, the browser-focused element is
|
|
13910
|
-
// the authoritative source, not getFocusedNode(). If they don't match, it means
|
|
13911
|
-
// renderVirtualItems already correctly set up the element, so we must not
|
|
13912
|
-
// override its setup by calling setFocus again.
|
|
13913
13824
|
const actuallyFocusedElement = document.activeElement;
|
|
13914
13825
|
const isAlreadyFocusedInVirtualMode = this.enableVirtualization
|
|
13915
13826
|
&& actuallyFocusedElement === focusedElement
|
|
@@ -14162,12 +14073,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
14162
14073
|
else {
|
|
14163
14074
|
txtEle[ListBase.getDomSetter(this.listBaseOption)] = ListBase.getSanitizedText(newText, this.listBaseOption);
|
|
14164
14075
|
}
|
|
14165
|
-
// ── Virtual mode: sync the flat-node snapshot so re-renders show updated text ──
|
|
14166
|
-
// virtualFlatNodes is rebuilt from this.treeData (via flattenVisibleNodes) only on
|
|
14167
|
-
// expand/collapse/scroll. Between those events the snapshot is the single source of
|
|
14168
|
-
// truth used by createVirtualListItem. setValue() above updates this.editData (which
|
|
14169
|
-
// IS the treeData object reference), but virtualFlatNodes[idx].text still holds the
|
|
14170
|
-
// old value. Update it here so the next renderVirtualItems() call shows the new text.
|
|
14171
14076
|
if (this.enableVirtualization) {
|
|
14172
14077
|
const id = liEle.getAttribute('data-uid');
|
|
14173
14078
|
const idx = this.virtualNodeMap ? this.virtualNodeMap.get(id) : undefined;
|
|
@@ -14611,11 +14516,49 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
14611
14516
|
this.fields.dataSource = this.treeData;
|
|
14612
14517
|
}
|
|
14613
14518
|
this.triggerEvent('nodeDropped', nodeData);
|
|
14519
|
+
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
14520
|
+
this.setRootData();
|
|
14521
|
+
this.rebuildVirtualCheckCache();
|
|
14522
|
+
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
14523
|
+
this.rebuildVirtualNodeMap();
|
|
14524
|
+
const scrollTopSnapshot = this.element.scrollTop;
|
|
14525
|
+
const win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
14526
|
+
this.virtualStartIndex = win.startIndex;
|
|
14527
|
+
this.virtualEndIndex = win.endIndex;
|
|
14528
|
+
this.virtualSkipNextScrollRender = true;
|
|
14529
|
+
this.renderVirtualItems();
|
|
14530
|
+
}
|
|
14614
14531
|
}
|
|
14615
14532
|
appendNode(dropTarget, dragLi, dropLi, e, dragObj, offsetY) {
|
|
14616
14533
|
const checkWrapper = closest(dropTarget, '.' + CHECKBOXWRAP);
|
|
14617
14534
|
const collapse = closest(e.target, '.' + COLLAPSIBLE);
|
|
14618
14535
|
const expand = closest(e.target, '.' + EXPANDABLE);
|
|
14536
|
+
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
14537
|
+
const routesToSiblingBranch = !dragLi.classList.contains('e-disable') && !checkWrapper &&
|
|
14538
|
+
((expand && e.event.offsetY < 5) || (collapse && e.event.offsetX < 3) ||
|
|
14539
|
+
(expand && e.event.offsetY > 19) || (collapse && e.event.offsetX > 19) ||
|
|
14540
|
+
(!expand && !collapse));
|
|
14541
|
+
if (routesToSiblingBranch) {
|
|
14542
|
+
if (dropTarget.nodeName === 'LI') {
|
|
14543
|
+
this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
|
|
14544
|
+
}
|
|
14545
|
+
else if (dropTarget.firstElementChild && dropTarget.classList.contains(ROOT)) {
|
|
14546
|
+
if (dropTarget.firstElementChild.nodeName === 'UL') {
|
|
14547
|
+
this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
|
|
14548
|
+
}
|
|
14549
|
+
}
|
|
14550
|
+
else if ((dropTarget.classList.contains('e-icon-collapsible')) || (dropTarget.classList.contains('e-icon-expandable'))) {
|
|
14551
|
+
this.virtualMoveAsSibling(dragLi, dropLi, e, dragObj);
|
|
14552
|
+
}
|
|
14553
|
+
else {
|
|
14554
|
+
this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
|
|
14555
|
+
}
|
|
14556
|
+
}
|
|
14557
|
+
else {
|
|
14558
|
+
this.virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget);
|
|
14559
|
+
}
|
|
14560
|
+
return;
|
|
14561
|
+
}
|
|
14619
14562
|
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))) {
|
|
14620
14563
|
if (dropTarget.nodeName === 'LI') {
|
|
14621
14564
|
this.dropAsSiblingNode(dragLi, dropLi, e, dragObj);
|
|
@@ -14639,6 +14582,62 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
14639
14582
|
this.ensureIndeterminate();
|
|
14640
14583
|
}
|
|
14641
14584
|
}
|
|
14585
|
+
virtualMoveAsSibling(dragLi, dropLi, e, dragObj) {
|
|
14586
|
+
const dropUl = closest(dropLi, '.' + PARENTITEM);
|
|
14587
|
+
let pre;
|
|
14588
|
+
if (e.target.offsetHeight > 0 && e.event.offsetY > e.target.offsetHeight - 2) {
|
|
14589
|
+
pre = false;
|
|
14590
|
+
}
|
|
14591
|
+
else if (e.event.offsetY < 2) {
|
|
14592
|
+
pre = true;
|
|
14593
|
+
}
|
|
14594
|
+
else if (e.target.classList.contains('e-icon-expandable') || (e.target.classList.contains('e-icon-collapsible'))) {
|
|
14595
|
+
if ((e.event.offsetY < 5) || (e.event.offsetX < 3)) {
|
|
14596
|
+
pre = true;
|
|
14597
|
+
}
|
|
14598
|
+
else if ((e.event.offsetY > 15) || (e.event.offsetX > 17)) {
|
|
14599
|
+
pre = false;
|
|
14600
|
+
}
|
|
14601
|
+
}
|
|
14602
|
+
this.moveData(dragLi, dropLi, dropUl, pre, dragObj);
|
|
14603
|
+
}
|
|
14604
|
+
virtualMoveAsChild(dragLi, dropLi, dragObj, offsetY, dropTarget) {
|
|
14605
|
+
const pos = offsetY;
|
|
14606
|
+
const isSiblingMarker = dropTarget.classList.contains('e-sibling');
|
|
14607
|
+
const refLi = (pos < 7 && !isSiblingMarker) || (isSiblingMarker &&
|
|
14608
|
+
dropLi.lastChild && !dropLi.lastChild.classList.contains('e-sibling'))
|
|
14609
|
+
? dropLi : null;
|
|
14610
|
+
const dropTargetHt = dropTarget;
|
|
14611
|
+
const isAfter = (dropTargetHt.offsetHeight > 0 && pos > dropTargetHt.offsetHeight - 10);
|
|
14612
|
+
if (refLi) {
|
|
14613
|
+
const dropParentUl = closest(dropLi, '.' + PARENTITEM);
|
|
14614
|
+
this.moveData(dragLi, dropLi, dropParentUl, true, dragObj);
|
|
14615
|
+
}
|
|
14616
|
+
else if (isAfter) {
|
|
14617
|
+
const dropParentUl = closest(dropLi, '.' + PARENTITEM);
|
|
14618
|
+
this.moveData(dragLi, dropLi, dropParentUl, false, dragObj);
|
|
14619
|
+
}
|
|
14620
|
+
else {
|
|
14621
|
+
const dropIcon = select('div.' + EXPANDABLE + ', div.' + COLLAPSIBLE, dropLi);
|
|
14622
|
+
const dropIsCollapsedParent = dropIcon && dropIcon.classList.contains(EXPANDABLE);
|
|
14623
|
+
const dropIsLeaf = isNullOrUndefined(dropIcon);
|
|
14624
|
+
if (this.preventExpand !== true && (dropIsCollapsedParent || dropIsLeaf)) {
|
|
14625
|
+
const dropLiId = dropLi.getAttribute('data-uid');
|
|
14626
|
+
if (dropLiId && this.expandedNodes.indexOf(dropLiId) === -1) {
|
|
14627
|
+
this.expandedNodes.push(dropLiId);
|
|
14628
|
+
}
|
|
14629
|
+
if (this.virtualExpandedNodesSet && dropLiId) {
|
|
14630
|
+
this.virtualExpandedNodesSet.add(dropLiId);
|
|
14631
|
+
}
|
|
14632
|
+
const dropRecord = this.findNodeInTreeData(dropLiId);
|
|
14633
|
+
if (dropRecord && this.fields && this.fields.expanded) {
|
|
14634
|
+
setValue(this.fields.expanded, true, dropRecord);
|
|
14635
|
+
}
|
|
14636
|
+
}
|
|
14637
|
+
const dropUl = select('.' + PARENTITEM, dropLi) || dropLi;
|
|
14638
|
+
this.moveData(dragLi, null, dropUl, true, dragObj);
|
|
14639
|
+
}
|
|
14640
|
+
}
|
|
14642
14641
|
dropAsSiblingNode(dragLi, dropLi, e, dragObj) {
|
|
14643
14642
|
const dropUl = closest(dropLi, '.' + PARENTITEM);
|
|
14644
14643
|
const dragParentUl = closest(dragLi, '.' + PARENTITEM);
|
|
@@ -14726,7 +14725,7 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
14726
14725
|
delete removedData[0][this.fields.parentID];
|
|
14727
14726
|
}
|
|
14728
14727
|
else {
|
|
14729
|
-
const currPid = this.isNumberTypeId ? parseFloat(parentId) : parentId;
|
|
14728
|
+
const currPid = this.isNumberTypeId && parentId ? parseFloat(parentId) : parentId;
|
|
14730
14729
|
setValue(this.fields.parentID, currPid, removedData[0]);
|
|
14731
14730
|
}
|
|
14732
14731
|
this.treeData.splice(pos, 0, removedData[0]);
|
|
@@ -14868,7 +14867,7 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
14868
14867
|
const draggedNodeData = obj ? obj.dragData : null;
|
|
14869
14868
|
const newParent = dropTarget ? this.parents(dropTarget, '.' + LISTITEM) : null;
|
|
14870
14869
|
const dragLiParent = obj.dragLi.parentElement;
|
|
14871
|
-
let dragParent = obj.dragLi ? closest(dragLiParent, '.' + LISTITEM) : null;
|
|
14870
|
+
let dragParent = obj.dragLi && dragLiParent ? closest(dragLiParent, '.' + LISTITEM) : null;
|
|
14872
14871
|
let targetParent = null;
|
|
14873
14872
|
let indexValue = null;
|
|
14874
14873
|
const iconCss = [DROPNEXT, DROPIN, DROPOUT, NODROP];
|
|
@@ -14877,7 +14876,7 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
14877
14876
|
const index = node ? closest(node, '.e-list-parent') : null;
|
|
14878
14877
|
let i = 0;
|
|
14879
14878
|
let position = null;
|
|
14880
|
-
dragParent = (obj.dragLi && dragParent === null) ? closest(dragLiParent, '.' + ROOT) : dragParent;
|
|
14879
|
+
dragParent = (obj.dragLi && dragParent === null) && dragLiParent ? closest(dragLiParent, '.' + ROOT) : dragParent;
|
|
14881
14880
|
dragParent = (drop === true) ? this.dragParent : dragParent;
|
|
14882
14881
|
if (cloneEle) {
|
|
14883
14882
|
while (i < 4) {
|
|
@@ -15544,26 +15543,10 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
15544
15543
|
keyDownHandler(e) {
|
|
15545
15544
|
if (e.key === 'Tab' || e.keyCode === 9) {
|
|
15546
15545
|
this.isKeyboardFocus = true;
|
|
15547
|
-
// Mark the upcoming scroll event (if any) as Tab-induced so the
|
|
15548
|
-
// virtual scroll handler skips its re-render path. The browser
|
|
15549
|
-
// calls `Element.scrollIntoView` semantics on focus, which scrolls
|
|
15550
|
-
// a `<li tabindex="0">` in the middle of the tree into view and
|
|
15551
|
-
// therefore fires `scroll` on the container. Without this guard
|
|
15552
|
-
// the scroll handler would tear down the focused `<li>` and move
|
|
15553
|
-
// focus elsewhere.
|
|
15554
15546
|
if (this.enableVirtualization) {
|
|
15555
15547
|
this.virtualSkipNextScrollRender = true;
|
|
15556
15548
|
}
|
|
15557
15549
|
}
|
|
15558
|
-
// ── Fix: Also skip scroll render for arrow key navigation ──────────────────
|
|
15559
|
-
// Arrow Down/Up calls focusNextNode() which calls ensureVirtualNodeVisible()
|
|
15560
|
-
// to scroll the target node into view. Without this guard, the scroll event
|
|
15561
|
-
// would trigger onVirtualScroll() which re-renders the window and resets the
|
|
15562
|
-
// focus that focusNextNode() just set. This manifests as tabindex resetting
|
|
15563
|
-
// to the first item instead of staying on the arrow-navigated target.
|
|
15564
|
-
// Arrow Right/Left (expand/collapse) also calls renderVirtualItems() which
|
|
15565
|
-
// calls li.focus() in branch 2; that can trigger a scroll on an edge node,
|
|
15566
|
-
// so we consume it here to prevent a stale RAF re-render.
|
|
15567
15550
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.keyCode === 40 || e.keyCode === 38
|
|
15568
15551
|
|| e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.keyCode === 39 || e.keyCode === 37) {
|
|
15569
15552
|
if (this.enableVirtualization) {
|
|
@@ -15922,10 +15905,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
15922
15905
|
if (this.dataType === 1) {
|
|
15923
15906
|
this.groupedData = this.getGroupedData(this.treeData, this.fields.parentID);
|
|
15924
15907
|
}
|
|
15925
|
-
// In virtual mode (with a fixed height), rebuild the flat node list and re-render
|
|
15926
|
-
// after deletion so the removed nodes are correctly reflected in the hierarchical
|
|
15927
|
-
// virtual DOM. When height is not provided, the tree uses normal rendering and
|
|
15928
|
-
// the DOM is already updated by removeNode() above.
|
|
15929
15908
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
15930
15909
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
15931
15910
|
this.rebuildVirtualNodeMap();
|
|
@@ -15988,16 +15967,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
15988
15967
|
}
|
|
15989
15968
|
return parentsID;
|
|
15990
15969
|
}
|
|
15991
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
15992
|
-
// VIRTUALIZATION — private helpers
|
|
15993
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
15994
|
-
/**
|
|
15995
|
-
* Parses the `height` property (string like '400px' or number) to a pixel value.
|
|
15996
|
-
* Returns 0 if the value cannot be determined.
|
|
15997
|
-
*
|
|
15998
|
-
* @returns {number} Pixel height.
|
|
15999
|
-
* @private
|
|
16000
|
-
*/
|
|
16001
15970
|
parseHeightToPixels() {
|
|
16002
15971
|
const h = this.height;
|
|
16003
15972
|
if (isNullOrUndefined(h)) {
|
|
@@ -16009,13 +15978,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16009
15978
|
const parsed = parseFloat(h);
|
|
16010
15979
|
return isNaN(parsed) ? 0 : parsed;
|
|
16011
15980
|
}
|
|
16012
|
-
/**
|
|
16013
|
-
* Measures the height of the first rendered list item so subsequent calculations
|
|
16014
|
-
* use real layout values. Falls back to the stored `virtualItemHeight` default (40).
|
|
16015
|
-
*
|
|
16016
|
-
* @returns {number} Row height in pixels.
|
|
16017
|
-
* @private
|
|
16018
|
-
*/
|
|
16019
15981
|
measureVirtualItemHeight() {
|
|
16020
15982
|
const firstLi = select('.' + LISTITEM, this.element);
|
|
16021
15983
|
if (firstLi && firstLi.offsetHeight > 0) {
|
|
@@ -16023,13 +15985,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16023
15985
|
}
|
|
16024
15986
|
return this.virtualItemHeight;
|
|
16025
15987
|
}
|
|
16026
|
-
/**
|
|
16027
|
-
* Rebuilds the cached Sets for checked/selected nodes to enable O(1) lookup
|
|
16028
|
-
* during the flatten operation instead of O(n) array indexOf calls.
|
|
16029
|
-
*
|
|
16030
|
-
* @returns {void}
|
|
16031
|
-
* @private
|
|
16032
|
-
*/
|
|
16033
15988
|
rebuildVirtualCheckCache() {
|
|
16034
15989
|
this.virtualExpandedNodesSet.clear();
|
|
16035
15990
|
this.virtualCheckedNodesSet.clear();
|
|
@@ -16050,20 +16005,10 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16050
16005
|
}
|
|
16051
16006
|
}
|
|
16052
16007
|
}
|
|
16053
|
-
/**
|
|
16054
|
-
* Recursively walks the treeData (or groupedData for flat lists) and builds
|
|
16055
|
-
* `virtualFlatNodes` — an ordered list of every node that is currently visible
|
|
16056
|
-
* (i.e. its ancestors are all expanded). Uses cached Sets for O(1) state lookups.
|
|
16057
|
-
*
|
|
16058
|
-
* @returns {VirtualNodeData[]} Ordered flat list of visible nodes.
|
|
16059
|
-
* @private
|
|
16060
|
-
*/
|
|
16061
16008
|
flattenVisibleNodes() {
|
|
16062
|
-
// Build cached Sets for fast lookups
|
|
16063
16009
|
this.rebuildVirtualCheckCache();
|
|
16064
16010
|
const result = [];
|
|
16065
16011
|
if (this.dataType === 1) {
|
|
16066
|
-
// Flat data with parentID field — use getChildGroup to find root nodes
|
|
16067
16012
|
let rootNodes = [];
|
|
16068
16013
|
if (this.groupedData) {
|
|
16069
16014
|
rootNodes = this.getChildGroup(this.groupedData, null, true);
|
|
@@ -16071,40 +16016,29 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16071
16016
|
if (!rootNodes || rootNodes.length === 0) {
|
|
16072
16017
|
rootNodes = this.rootData;
|
|
16073
16018
|
}
|
|
16074
|
-
// Apply sortOrder to root-level nodes for flat data (same as hierarchical path below)
|
|
16075
16019
|
rootNodes = this.getSortedData(rootNodes);
|
|
16076
16020
|
this.flattenNodeRecursive(rootNodes, 1, null, result);
|
|
16077
16021
|
}
|
|
16078
16022
|
else {
|
|
16079
|
-
// Hierarchical data
|
|
16080
16023
|
const sorted = this.getSortedData(this.rootData);
|
|
16081
16024
|
this.flattenNodeRecursive(sorted, 1, null, result);
|
|
16082
16025
|
}
|
|
16083
16026
|
return result;
|
|
16084
16027
|
}
|
|
16085
|
-
/**
|
|
16086
|
-
* Recursive helper for `flattenVisibleNodes`.
|
|
16087
|
-
*
|
|
16088
|
-
* @param {Object[]} nodes - Array of raw data objects at this level.
|
|
16089
|
-
* @param {number} level - 1-based depth level.
|
|
16090
|
-
* @param {string | null} parentId - The parent node id, or null for root.
|
|
16091
|
-
* @param {VirtualNodeData[]} result - Accumulator array.
|
|
16092
|
-
* @returns {void}
|
|
16093
|
-
* @private
|
|
16094
|
-
*/
|
|
16095
16028
|
flattenNodeRecursive(nodes, level, parentId, result) {
|
|
16096
16029
|
if (!nodes || nodes.length === 0) {
|
|
16097
16030
|
return;
|
|
16098
16031
|
}
|
|
16099
|
-
|
|
16100
|
-
const childFieldName =
|
|
16101
|
-
const expandedFieldName =
|
|
16102
|
-
const isCheckedFieldName =
|
|
16103
|
-
const iconCssFieldName =
|
|
16104
|
-
const imageUrlFieldName =
|
|
16105
|
-
const idFieldName =
|
|
16106
|
-
const textFieldName =
|
|
16107
|
-
const selectedFieldName =
|
|
16032
|
+
const levelMapper = level === 1 ? this.fields : this.getChildFields(this.fields, level, 1);
|
|
16033
|
+
const childFieldName = levelMapper.child && levelMapper.child.toString();
|
|
16034
|
+
const expandedFieldName = levelMapper.expanded;
|
|
16035
|
+
const isCheckedFieldName = levelMapper.isChecked;
|
|
16036
|
+
const iconCssFieldName = levelMapper.iconCss;
|
|
16037
|
+
const imageUrlFieldName = levelMapper.imageUrl;
|
|
16038
|
+
const idFieldName = levelMapper.id;
|
|
16039
|
+
const textFieldName = levelMapper.text;
|
|
16040
|
+
const selectedFieldName = levelMapper.selected;
|
|
16041
|
+
const hasChildrenFieldName = levelMapper.hasChildren;
|
|
16108
16042
|
const needsSorting = this.sortOrder !== 'None';
|
|
16109
16043
|
const isDataTypeFlat = this.dataType === 1;
|
|
16110
16044
|
const enableVirtualization = this.enableVirtualization;
|
|
@@ -16112,36 +16046,57 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16112
16046
|
const raw = nodes[i];
|
|
16113
16047
|
const id = isNullOrUndefined(raw[idFieldName]) ? '' : raw[idFieldName].toString();
|
|
16114
16048
|
const text = isNullOrUndefined(raw[textFieldName]) ? '' : raw[textFieldName].toString();
|
|
16115
|
-
// Use expandedNodes as the single source of truth.
|
|
16116
16049
|
const inExpandedNodes = this.virtualExpandedNodesSet.has(id);
|
|
16117
|
-
const rawExpanded = !inExpandedNodes && getValue(expandedFieldName, raw) === true;
|
|
16050
|
+
const rawExpanded = !inExpandedNodes && expandedFieldName && getValue(expandedFieldName, raw) === true;
|
|
16118
16051
|
const expanded = inExpandedNodes || rawExpanded;
|
|
16119
16052
|
if (rawExpanded && id) {
|
|
16120
16053
|
this.expandedNodes.push(id);
|
|
16121
16054
|
this.virtualExpandedNodesSet.add(id);
|
|
16122
16055
|
}
|
|
16123
|
-
// Inline hasChildRecords check to avoid function call (optimization)
|
|
16124
16056
|
let hasChildren = false;
|
|
16125
|
-
|
|
16057
|
+
const explicitHasChildren = hasChildrenFieldName ? getValue(hasChildrenFieldName, raw) : null;
|
|
16058
|
+
if (levelMapper.dataSource instanceof DataManager && !isNullOrUndefined(explicitHasChildren) && explicitHasChildren) {
|
|
16059
|
+
hasChildren = true;
|
|
16060
|
+
}
|
|
16061
|
+
else if (isDataTypeFlat) {
|
|
16126
16062
|
if (this.groupedData) {
|
|
16127
16063
|
const groupedChild = this.getChildGroup(this.groupedData, id, false);
|
|
16128
16064
|
hasChildren = !isNullOrUndefined(groupedChild) && groupedChild.length > 0;
|
|
16129
16065
|
}
|
|
16130
16066
|
}
|
|
16131
16067
|
else {
|
|
16132
|
-
const childrenObj = getValue(childFieldName, raw);
|
|
16068
|
+
const childrenObj = childFieldName ? getValue(childFieldName, raw) : null;
|
|
16133
16069
|
hasChildren = !isNullOrUndefined(childrenObj) && Array.isArray(childrenObj) && childrenObj.length > 0;
|
|
16134
16070
|
}
|
|
16135
|
-
|
|
16136
|
-
if (
|
|
16137
|
-
|
|
16138
|
-
|
|
16071
|
+
let parentChecked = null;
|
|
16072
|
+
if (levelMapper.dataSource instanceof DataManager && level > 1 && this.autoCheck && parentId) {
|
|
16073
|
+
if (this.virtualCheckedNodesSet.has(parentId) ||
|
|
16074
|
+
(this.checkedNodes && this.checkedNodes.indexOf(parentId) !== -1)) {
|
|
16075
|
+
parentChecked = true;
|
|
16076
|
+
}
|
|
16077
|
+
else {
|
|
16078
|
+
parentChecked = false;
|
|
16079
|
+
}
|
|
16080
|
+
}
|
|
16081
|
+
const rawChecked = isCheckedFieldName && getValue(isCheckedFieldName, raw);
|
|
16082
|
+
if (enableVirtualization && id && !this.virtualCheckedNodesSet.has(id)) {
|
|
16083
|
+
if ((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'true') ||
|
|
16084
|
+
(levelMapper.dataSource instanceof DataManager && parentChecked === true)) {
|
|
16139
16085
|
this.virtualCheckedNodesSet.add(id);
|
|
16140
16086
|
if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
|
|
16141
16087
|
this.checkedNodes.push(id);
|
|
16142
16088
|
}
|
|
16143
16089
|
}
|
|
16144
16090
|
}
|
|
16091
|
+
else if (enableVirtualization && id && this.virtualCheckedNodesSet.has(id) && levelMapper.dataSource instanceof DataManager) {
|
|
16092
|
+
if (level > 1 && !this.virtualIndeterminateNodesSet.has(parentId) && this.autoCheck &&
|
|
16093
|
+
((!isNullOrUndefined(rawChecked) && rawChecked.toString() === 'false') || parentChecked === false)) {
|
|
16094
|
+
this.virtualCheckedNodesSet.delete(id);
|
|
16095
|
+
if (this.checkedNodes && this.checkedNodes.indexOf(id) === -1) {
|
|
16096
|
+
this.checkedNodes.splice(this.checkedNodes.indexOf(id), 1);
|
|
16097
|
+
}
|
|
16098
|
+
}
|
|
16099
|
+
}
|
|
16145
16100
|
if (this.initialRender && enableVirtualization && id && selectedFieldName && !this.virtualSelectedNodesSet.has(id)) {
|
|
16146
16101
|
const rawSelected = getValue(selectedFieldName, raw);
|
|
16147
16102
|
if (!isNullOrUndefined(rawSelected) && rawSelected.toString() === 'true') {
|
|
@@ -16151,10 +16106,7 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16151
16106
|
}
|
|
16152
16107
|
}
|
|
16153
16108
|
}
|
|
16154
|
-
// Use cached Sets for O(1) lookup
|
|
16155
16109
|
const selected = this.virtualSelectedNodesSet.has(id);
|
|
16156
|
-
// Resolve checkbox state: indeterminate takes precedence over the checked set,
|
|
16157
|
-
// because a node can be in the indeterminate state without being in checkedNodes.
|
|
16158
16110
|
const isChecked = (enableVirtualization && this.virtualIndeterminateNodesSet.has(id))
|
|
16159
16111
|
? 'indeterminate'
|
|
16160
16112
|
: this.virtualCheckedNodesSet.has(id);
|
|
@@ -16168,22 +16120,19 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16168
16120
|
};
|
|
16169
16121
|
result.push(node);
|
|
16170
16122
|
if (expanded && hasChildren) {
|
|
16171
|
-
// Determine children list
|
|
16172
16123
|
let children = [];
|
|
16173
16124
|
if (isDataTypeFlat) {
|
|
16174
16125
|
if (this.groupedData) {
|
|
16175
16126
|
children = this.getChildGroup(this.groupedData, id, false);
|
|
16176
|
-
// Apply sortOrder to flat-data children (same as hierarchical path)
|
|
16177
16127
|
if (needsSorting) {
|
|
16178
16128
|
children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
|
|
16179
16129
|
}
|
|
16180
16130
|
}
|
|
16181
16131
|
}
|
|
16182
16132
|
else {
|
|
16183
|
-
const rawChildren = getValue(childFieldName, raw);
|
|
16133
|
+
const rawChildren = childFieldName ? getValue(childFieldName, raw) : null;
|
|
16184
16134
|
if (!isNullOrUndefined(rawChildren) && Array.isArray(rawChildren)) {
|
|
16185
16135
|
children = rawChildren;
|
|
16186
|
-
// Only sort if needed
|
|
16187
16136
|
if (needsSorting) {
|
|
16188
16137
|
children = ListBase.getDataSource(children, ListBase.addSorting(this.sortOrder, this.fields.text));
|
|
16189
16138
|
}
|
|
@@ -16193,35 +16142,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16193
16142
|
}
|
|
16194
16143
|
}
|
|
16195
16144
|
}
|
|
16196
|
-
/**
|
|
16197
|
-
* Checks whether a raw datasource node has child records.
|
|
16198
|
-
*
|
|
16199
|
-
* @param {Object} raw - Raw datasource object.
|
|
16200
|
-
* @returns {boolean} True if children exist.
|
|
16201
|
-
* @private
|
|
16202
|
-
*/
|
|
16203
|
-
hasChildRecords(raw) {
|
|
16204
|
-
if (this.dataType === 1) {
|
|
16205
|
-
if (!this.groupedData) {
|
|
16206
|
-
return false;
|
|
16207
|
-
}
|
|
16208
|
-
const rawId = raw[this.fields.id];
|
|
16209
|
-
if (isNullOrUndefined(rawId)) {
|
|
16210
|
-
return false;
|
|
16211
|
-
}
|
|
16212
|
-
const groupedChild = this.getChildGroup(this.groupedData, rawId.toString(), false);
|
|
16213
|
-
return !isNullOrUndefined(groupedChild) && groupedChild.length > 0;
|
|
16214
|
-
}
|
|
16215
|
-
const childField = this.fields.child.toString();
|
|
16216
|
-
const children = getValue(childField, raw);
|
|
16217
|
-
return !isNullOrUndefined(children) && Array.isArray(children) && children.length > 0;
|
|
16218
|
-
}
|
|
16219
|
-
/**
|
|
16220
|
-
* Rebuilds `virtualNodeMap` (id → index) from the current `virtualFlatNodes`.
|
|
16221
|
-
*
|
|
16222
|
-
* @returns {void}
|
|
16223
|
-
* @private
|
|
16224
|
-
*/
|
|
16225
16145
|
rebuildVirtualNodeMap() {
|
|
16226
16146
|
this.virtualNodeMap.clear();
|
|
16227
16147
|
for (let i = 0; i < this.virtualFlatNodes.length; i++) {
|
|
@@ -16254,49 +16174,12 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16254
16174
|
const paddingBottom = Math.max(0, (total - end) * itemH);
|
|
16255
16175
|
return { paddingTop, paddingBottom };
|
|
16256
16176
|
}
|
|
16257
|
-
/**
|
|
16258
|
-
* Renders the virtual window into a **nested** `<ul>/<li>` hierarchy identical to
|
|
16259
|
-
* the non-virtualized DOM structure.
|
|
16260
|
-
*
|
|
16261
|
-
* ## Scroll math
|
|
16262
|
-
* The flat-index ↔ pixel mapping stays unchanged: every entry in
|
|
16263
|
-
* `virtualFlatNodes` represents exactly one `itemHeight` row.
|
|
16264
|
-
* `paddingTop = startIndex * itemHeight` and
|
|
16265
|
-
* `paddingBottom = (total - endIndex) * itemHeight` keep the scrollbar
|
|
16266
|
-
* thumb in the correct position.
|
|
16267
|
-
*
|
|
16268
|
-
* ## Ancestor injection
|
|
16269
|
-
* When `startIndex` points at a child node whose ancestor(s) have already
|
|
16270
|
-
* scrolled above the viewport (their flat indices are < startIndex), the
|
|
16271
|
-
* ancestors would normally be missing from the DOM, forcing the children to
|
|
16272
|
-
* fall back to the root `<ul>` as flat siblings — producing wrong HTML.
|
|
16273
|
-
*
|
|
16274
|
-
* To fix this, before rendering the window we walk the ancestor chain of the
|
|
16275
|
-
* first visible node and collect every ancestor that lies **outside** the
|
|
16276
|
-
* window. Those ancestors are injected as real `<li>` containers at the
|
|
16277
|
-
* very top of the root `<ul>`. To compensate for their presence, `paddingTop`
|
|
16278
|
-
* is reduced by `injectedCount * itemHeight` so the total scroll height
|
|
16279
|
-
* remains the same.
|
|
16280
|
-
*
|
|
16281
|
-
* Once the ancestors are in the DOM they are registered in `renderedParentMap`
|
|
16282
|
-
* so the normal window-rendering loop can nest children inside them exactly
|
|
16283
|
-
* as it does when both parent and child are in the same window.
|
|
16284
|
-
*
|
|
16285
|
-
* @returns {void}
|
|
16286
|
-
* @private
|
|
16287
|
-
*/
|
|
16288
16177
|
renderVirtualItems() {
|
|
16289
16178
|
if (!this.ulElement) {
|
|
16290
16179
|
return;
|
|
16291
16180
|
}
|
|
16292
16181
|
const ul = this.ulElement;
|
|
16293
|
-
// ── Snapshot focused ID BEFORE clearing the DOM ───────────────────────────
|
|
16294
|
-
// Removing a focused <li> from the DOM triggers a synchronous `blur` event,
|
|
16295
|
-
// which calls focusOut() and sets virtualFocusedId = null. Capturing the id
|
|
16296
|
-
// here (before any removeChild) ensures focus is correctly restored to the
|
|
16297
|
-
// same node after re-render even when keyboard navigation was in progress.
|
|
16298
16182
|
const focusedUid = this.virtualFocusedId || null;
|
|
16299
|
-
// ── Clear previous render (direct children only) ─────────────────────────
|
|
16300
16183
|
const existingItems = ul.querySelectorAll(':scope > li');
|
|
16301
16184
|
for (let i = existingItems.length - 1; i >= 0; i--) {
|
|
16302
16185
|
ul.removeChild(existingItems[parseInt(i.toString(), 10)]);
|
|
@@ -16307,26 +16190,22 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16307
16190
|
if (this.virtualStartIndex < this.virtualFlatNodes.length) {
|
|
16308
16191
|
const firstNode = this.virtualFlatNodes[this.virtualStartIndex];
|
|
16309
16192
|
if (firstNode.parentId) {
|
|
16310
|
-
// Build the ancestor chain from root down to the immediate parent
|
|
16311
16193
|
const ancestors = [];
|
|
16312
16194
|
let searchId = firstNode.parentId;
|
|
16313
16195
|
while (searchId) {
|
|
16314
16196
|
const ancestorIdx = this.virtualNodeMap.get(searchId);
|
|
16315
16197
|
if (!isNullOrUndefined(ancestorIdx) && ancestorIdx < this.virtualStartIndex) {
|
|
16316
16198
|
const ancestor = this.virtualFlatNodes[parseInt(ancestorIdx.toString(), 10)];
|
|
16317
|
-
ancestors.unshift(ancestor);
|
|
16199
|
+
ancestors.unshift(ancestor);
|
|
16318
16200
|
searchId = ancestor.parentId;
|
|
16319
16201
|
}
|
|
16320
16202
|
else {
|
|
16321
16203
|
break;
|
|
16322
16204
|
}
|
|
16323
16205
|
}
|
|
16324
|
-
// Inject each ancestor as a real <li> into the root <ul>
|
|
16325
|
-
// and register it in renderedParentMap for child-nesting below.
|
|
16326
16206
|
for (let a = 0; a < ancestors.length; a++) {
|
|
16327
16207
|
const ancestor = ancestors[parseInt(a.toString(), 10)];
|
|
16328
16208
|
const ancestorLi = this.createVirtualListItem(ancestor, false);
|
|
16329
|
-
// Find the correct container: root or a previously injected ancestor's <ul>
|
|
16330
16209
|
let targetUl = ul;
|
|
16331
16210
|
if (ancestor.parentId && renderedParentMap.has(ancestor.parentId)) {
|
|
16332
16211
|
const pe = renderedParentMap.get(ancestor.parentId);
|
|
@@ -16345,26 +16224,20 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16345
16224
|
}
|
|
16346
16225
|
}
|
|
16347
16226
|
}
|
|
16348
|
-
// ── Scroll-space padding (adjusted for injected ancestors) ────────────────
|
|
16349
16227
|
const total = this.virtualFlatNodes.length;
|
|
16350
16228
|
const start = this.virtualStartIndex;
|
|
16351
16229
|
const end = this.virtualEndIndex;
|
|
16352
16230
|
const rawPaddingTop = start * itemH;
|
|
16353
|
-
// Subtract the height of injected ancestors so the scroll position is
|
|
16354
|
-
// exact: those ancestor rows are now real DOM elements, not hidden space.
|
|
16355
16231
|
const adjustedPaddingTop = Math.max(0, rawPaddingTop - injectedCount * itemH);
|
|
16356
16232
|
ul.style.paddingTop = adjustedPaddingTop + 'px';
|
|
16357
16233
|
ul.style.paddingBottom = Math.max(0, (total - end) * itemH) + 'px';
|
|
16358
|
-
// ── Render window items (nested via renderedParentMap) ────────────────────
|
|
16359
16234
|
const liListBuf = [];
|
|
16360
16235
|
for (let i = start; i < end; i++) {
|
|
16361
16236
|
const node = this.virtualFlatNodes[parseInt(i.toString(), 10)];
|
|
16362
16237
|
const li = this.createVirtualListItem(node, i === start);
|
|
16363
|
-
// Find the correct parent container
|
|
16364
16238
|
let targetUl = ul;
|
|
16365
16239
|
if (node.parentId && renderedParentMap.has(node.parentId)) {
|
|
16366
16240
|
const parentEntry = renderedParentMap.get(node.parentId);
|
|
16367
|
-
// Lazily create the child <ul> the first time a child needs it
|
|
16368
16241
|
if (!parentEntry.childUl) {
|
|
16369
16242
|
parentEntry.childUl = this.createElement('ul', {
|
|
16370
16243
|
className: PARENTITEM + ' e-ul',
|
|
@@ -16376,30 +16249,11 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16376
16249
|
}
|
|
16377
16250
|
targetUl.appendChild(li);
|
|
16378
16251
|
liListBuf.push(li);
|
|
16379
|
-
// Register this node so its children in the same window can nest under it
|
|
16380
16252
|
if (node.expanded && node.hasChildren) {
|
|
16381
16253
|
renderedParentMap.set(node.id, { li, childUl: null });
|
|
16382
16254
|
}
|
|
16383
16255
|
}
|
|
16384
|
-
// ── Restore focus / tabindex ──────────────────────────────────────────────
|
|
16385
|
-
// In virtual mode only the li that carries the 'e-active' class should be
|
|
16386
|
-
// focusable (tabindex='0'). All other rendered li elements must be set
|
|
16387
|
-
// to tabindex='-1' so the tab order remains unambiguous as the window
|
|
16388
|
-
// changes during scrolling.
|
|
16389
16256
|
let focusRestored = false;
|
|
16390
|
-
// 1. Prefer the currently active node if it is in the rendered window.
|
|
16391
|
-
for (let k = 0; k < liListBuf.length; k++) {
|
|
16392
|
-
const li = liListBuf[parseInt(k.toString(), 10)];
|
|
16393
|
-
if (li.classList.contains(ACTIVE)) {
|
|
16394
|
-
li.setAttribute('tabindex', '0');
|
|
16395
|
-
// ── Fix: ensure FOCUS class set on active node too ──
|
|
16396
|
-
// This ensures focusIn() guard condition is met: tabindex='0' && FOCUS class
|
|
16397
|
-
addClass([li], FOCUS);
|
|
16398
|
-
focusRestored = true;
|
|
16399
|
-
break;
|
|
16400
|
-
}
|
|
16401
|
-
}
|
|
16402
|
-
// 2. Otherwise fall back to the previously focused node (if it is in window).
|
|
16403
16257
|
if (!focusRestored && focusedUid) {
|
|
16404
16258
|
for (let k = 0; k < liListBuf.length; k++) {
|
|
16405
16259
|
const li = liListBuf[parseInt(k.toString(), 10)];
|
|
@@ -16407,22 +16261,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16407
16261
|
li.setAttribute('tabindex', '0');
|
|
16408
16262
|
addClass([li], FOCUS);
|
|
16409
16263
|
addClass([li], FOCUSED$1);
|
|
16410
|
-
// ── Fix: restore actual browser focus so keyboard events keep working ──
|
|
16411
|
-
// After renderVirtualItems() replaces all <li> elements the old element
|
|
16412
|
-
// is gone from the DOM and document.activeElement falls back to
|
|
16413
|
-
// document.body. Setting tabindex + classes is not enough — the new
|
|
16414
|
-
// element also needs a programmatic .focus() call so subsequent keyboard
|
|
16415
|
-
// events (Arrow keys, Home, End, etc.) are delivered to the TreeView.
|
|
16416
|
-
// Re-attach the blur→focusOut handler so virtualFocusedId is cleared
|
|
16417
|
-
// correctly when the user tabs away.
|
|
16418
|
-
// ── Use preventScroll:true ────────────────────────────────────────────
|
|
16419
|
-
// Without preventScroll the browser scrolls the container to bring this
|
|
16420
|
-
// node into view during the re-render, changing scrollTop AFTER the
|
|
16421
|
-
// correct paddingTop has been applied. This is the root cause of the
|
|
16422
|
-
// KBN expand position-jump bug: the render is correct but the browser
|
|
16423
|
-
// silently scrolls the viewport, making the parent node appear to jump.
|
|
16424
|
-
// ensureVirtualNodeVisible() handles any legitimate scroll-into-view
|
|
16425
|
-
// needs before renderVirtualItems() is called, so scroll here is wrong.
|
|
16426
16264
|
EventHandler.add(li, 'blur', this.focusOut, this);
|
|
16427
16265
|
li.focus({ preventScroll: true });
|
|
16428
16266
|
focusRestored = true;
|
|
@@ -16430,53 +16268,36 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16430
16268
|
}
|
|
16431
16269
|
}
|
|
16432
16270
|
}
|
|
16433
|
-
// 3. Finally default to the first item in the window.
|
|
16434
16271
|
if (!focusRestored && liListBuf.length > 0) {
|
|
16435
16272
|
const firstLi = liListBuf[0];
|
|
16436
16273
|
firstLi.setAttribute('tabindex', '0');
|
|
16437
16274
|
}
|
|
16438
|
-
// ── Fix: Ensure all other rendered nodes have tabindex="-1" ──
|
|
16439
|
-
// This prevents stale tabindex="0" on multiple nodes which breaks Tab order
|
|
16440
16275
|
for (let k = 0; k < liListBuf.length; k++) {
|
|
16441
16276
|
const li = liListBuf[parseInt(k.toString(), 10)];
|
|
16442
16277
|
if (li.getAttribute('tabindex') !== '0') {
|
|
16443
16278
|
li.setAttribute('tabindex', '-1');
|
|
16444
16279
|
}
|
|
16445
16280
|
}
|
|
16446
|
-
// Keep liList in sync so keyboard-navigation helpers stay functional.
|
|
16447
16281
|
this.liList = liListBuf;
|
|
16448
16282
|
}
|
|
16449
|
-
/**
|
|
16450
|
-
* Creates a single `<li>` element for the virtual window representing `node`.
|
|
16451
|
-
*
|
|
16452
|
-
* @param {VirtualNodeData} node - The node data.
|
|
16453
|
-
* @param {boolean} _isFirst - True if this is the first rendered item (unused here but kept for potential override).
|
|
16454
|
-
* @returns {HTMLElement} The constructed `<li>` element.
|
|
16455
|
-
* @private
|
|
16456
|
-
*/
|
|
16457
16283
|
createVirtualListItem(node, _isFirst) {
|
|
16458
16284
|
const li = this.createElement('li', { className: LISTITEM });
|
|
16459
|
-
// Level class for CSS indentation
|
|
16460
16285
|
li.classList.add('e-level-' + node.level);
|
|
16461
16286
|
li.setAttribute('data-uid', node.id);
|
|
16462
16287
|
li.setAttribute('aria-level', node.level.toString());
|
|
16463
16288
|
li.setAttribute('role', 'treeitem');
|
|
16464
16289
|
li.setAttribute('tabindex', '-1');
|
|
16465
|
-
// Full-row overlay (required for fullRowSelect mode)
|
|
16466
16290
|
if (this.fullRowSelect) {
|
|
16467
16291
|
const fullRow = this.createElement('div', { className: FULLROW });
|
|
16468
16292
|
li.appendChild(fullRow);
|
|
16469
16293
|
}
|
|
16470
|
-
// Text/icon wrapper
|
|
16471
16294
|
const textWrap = this.createElement('div', { className: TEXTWRAP });
|
|
16472
|
-
// Expand/collapse icon
|
|
16473
16295
|
if (node.hasChildren) {
|
|
16474
16296
|
const iconSpan = this.createElement('div', {
|
|
16475
16297
|
className: ICON + ' ' + (node.expanded ? COLLAPSIBLE : EXPANDABLE)
|
|
16476
16298
|
});
|
|
16477
16299
|
textWrap.appendChild(iconSpan);
|
|
16478
16300
|
}
|
|
16479
|
-
// Checkbox
|
|
16480
16301
|
if (this.showCheckBox) {
|
|
16481
16302
|
const checkWrap = createCheckBox(this.createElement, false, {
|
|
16482
16303
|
checked: node.isChecked === true,
|
|
@@ -16490,28 +16311,24 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16490
16311
|
}
|
|
16491
16312
|
textWrap.appendChild(checkWrap);
|
|
16492
16313
|
}
|
|
16493
|
-
// Icon
|
|
16494
16314
|
if (node.iconCss) {
|
|
16495
16315
|
const iconEl = this.createElement('span', {
|
|
16496
16316
|
className: ICON + ' ' + node.iconCss
|
|
16497
16317
|
});
|
|
16498
16318
|
textWrap.appendChild(iconEl);
|
|
16499
16319
|
}
|
|
16500
|
-
// Image
|
|
16501
16320
|
if (node.imageUrl) {
|
|
16502
16321
|
const img = document.createElement('img');
|
|
16503
16322
|
img.src = node.imageUrl;
|
|
16504
16323
|
img.classList.add(IMAGE);
|
|
16505
16324
|
textWrap.appendChild(img);
|
|
16506
16325
|
}
|
|
16507
|
-
// Text anchor
|
|
16508
16326
|
const anchor = this.createElement('span', {
|
|
16509
16327
|
className: 'e-list-text',
|
|
16510
16328
|
innerHTML: node.text
|
|
16511
16329
|
});
|
|
16512
16330
|
textWrap.appendChild(anchor);
|
|
16513
16331
|
li.appendChild(textWrap);
|
|
16514
|
-
// nodeTemplate
|
|
16515
16332
|
if (!isNullOrUndefined(this.nodeTemplateFn)) {
|
|
16516
16333
|
const rawData = this.findNodeInTreeData(node.id);
|
|
16517
16334
|
if (!isNullOrUndefined(rawData)) {
|
|
@@ -16519,7 +16336,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16519
16336
|
this.renderNodeTemplate(rawData, anchor, node.id);
|
|
16520
16337
|
}
|
|
16521
16338
|
}
|
|
16522
|
-
// State classes
|
|
16523
16339
|
if (node.expanded) {
|
|
16524
16340
|
li.classList.add(EXPANDED);
|
|
16525
16341
|
li.setAttribute('aria-expanded', 'true');
|
|
@@ -16535,80 +16351,43 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16535
16351
|
if (node.hasChildren) {
|
|
16536
16352
|
li.classList.add(CHILD);
|
|
16537
16353
|
}
|
|
16538
|
-
// Wire the focus event so `focusIn()` fires when the user Tabs into this
|
|
16539
|
-
// item. The non-virtualized path registers this in `beforeNodeCreate()`,
|
|
16540
|
-
// but virtual items never go through that callback, so we must register
|
|
16541
|
-
// it here to keep focus styling (e.g. `e-focused`) consistent between
|
|
16542
|
-
// virtual and non-virtual modes. `unWireEvents()` already removes this
|
|
16543
|
-
// handler during teardown by iterating over the rendered `<li>`s.
|
|
16544
16354
|
EventHandler.add(li, 'focus', this.focusIn, this);
|
|
16545
16355
|
return li;
|
|
16546
16356
|
}
|
|
16547
|
-
/**
|
|
16548
|
-
* Initialises the virtual-scrolling layout:
|
|
16549
|
-
* 1. Adds the `e-virtualization` CSS class and applies a fixed `height` + `overflow-y`.
|
|
16550
|
-
* 2. Flattens all visible nodes into `virtualFlatNodes`.
|
|
16551
|
-
* 3. Measures item height and viewport height.
|
|
16552
|
-
* 4. Computes the initial window and renders it.
|
|
16553
|
-
* 5. Wires the scroll event handler.
|
|
16554
|
-
*
|
|
16555
|
-
* @returns {void}
|
|
16556
|
-
* @private
|
|
16557
|
-
*/
|
|
16558
16357
|
initVirtualization() {
|
|
16559
16358
|
addClass([this.element], VIRTUALIZATION);
|
|
16560
|
-
// Apply container height
|
|
16561
16359
|
const px = this.parseHeightToPixels();
|
|
16562
16360
|
if (px > 0) {
|
|
16563
16361
|
this.element.style.height = px + 'px';
|
|
16564
16362
|
this.element.style.overflowY = 'auto';
|
|
16565
16363
|
}
|
|
16566
|
-
// Ensure the inner UL is ready
|
|
16567
16364
|
if (!this.ulElement) {
|
|
16568
16365
|
this.ulElement = select('.' + PARENTITEM, this.element);
|
|
16569
16366
|
}
|
|
16570
16367
|
if (!this.ulElement) {
|
|
16571
16368
|
return;
|
|
16572
16369
|
}
|
|
16573
|
-
// Make the UL position-relative for the absolute spacers
|
|
16574
16370
|
this.ulElement.style.position = 'relative';
|
|
16575
|
-
// Rebuild flat list
|
|
16576
16371
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
16577
16372
|
this.rebuildVirtualNodeMap();
|
|
16578
|
-
// Measure dimensions
|
|
16579
16373
|
this.virtualViewportHeight = px > 0 ? px : this.element.offsetHeight;
|
|
16580
|
-
// Render a single probe item to measure the real item height before the full render
|
|
16581
16374
|
if (this.virtualFlatNodes.length > 0) {
|
|
16582
16375
|
const probe = this.createVirtualListItem(this.virtualFlatNodes[0], true);
|
|
16583
16376
|
this.ulElement.appendChild(probe);
|
|
16584
16377
|
this.virtualItemHeight = probe.offsetHeight > 0 ? probe.offsetHeight : this.virtualItemHeight;
|
|
16585
16378
|
this.ulElement.removeChild(probe);
|
|
16586
16379
|
}
|
|
16587
|
-
// Compute window and render all items in the initial viewport
|
|
16588
16380
|
const win = this.getVirtualWindow();
|
|
16589
16381
|
this.virtualStartIndex = win.startIndex;
|
|
16590
16382
|
this.virtualEndIndex = win.endIndex;
|
|
16591
16383
|
this.renderVirtualItems();
|
|
16592
|
-
// Wire scroll
|
|
16593
16384
|
this.wireVirtualScrollEvents();
|
|
16594
16385
|
}
|
|
16595
|
-
/**
|
|
16596
|
-
* Wires the scroll event listener on the TreeView element.
|
|
16597
|
-
*
|
|
16598
|
-
* @returns {void}
|
|
16599
|
-
* @private
|
|
16600
|
-
*/
|
|
16601
16386
|
wireVirtualScrollEvents() {
|
|
16602
16387
|
this.unwireVirtualScrollEvents();
|
|
16603
16388
|
this.virtualScrollBound = this.onVirtualScroll.bind(this);
|
|
16604
16389
|
this.element.addEventListener('scroll', this.virtualScrollBound, { passive: true });
|
|
16605
16390
|
}
|
|
16606
|
-
/**
|
|
16607
|
-
* Removes the scroll event listener previously wired by `wireVirtualScrollEvents`.
|
|
16608
|
-
*
|
|
16609
|
-
* @returns {void}
|
|
16610
|
-
* @private
|
|
16611
|
-
*/
|
|
16612
16391
|
unwireVirtualScrollEvents() {
|
|
16613
16392
|
if (this.virtualScrollBound) {
|
|
16614
16393
|
this.element.removeEventListener('scroll', this.virtualScrollBound);
|
|
@@ -16619,28 +16398,12 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16619
16398
|
this.virtualScrollRAF = null;
|
|
16620
16399
|
}
|
|
16621
16400
|
}
|
|
16622
|
-
/**
|
|
16623
|
-
* Scroll handler for the virtual TreeView. Throttled via `requestAnimationFrame`.
|
|
16624
|
-
* Shows the loading mask during fast scrolls, re-renders the window, then hides the mask.
|
|
16625
|
-
*
|
|
16626
|
-
* @returns {void}
|
|
16627
|
-
* @private
|
|
16628
|
-
*/
|
|
16629
16401
|
onVirtualScroll() {
|
|
16630
16402
|
if (this.virtualScrollRAF !== null) {
|
|
16631
16403
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
16632
16404
|
}
|
|
16633
|
-
// ── Tab-induced focus guard ────────────────────────────────────────────────
|
|
16634
|
-
// When the user Tabs into a `<li tabindex="0">` that is in the middle of
|
|
16635
|
-
// the treeview, the browser auto-scrolls the container so the focused
|
|
16636
|
-
// element becomes visible. That fires a `scroll` event here, but the
|
|
16637
|
-
// virtual window already contains the focused element — re-rendering
|
|
16638
|
-
// would destroy the focused `<li>` and break focus. Detect this case
|
|
16639
|
-
// via the flag set in `keyDownHandler` and skip the re-render entirely.
|
|
16640
16405
|
if (this.virtualSkipNextScrollRender) {
|
|
16641
16406
|
this.virtualSkipNextScrollRender = false;
|
|
16642
|
-
// Update prev scrollTop so a subsequent user-driven scroll still
|
|
16643
|
-
// calculates the correct delta.
|
|
16644
16407
|
this.virtualPrevScrollTop = this.element.scrollTop;
|
|
16645
16408
|
return;
|
|
16646
16409
|
}
|
|
@@ -16657,30 +16420,16 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16657
16420
|
this.virtualStartIndex = win.startIndex;
|
|
16658
16421
|
this.virtualEndIndex = win.endIndex;
|
|
16659
16422
|
this.renderVirtualItems();
|
|
16660
|
-
// Hide the skeleton mask after a short delay so it stays visible long enough
|
|
16661
|
-
// for the user to perceive the loading indicator even on short scrolls.
|
|
16662
16423
|
setTimeout(() => {
|
|
16663
16424
|
this.hideVirtualMask();
|
|
16664
|
-
// Keep the (now hidden) mask's coords fresh for the next show.
|
|
16665
16425
|
this.updateMaskPosition();
|
|
16666
16426
|
}, 200);
|
|
16667
16427
|
});
|
|
16668
16428
|
}
|
|
16669
|
-
/**
|
|
16670
|
-
* Creates the skeleton mask overlay used to indicate loading during fast scrolls.
|
|
16671
|
-
* Skeleton instances are stored in `skeletonInstances` for later disposal.
|
|
16672
|
-
*
|
|
16673
|
-
* @returns {void}
|
|
16674
|
-
* @private
|
|
16675
|
-
*/
|
|
16676
16429
|
createVirtualMask() {
|
|
16677
16430
|
if (this.virtualMaskElement) {
|
|
16678
16431
|
return;
|
|
16679
16432
|
}
|
|
16680
|
-
// Use position:fixed so the mask is anchored to the visible viewport (not to the
|
|
16681
|
-
// scrolling container). Width/height/left/top are set/refreshed by
|
|
16682
|
-
// updateMaskPosition() so the mask always covers the TreeView's visible area
|
|
16683
|
-
// regardless of how the page itself is scrolled.
|
|
16684
16433
|
const rect = this.element.getBoundingClientRect();
|
|
16685
16434
|
const scrollBarWidth = this.element.offsetWidth - this.element.clientWidth;
|
|
16686
16435
|
const maskParent = this.createElement('div', {
|
|
@@ -16693,20 +16442,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16693
16442
|
className: VIRTUAL_MASK_ITEM,
|
|
16694
16443
|
styles: `display:flex;align-items:center;height:${this.virtualItemHeight}px;padding:0 12px;gap:8px;`
|
|
16695
16444
|
});
|
|
16696
|
-
// Skeleton circle for icon placeholder
|
|
16697
|
-
// const iconHolder: HTMLElement = this.createElement('span', {
|
|
16698
|
-
// styles: `width:20px;height:20px;`
|
|
16699
|
-
// });
|
|
16700
|
-
// maskItem.appendChild(iconHolder);
|
|
16701
|
-
// const iconSkel: Skeleton = new Skeleton({
|
|
16702
|
-
// shape: 'Circle',
|
|
16703
|
-
// width: '20px',
|
|
16704
|
-
// height: '20px',
|
|
16705
|
-
// cssClass: 'e-virtual-skel-icon'
|
|
16706
|
-
// });
|
|
16707
|
-
// iconSkel.appendTo(iconHolder);
|
|
16708
|
-
// this.skeletonInstances.push(iconSkel);
|
|
16709
|
-
// Skeleton text bar
|
|
16710
16445
|
const textHolder = this.createElement('span');
|
|
16711
16446
|
textHolder.style.flex = '1';
|
|
16712
16447
|
textHolder.style.height = '14px';
|
|
@@ -16724,14 +16459,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16724
16459
|
this.virtualMaskElement = maskParent;
|
|
16725
16460
|
this.element.appendChild(maskParent);
|
|
16726
16461
|
}
|
|
16727
|
-
/**
|
|
16728
|
-
* Refreshes the mask position so it always overlays the TreeView's viewport.
|
|
16729
|
-
* Called on every show / scroll, since `position:fixed` is relative to the
|
|
16730
|
-
* page viewport, not to the TreeView scroll container.
|
|
16731
|
-
*
|
|
16732
|
-
* @returns {void}
|
|
16733
|
-
* @private
|
|
16734
|
-
*/
|
|
16735
16462
|
updateMaskPosition() {
|
|
16736
16463
|
if (!this.virtualMaskElement) {
|
|
16737
16464
|
return;
|
|
@@ -16743,12 +16470,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16743
16470
|
this.virtualMaskElement.style.width = (rect.width - scrollBarWidth) + 'px';
|
|
16744
16471
|
this.virtualMaskElement.style.height = rect.height + 'px';
|
|
16745
16472
|
}
|
|
16746
|
-
/**
|
|
16747
|
-
* Destroys all skeleton instances and removes the mask overlay from the DOM.
|
|
16748
|
-
*
|
|
16749
|
-
* @returns {void}
|
|
16750
|
-
* @private
|
|
16751
|
-
*/
|
|
16752
16473
|
destroyVirtualMask() {
|
|
16753
16474
|
for (let i = this.skeletonInstances.length - 1; i >= 0; i--) {
|
|
16754
16475
|
this.skeletonInstances[parseInt(i.toString(), 10)].destroy();
|
|
@@ -16761,52 +16482,26 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16761
16482
|
this.virtualMaskElement = null;
|
|
16762
16483
|
}
|
|
16763
16484
|
}
|
|
16764
|
-
/**
|
|
16765
|
-
* Positions and shows the skeleton mask at the current `scrollTop` position.
|
|
16766
|
-
*
|
|
16767
|
-
* @param {number} _scrollTop - Current scrollTop of the container.
|
|
16768
|
-
* @returns {void}
|
|
16769
|
-
* @private
|
|
16770
|
-
*/
|
|
16771
16485
|
showVirtualMask(_scrollTop) {
|
|
16772
16486
|
if (!this.virtualMaskElement) {
|
|
16773
16487
|
this.createVirtualMask();
|
|
16774
16488
|
}
|
|
16775
16489
|
if (this.virtualMaskElement) {
|
|
16776
|
-
// Refresh the fixed-position coords in case the TreeView was moved/resized
|
|
16777
|
-
// since the last time the mask was shown.
|
|
16778
16490
|
this.updateMaskPosition();
|
|
16779
16491
|
this.virtualMaskElement.style.display = 'block';
|
|
16780
|
-
// Visually hide the real list while the skeleton is shown
|
|
16781
16492
|
if (this.ulElement) {
|
|
16782
16493
|
this.ulElement.style.visibility = 'hidden';
|
|
16783
16494
|
}
|
|
16784
16495
|
}
|
|
16785
16496
|
}
|
|
16786
|
-
/**
|
|
16787
|
-
* Hides the skeleton mask overlay after the virtual window has been rendered.
|
|
16788
|
-
*
|
|
16789
|
-
* @returns {void}
|
|
16790
|
-
* @private
|
|
16791
|
-
*/
|
|
16792
16497
|
hideVirtualMask() {
|
|
16793
16498
|
if (this.virtualMaskElement) {
|
|
16794
16499
|
this.virtualMaskElement.style.display = 'none';
|
|
16795
|
-
// Restore real list visibility
|
|
16796
16500
|
if (this.ulElement) {
|
|
16797
16501
|
this.ulElement.style.visibility = '';
|
|
16798
16502
|
}
|
|
16799
16503
|
}
|
|
16800
16504
|
}
|
|
16801
|
-
/**
|
|
16802
|
-
* Ensures the node identified by `id` is scrolled into view inside the virtual container.
|
|
16803
|
-
* If the node falls outside the rendered window, the container's `scrollTop` is adjusted so
|
|
16804
|
-
* that the node becomes visible, the window is re-computed and `renderVirtualItems` is called.
|
|
16805
|
-
*
|
|
16806
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
16807
|
-
* @returns {void}
|
|
16808
|
-
* @private
|
|
16809
|
-
*/
|
|
16810
16505
|
ensureVirtualNodeVisible(id) {
|
|
16811
16506
|
const idx = this.virtualNodeMap.get(id);
|
|
16812
16507
|
if (isNullOrUndefined(idx)) {
|
|
@@ -16825,25 +16520,13 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16825
16520
|
this.element.scrollTop = nodeBottom - vpH;
|
|
16826
16521
|
}
|
|
16827
16522
|
else {
|
|
16828
|
-
return;
|
|
16523
|
+
return;
|
|
16829
16524
|
}
|
|
16830
|
-
// Re-render the new window synchronously so the <li> is in the DOM
|
|
16831
16525
|
const win = this.getVirtualWindow();
|
|
16832
16526
|
this.virtualStartIndex = win.startIndex;
|
|
16833
16527
|
this.virtualEndIndex = win.endIndex;
|
|
16834
16528
|
this.renderVirtualItems();
|
|
16835
16529
|
}
|
|
16836
|
-
/**
|
|
16837
|
-
* Recursively walks the treeData (and any nested `childFieldName` arrays) to
|
|
16838
|
-
* find the node whose `idFieldName` matches the given `id` (compared as
|
|
16839
|
-
* strings). Used by the virtual-mode expand/collapse handlers so they can
|
|
16840
|
-
* locate nodes at any depth — not just the root level — before writing the
|
|
16841
|
-
* `expanded` flag back to the datasource.
|
|
16842
|
-
*
|
|
16843
|
-
* @param {string} id - The id (as a string) of the node to locate.
|
|
16844
|
-
* @returns {Object | null} The matching raw datasource node, or null if not found.
|
|
16845
|
-
* @private
|
|
16846
|
-
*/
|
|
16847
16530
|
findNodeInTreeData(id) {
|
|
16848
16531
|
if (!this.treeData) {
|
|
16849
16532
|
return null;
|
|
@@ -16871,12 +16554,18 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16871
16554
|
};
|
|
16872
16555
|
return findRecursive(this.treeData);
|
|
16873
16556
|
}
|
|
16557
|
+
getNodeLevel(id) {
|
|
16558
|
+
if (!this.virtualFlatNodes) {
|
|
16559
|
+
return 1;
|
|
16560
|
+
}
|
|
16561
|
+
for (let i = 0; i < this.virtualFlatNodes.length; i++) {
|
|
16562
|
+
if (this.virtualFlatNodes[parseInt(i.toString(), 10)].id === id) {
|
|
16563
|
+
return this.virtualFlatNodes[parseInt(i.toString(), 10)].level;
|
|
16564
|
+
}
|
|
16565
|
+
}
|
|
16566
|
+
return 1;
|
|
16567
|
+
}
|
|
16874
16568
|
handleVirtualExpand(id, isKeyboardNav) {
|
|
16875
|
-
// ── Cancel any pending scroll RAF ─────────────────────────────────────────
|
|
16876
|
-
// A scroll RAF queued before the expand click would run asynchronously after
|
|
16877
|
-
// this synchronous expand completes, re-rendering the window from a stale
|
|
16878
|
-
// scrollTop and moving the parent node to the wrong position. Cancel it now
|
|
16879
|
-
// so the expand render is authoritative.
|
|
16880
16569
|
if (this.virtualScrollRAF !== null) {
|
|
16881
16570
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
16882
16571
|
this.virtualScrollRAF = null;
|
|
@@ -16884,7 +16573,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16884
16573
|
if (this.expandedNodes.indexOf(id) === -1) {
|
|
16885
16574
|
this.expandedNodes.push(id);
|
|
16886
16575
|
}
|
|
16887
|
-
// Also update virtualExpandedNodesSet to keep it in sync
|
|
16888
16576
|
if (this.virtualExpandedNodesSet) {
|
|
16889
16577
|
this.virtualExpandedNodesSet.add(id);
|
|
16890
16578
|
}
|
|
@@ -16894,56 +16582,75 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16894
16582
|
setValue(this.fields.expanded, true, acutalRootData);
|
|
16895
16583
|
}
|
|
16896
16584
|
}
|
|
16897
|
-
// ── Fix: Pin focused ID to the expanding node for keyboard navigation ──
|
|
16898
|
-
// When keyboard-triggered, the expanded node IS definitively the focused node
|
|
16899
|
-
// (`id` comes from currLi.getAttribute('data-uid') in expandNode, where currLi
|
|
16900
|
-
// is getFocusedNode()). Set unconditionally — the old conditional guard
|
|
16901
|
-
// `!this.virtualFocusedId` could leave a stale or null value if a previous
|
|
16902
|
-
// RAF render (from arrow-key scroll) hadn't yet completed, causing branch 3
|
|
16903
|
-
// (first item) to fire in renderVirtualItems() instead of branch 2.
|
|
16904
|
-
// For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
|
|
16905
|
-
// renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
|
|
16906
|
-
// mouse-based expand/collapse should never add the e-focused class.
|
|
16907
16585
|
if (isKeyboardNav) {
|
|
16908
16586
|
this.virtualFocusedId = id;
|
|
16909
16587
|
}
|
|
16910
16588
|
else {
|
|
16911
16589
|
this.virtualFocusedId = null;
|
|
16912
16590
|
}
|
|
16591
|
+
if (this.fields.dataSource instanceof DataManager && !this.isOffline) {
|
|
16592
|
+
const parentNode = this.findNodeInTreeData(id);
|
|
16593
|
+
if (parentNode && !isNullOrUndefined(parentNode)) {
|
|
16594
|
+
const childFieldName = this.fields.child.toString();
|
|
16595
|
+
const childrenObj = getValue(childFieldName, parentNode);
|
|
16596
|
+
const childrenNotLoaded = isNullOrUndefined(childrenObj) || !Array.isArray(childrenObj) ||
|
|
16597
|
+
childrenObj.length === 0;
|
|
16598
|
+
if (childrenNotLoaded) {
|
|
16599
|
+
this.loadVirtualChildData(id, parentNode, () => {
|
|
16600
|
+
this.completeVirtualExpand(id, isKeyboardNav);
|
|
16601
|
+
});
|
|
16602
|
+
return;
|
|
16603
|
+
}
|
|
16604
|
+
}
|
|
16605
|
+
}
|
|
16606
|
+
this.completeVirtualExpand(id, isKeyboardNav);
|
|
16607
|
+
}
|
|
16608
|
+
loadVirtualChildData(parentId, parentNode, callback) {
|
|
16609
|
+
const level = this.getNodeLevel(parentId);
|
|
16610
|
+
const mapper = this.getChildFields(this.fields, level + 1, 1);
|
|
16611
|
+
if (isNullOrUndefined(mapper) || isNullOrUndefined(mapper.dataSource)) {
|
|
16612
|
+
if (callback) {
|
|
16613
|
+
callback();
|
|
16614
|
+
}
|
|
16615
|
+
return;
|
|
16616
|
+
}
|
|
16617
|
+
mapper.dataSource.executeQuery(this.getQuery(mapper, parentId)).then((e) => {
|
|
16618
|
+
const childItems = e.result;
|
|
16619
|
+
if (!isNullOrUndefined(childItems) && childItems.length > 0) {
|
|
16620
|
+
const childFieldName = this.fields.child.toString();
|
|
16621
|
+
setValue(childFieldName, childItems, parentNode);
|
|
16622
|
+
if (this.dataType === 2) {
|
|
16623
|
+
const nodeInTree = this.findNodeInTreeData(parentId);
|
|
16624
|
+
if (nodeInTree) {
|
|
16625
|
+
setValue(childFieldName, childItems, nodeInTree);
|
|
16626
|
+
}
|
|
16627
|
+
}
|
|
16628
|
+
}
|
|
16629
|
+
if (callback) {
|
|
16630
|
+
callback();
|
|
16631
|
+
}
|
|
16632
|
+
}).catch((e) => {
|
|
16633
|
+
this.trigger('actionFailure', { error: e });
|
|
16634
|
+
if (callback) {
|
|
16635
|
+
callback();
|
|
16636
|
+
}
|
|
16637
|
+
});
|
|
16638
|
+
}
|
|
16639
|
+
completeVirtualExpand(id, isKeyboardNav) {
|
|
16913
16640
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
16914
16641
|
this.rebuildVirtualNodeMap();
|
|
16915
|
-
// ── Snapshot scrollTop before computing the window ────────────────────────
|
|
16916
|
-
// Read scrollTop once here and pass it into getVirtualWindow so that any
|
|
16917
|
-
// browser scroll-anchoring adjustment that happens when the padding values
|
|
16918
|
-
// change cannot affect which window we render.
|
|
16919
16642
|
const scrollTopSnapshot = this.element.scrollTop;
|
|
16920
16643
|
const win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
16921
16644
|
this.virtualStartIndex = win.startIndex;
|
|
16922
16645
|
this.virtualEndIndex = win.endIndex;
|
|
16923
|
-
// ── Block ALL scroll-triggered re-renders during this expand ─────────────
|
|
16924
|
-
// renderVirtualItems() calls li.focus() in branch 2 (keyboard focus restore).
|
|
16925
|
-
// The post-render focus() call below can also trigger browser scroll. Both
|
|
16926
|
-
// can fire the 'scroll' event and cause onVirtualScroll() to queue a RAF.
|
|
16927
|
-
// We set the skip flag ONCE here; it is consumed by the first scroll event.
|
|
16928
|
-
// To cover the second potential scroll (from the focus() call below) we use
|
|
16929
|
-
// preventScroll:true on that call so it never moves the container at all.
|
|
16930
16646
|
this.virtualSkipNextScrollRender = true;
|
|
16931
16647
|
this.renderVirtualItems();
|
|
16932
|
-
// renderVirtualItems() replaces all <li> elements so the old icon element that
|
|
16933
|
-
// had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
|
|
16934
|
-
// so the node can be collapsed/expanded again without being blocked.
|
|
16935
16648
|
const newLi = this.getElement(id);
|
|
16936
16649
|
if (newLi) {
|
|
16937
16650
|
const icon = select('.' + ICON, newLi);
|
|
16938
16651
|
if (icon) {
|
|
16939
16652
|
removeClass([icon], PROCESS);
|
|
16940
16653
|
}
|
|
16941
|
-
// ── Use preventScroll:true ────────────────────────────────────────────
|
|
16942
|
-
// Without preventScroll the browser scrolls the container to bring this
|
|
16943
|
-
// node into view, which fires a second 'scroll' event. The first scroll
|
|
16944
|
-
// was already consumed by virtualSkipNextScrollRender (set above), so the
|
|
16945
|
-
// second scroll would queue a new RAF that re-renders from the
|
|
16946
|
-
// focus-adjusted scrollTop — moving the parent node to the wrong position.
|
|
16947
16654
|
newLi.focus({ preventScroll: true });
|
|
16948
16655
|
}
|
|
16949
16656
|
if (this.isLoaded) {
|
|
@@ -16956,9 +16663,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16956
16663
|
}
|
|
16957
16664
|
}
|
|
16958
16665
|
handleVirtualCollapse(id, isKeyboardNav) {
|
|
16959
|
-
// ── Cancel any pending scroll RAF ─────────────────────────────────────────
|
|
16960
|
-
// Mirrors handleVirtualExpand: a queued RAF from a previous scroll event
|
|
16961
|
-
// must not override the collapse render with a stale window.
|
|
16962
16666
|
if (this.virtualScrollRAF !== null) {
|
|
16963
16667
|
cancelAnimationFrame(this.virtualScrollRAF);
|
|
16964
16668
|
this.virtualScrollRAF = null;
|
|
@@ -16967,7 +16671,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16967
16671
|
if (idx !== -1) {
|
|
16968
16672
|
this.expandedNodes.splice(idx, 1);
|
|
16969
16673
|
}
|
|
16970
|
-
// Also update virtualExpandedNodesSet to keep it in sync
|
|
16971
16674
|
if (this.virtualExpandedNodesSet) {
|
|
16972
16675
|
this.virtualExpandedNodesSet.delete(id);
|
|
16973
16676
|
}
|
|
@@ -16977,13 +16680,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16977
16680
|
setValue(this.fields.expanded, false, acutalRootData);
|
|
16978
16681
|
}
|
|
16979
16682
|
}
|
|
16980
|
-
// ── Fix: Pin focused ID to the collapsing node for keyboard navigation ──
|
|
16981
|
-
// Same reasoning as handleVirtualExpand: when keyboard-triggered, `id` is
|
|
16982
|
-
// definitively the focused node. Set unconditionally to avoid the stale/null
|
|
16983
|
-
// state that could cause the RAF re-render to fall through to branch 3.
|
|
16984
|
-
// For mouse clicks (isKeyboardNav=false), clear virtualFocusedId so that
|
|
16985
|
-
// renderVirtualItems() Branch 2 does NOT apply the FOCUS/FOCUSED classes —
|
|
16986
|
-
// mouse-based expand/collapse should never add the e-focused class.
|
|
16987
16683
|
if (isKeyboardNav) {
|
|
16988
16684
|
this.virtualFocusedId = id;
|
|
16989
16685
|
}
|
|
@@ -16992,28 +16688,18 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
16992
16688
|
}
|
|
16993
16689
|
this.virtualFlatNodes = this.flattenVisibleNodes();
|
|
16994
16690
|
this.rebuildVirtualNodeMap();
|
|
16995
|
-
// ── Snapshot scrollTop before computing the window ────────────────────────
|
|
16996
|
-
// Same rationale as handleVirtualExpand: capture scrollTop before any focus()
|
|
16997
|
-
// call or padding change can cause the browser to auto-scroll.
|
|
16998
16691
|
const scrollTopSnapshot = this.element.scrollTop;
|
|
16999
16692
|
const win = this.getVirtualWindowAt(scrollTopSnapshot);
|
|
17000
16693
|
this.virtualStartIndex = win.startIndex;
|
|
17001
16694
|
this.virtualEndIndex = win.endIndex;
|
|
17002
|
-
// ── Block ALL scroll-triggered re-renders during this collapse ────────────
|
|
17003
|
-
// Same as handleVirtualExpand: use preventScroll:true on the focus() call
|
|
17004
|
-
// below so the second potential scroll event is never fired.
|
|
17005
16695
|
this.virtualSkipNextScrollRender = true;
|
|
17006
16696
|
this.renderVirtualItems();
|
|
17007
|
-
// renderVirtualItems() replaces all <li> elements so the old icon element that
|
|
17008
|
-
// had the PROCESS class is gone. Remove PROCESS from the newly rendered icon
|
|
17009
|
-
// so the node can be expanded/collapsed again without being blocked.
|
|
17010
16697
|
const newLi = this.getElement(id);
|
|
17011
16698
|
if (newLi) {
|
|
17012
16699
|
const icon = select('.' + ICON, newLi);
|
|
17013
16700
|
if (icon) {
|
|
17014
16701
|
removeClass([icon], PROCESS);
|
|
17015
16702
|
}
|
|
17016
|
-
// ── Use preventScroll:true to avoid consuming virtualSkipNextScrollRender ─
|
|
17017
16703
|
newLi.focus({ preventScroll: true });
|
|
17018
16704
|
}
|
|
17019
16705
|
if (this.isLoaded) {
|
|
@@ -17025,14 +16711,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
17025
16711
|
}
|
|
17026
16712
|
}
|
|
17027
16713
|
}
|
|
17028
|
-
/**
|
|
17029
|
-
* Updates the selected state of a node in `virtualFlatNodes` and re-renders the window.
|
|
17030
|
-
*
|
|
17031
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
17032
|
-
* @param {boolean} selected - Whether the node is now selected.
|
|
17033
|
-
* @returns {void}
|
|
17034
|
-
* @private
|
|
17035
|
-
*/
|
|
17036
16714
|
updateVirtualNodeSelection(id, selected) {
|
|
17037
16715
|
const idx = this.virtualNodeMap.get(id);
|
|
17038
16716
|
if (!isNullOrUndefined(idx)) {
|
|
@@ -17040,21 +16718,11 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
17040
16718
|
this.renderVirtualItems();
|
|
17041
16719
|
}
|
|
17042
16720
|
}
|
|
17043
|
-
/**
|
|
17044
|
-
* Updates the checkbox state of a node in `virtualFlatNodes` and re-renders the window.
|
|
17045
|
-
*
|
|
17046
|
-
* @param {string} id - The `data-uid` of the target node.
|
|
17047
|
-
* @param {'check' | 'uncheck' | 'indeterminate'} state - The new check state.
|
|
17048
|
-
* @returns {void}
|
|
17049
|
-
* @private
|
|
17050
|
-
*/
|
|
17051
16721
|
updateVirtualNodeCheckState(id, state) {
|
|
17052
16722
|
const idx = this.virtualNodeMap.get(id);
|
|
17053
16723
|
if (!isNullOrUndefined(idx)) {
|
|
17054
16724
|
this.virtualFlatNodes[parseInt(idx.toString(), 10)].isChecked =
|
|
17055
16725
|
state === 'check' ? true : state === 'indeterminate' ? 'indeterminate' : false;
|
|
17056
|
-
// Keep the persistent indeterminate set in sync so flattenVisibleNodes()
|
|
17057
|
-
// correctly restores state after any full list rebuild (e.g. collapse).
|
|
17058
16726
|
if (state === 'indeterminate') {
|
|
17059
16727
|
this.virtualIndeterminateNodesSet.add(id);
|
|
17060
16728
|
}
|
|
@@ -17064,9 +16732,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
17064
16732
|
this.renderVirtualItems();
|
|
17065
16733
|
}
|
|
17066
16734
|
}
|
|
17067
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
17068
|
-
// END VIRTUALIZATION
|
|
17069
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
17070
16735
|
/**
|
|
17071
16736
|
* Called internally if any of the property value changed.
|
|
17072
16737
|
*
|
|
@@ -17210,7 +16875,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
17210
16875
|
}
|
|
17211
16876
|
}
|
|
17212
16877
|
else {
|
|
17213
|
-
// Tear down virtualization and do a full re-render
|
|
17214
16878
|
this.unwireVirtualScrollEvents();
|
|
17215
16879
|
this.destroyVirtualMask();
|
|
17216
16880
|
removeClass([this.element], VIRTUALIZATION);
|
|
@@ -17221,7 +16885,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
17221
16885
|
break;
|
|
17222
16886
|
case 'height':
|
|
17223
16887
|
if (this.enableVirtualization) {
|
|
17224
|
-
// Re-initialise so the new height takes effect
|
|
17225
16888
|
this.unwireVirtualScrollEvents();
|
|
17226
16889
|
this.initVirtualization();
|
|
17227
16890
|
}
|
|
@@ -17239,7 +16902,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
17239
16902
|
* @returns {void}
|
|
17240
16903
|
*/
|
|
17241
16904
|
destroy() {
|
|
17242
|
-
// Tear down virtualization before standard cleanup
|
|
17243
16905
|
if (this.enableVirtualization) {
|
|
17244
16906
|
this.unwireVirtualScrollEvents();
|
|
17245
16907
|
this.destroyVirtualMask();
|
|
@@ -17330,10 +16992,6 @@ let TreeView = TreeView_1 = class TreeView extends Component {
|
|
|
17330
16992
|
if (isNullOrUndefined(fNode)) {
|
|
17331
16993
|
this.setNodeFocusable();
|
|
17332
16994
|
}
|
|
17333
|
-
// In virtual mode (with a fixed height), rebuild the flat node list and re-render
|
|
17334
|
-
// the window so the newly added nodes appear in the correct hierarchical position.
|
|
17335
|
-
// When height is not provided, the tree uses normal rendering and the DOM is
|
|
17336
|
-
// already updated by addGivenNodes() above.
|
|
17337
16995
|
if (this.enableVirtualization && !isNullOrUndefined(this.height)) {
|
|
17338
16996
|
this.setRootData();
|
|
17339
16997
|
this.rebuildVirtualCheckCache();
|