@ixfx/components 0.13.6 → 0.13.8
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/bundle/index.d.ts +64 -10
- package/bundle/index.js +147 -116
- package/dist/crumbs.d.ts +1 -1
- package/dist/{data-grid-CGe1kRMI.d.ts → data-grid-D7RxYISt.d.ts} +2 -2
- package/dist/data-grid.d.ts +2 -2
- package/dist/{detail-list-BhuV3_jF.d.ts → detail-list-w32XZEgH.d.ts} +3 -3
- package/dist/detail-list.d.ts +3 -3
- package/dist/{flat-list-C7fmx7ii.d.ts → flat-list-CkRIZCuR.d.ts} +2 -2
- package/dist/flat-list.d.ts +1 -1
- package/dist/{grid-list-DqRuaIRk.d.ts → grid-list-DUvhSoT2.d.ts} +3 -3
- package/dist/grid-list.d.ts +2 -2
- package/dist/incr-search.d.ts +1 -1
- package/dist/{index-CzFRWPzh.d.ts → index-CO6m-YC0.d.ts} +1 -1
- package/dist/{index-AifRs3k9.d.ts → index-CSCrK0hR.d.ts} +22 -12
- package/dist/index.d.ts +13 -9
- package/dist/index.js +109 -29
- package/dist/{list-native-drag-controller-B0xS2Sj4.d.ts → list-native-drag-controller-CvXN3NB7.d.ts} +1 -1
- package/dist/miller.d.ts +36 -4
- package/dist/miller.js +183 -39
- package/dist/{tree-BjrfaX7d.js → tree-9Hs7VxuX.js} +63 -0
- package/dist/{tree-component-Bx40hxMz.d.ts → tree-component-Tslymyyl.d.ts} +9 -1
- package/dist/tree.d.ts +3 -3
- package/dist/tree.js +1 -1
- package/dist/{vertical-list-BohPAS04.d.ts → vertical-list-B1Fq06xr.d.ts} +2 -2
- package/dist/vertical-list.d.ts +2 -2
- package/docs-user/hyperbolic-tree.md +1 -1
- package/docs-user/index.json +2 -2
- package/docs-user/miller.md +21 -1
- package/docs-user/multicol-list.md +1 -1
- package/docs-user/tree.md +33 -0
- package/package.json +1 -1
package/dist/miller.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as TreeTickleEvent, D as TreeSelectDetail, E as TreeSearchHost, O as TreeSelectEvent, a as TreeComponent, b as TreeItemClickDetail, i as TreeCollapseEvent, j as TreeDataModel, k as TreeTickleDetail, n as TreeActivateEvent, o as TreeComponentEventMap, r as TreeCollapseDetail, t as TreeActivateDetail, v as TreeExpandDetail, w as TreeNavigateOptions, x as TreeItemClickEvent, y as TreeExpandEvent } from "./tree-component-Tslymyyl.js";
|
|
2
2
|
import { a as TreeItem, n as TreeExclusivity, o as TreeNode, r as TreeFilterPredicate, s as TreeSelectionFilter, t as LoadChildrenCallback } from "./types-DQ7KOUIj.js";
|
|
3
|
-
import { S as ListSearchController } from "./index-
|
|
3
|
+
import { S as ListSearchController } from "./index-CO6m-YC0.js";
|
|
4
4
|
import { n as TickledController } from "./tickled-controller-DodxD3eQ.js";
|
|
5
5
|
import { LitElement, PropertyValues, TemplateResult, nothing } from "lit";
|
|
6
6
|
import { CommandRegistry } from "@clinth/ui-commands";
|
|
@@ -218,7 +218,13 @@ declare class MillerBaseElement extends LitElement implements TreeComponent, Tre
|
|
|
218
218
|
* Programmatically navigate to a node by key path, relative to the current
|
|
219
219
|
* view root (rebased or full tree).
|
|
220
220
|
*/
|
|
221
|
-
navigateTo(keys: readonly string[]): boolean;
|
|
221
|
+
navigateTo(keys: readonly string[], options?: TreeNavigateOptions): boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Re-sync the element with children arrays patched in place (see
|
|
224
|
+
* `MillerTreeController.sync()`): columns whose selected node was removed are
|
|
225
|
+
* closed, selection entries for removed nodes are dropped, then it re-renders.
|
|
226
|
+
*/
|
|
227
|
+
invalidateStructure(): void;
|
|
222
228
|
/**
|
|
223
229
|
* Programmatically navigate to the first node (breadth-first) matching a predicate.
|
|
224
230
|
*/
|
|
@@ -352,6 +358,11 @@ type MillerTreeControllerOptions<T> = {
|
|
|
352
358
|
* `TraversableTree` node has any children.
|
|
353
359
|
*/
|
|
354
360
|
valueToItem: (value: T, node: TraversableTree<T>) => TreeItem;
|
|
361
|
+
/**
|
|
362
|
+
* Children loader installed on the element by `connect()`. Defaults to a
|
|
363
|
+
* synchronous read from the source tree. `sync()` never touches it.
|
|
364
|
+
*/
|
|
365
|
+
loadChildren?: LoadChildrenCallback;
|
|
355
366
|
};
|
|
356
367
|
/**
|
|
357
368
|
* Bridges an `ixfx` `TraversableTree<T>` with an `ixfx-miller-*` component.
|
|
@@ -399,7 +410,28 @@ declare class MillerTreeController<T> {
|
|
|
399
410
|
*
|
|
400
411
|
* Returns `false` if no matching node is found.
|
|
401
412
|
*/
|
|
402
|
-
navigateToValue(predicate: (value: T) => boolean): boolean;
|
|
413
|
+
navigateToValue(predicate: (value: T) => boolean, options?: TreeNavigateOptions): boolean;
|
|
414
|
+
/**
|
|
415
|
+
* Navigate using item keys **relative to the element's current view root**
|
|
416
|
+
* (the real root when not rebased). Ancestors with unloaded children are
|
|
417
|
+
* populated first. Returns `false` if a key is missing.
|
|
418
|
+
*/
|
|
419
|
+
navigateToKeyPath(keys: readonly string[], options?: TreeNavigateOptions): boolean;
|
|
420
|
+
/**
|
|
421
|
+
* Navigate to the source node with this identity. Returns `false` when it is
|
|
422
|
+
* unknown or lies outside a rebased view (keys are relative to the view root).
|
|
423
|
+
*/
|
|
424
|
+
navigateToIdentity(identity: unknown, options?: TreeNavigateOptions): boolean;
|
|
425
|
+
/** The source node whose item key is `key`, if it has been materialised. */
|
|
426
|
+
lookupByKey(key: string): TraversableTree<T> | undefined;
|
|
427
|
+
/**
|
|
428
|
+
* Re-derive children for every already-materialised node from the source tree
|
|
429
|
+
* and patch them in place. Children arrays are mutated (not replaced) so open
|
|
430
|
+
* columns keep their references, and `MillerNode`s are reused by key so
|
|
431
|
+
* selection survives. A rebased `viewRoot` is reset only if its node was
|
|
432
|
+
* removed. `loadChildren` is left alone. Refreshes the element once.
|
|
433
|
+
*/
|
|
434
|
+
sync(): void;
|
|
403
435
|
}
|
|
404
436
|
//#endregion
|
|
405
437
|
export { type TreeActivateDetail as MillerActivateDetail, type TreeActivateEvent as MillerActivateEvent, MillerBaseElement, MillerBaseElementEventMap, type TreeCollapseDetail as MillerCollapseDetail, type TreeCollapseEvent as MillerCollapseEvent, MillerConfig, type TreeExpandDetail as MillerExpandDetail, type TreeExpandEvent as MillerExpandEvent, MillerHeaderClickDetail, MillerHeaderClickEvent, MillerHeaderRightClickDetail, MillerHeaderRightClickEvent, type TreeItem as MillerItem, type TreeItemClickDetail as MillerItemClickDetail, type TreeItemClickEvent as MillerItemClickEvent, MillerItemDblClickDetail, MillerItemDblClickEvent, MillerItemRightClickDetail, MillerItemRightClickEvent, MillerItemState, MillerListElement, type TreeNode as MillerNode, MillerRebaseDetail, MillerRebaseEvent, type TreeSelectDetail as MillerSelectDetail, type TreeSelectEvent as MillerSelectEvent, MillerSelectionMode, type TreeTickleDetail as MillerTickleDetail, type TreeTickleEvent as MillerTickleEvent, MillerTreeController, MillerTreeControllerOptions };
|
package/dist/miller.js
CHANGED
|
@@ -39,6 +39,8 @@ let MillerBaseElement = class MillerBaseElement extends LitElement {
|
|
|
39
39
|
}
|
|
40
40
|
return items;
|
|
41
41
|
} });
|
|
42
|
+
this.#selectOrigin = `user`;
|
|
43
|
+
this.#selectSilent = false;
|
|
42
44
|
this.#onKeyDown = (event) => {
|
|
43
45
|
if (this.search.isOpen) return;
|
|
44
46
|
const currentDepth = this.#getTickledDepthOrDefault();
|
|
@@ -403,14 +405,47 @@ let MillerBaseElement = class MillerBaseElement extends LitElement {
|
|
|
403
405
|
* Programmatically navigate to a node by key path, relative to the current
|
|
404
406
|
* view root (rebased or full tree).
|
|
405
407
|
*/
|
|
406
|
-
navigateTo(keys) {
|
|
408
|
+
navigateTo(keys, options = {}) {
|
|
407
409
|
const display = this.#displayRoot;
|
|
408
410
|
if (!display?.children) return false;
|
|
411
|
+
const prevOrigin = this.#selectOrigin;
|
|
412
|
+
const prevSilent = this.#selectSilent;
|
|
413
|
+
this.#selectOrigin = `programmatic`;
|
|
414
|
+
this.#selectSilent = options.silent === true;
|
|
415
|
+
try {
|
|
416
|
+
return this.#navigateTo(display.children, keys, options.reveal ?? `edge`);
|
|
417
|
+
} finally {
|
|
418
|
+
this.#selectOrigin = prevOrigin;
|
|
419
|
+
this.#selectSilent = prevSilent;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Re-sync the element with children arrays patched in place (see
|
|
424
|
+
* `MillerTreeController.sync()`): columns whose selected node was removed are
|
|
425
|
+
* closed, selection entries for removed nodes are dropped, then it re-renders.
|
|
426
|
+
*/
|
|
427
|
+
invalidateStructure() {
|
|
428
|
+
const present = /* @__PURE__ */ new Set();
|
|
429
|
+
for (const col of this._columns) for (const n of col) present.add(n);
|
|
430
|
+
for (let depth = 0; depth < this._columns.length; depth++) {
|
|
431
|
+
const selected = this._selectedByDepth.get(depth);
|
|
432
|
+
if (selected && !this._columns[depth].includes(selected)) {
|
|
433
|
+
this._columns = this._columns.slice(0, depth + 1);
|
|
434
|
+
for (const d of [...this._selectedByDepth.keys()]) if (d >= depth) this._selectedByDepth.delete(d);
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
for (const n of [...this._selectedNodes]) if (!present.has(n)) this._selectedNodes.delete(n);
|
|
439
|
+
this.requestUpdate();
|
|
440
|
+
}
|
|
441
|
+
#selectOrigin;
|
|
442
|
+
#selectSilent;
|
|
443
|
+
#navigateTo(rootColumn, keys, reveal) {
|
|
409
444
|
this.#abortPending();
|
|
410
445
|
this._selectedByDepth.clear();
|
|
411
446
|
this.tickled.clearTickled();
|
|
412
|
-
const newColumns = [
|
|
413
|
-
let currentColumn =
|
|
447
|
+
const newColumns = [rootColumn];
|
|
448
|
+
let currentColumn = rootColumn;
|
|
414
449
|
let success = true;
|
|
415
450
|
let lastNode;
|
|
416
451
|
let lastDepth = 0;
|
|
@@ -438,7 +473,7 @@ let MillerBaseElement = class MillerBaseElement extends LitElement {
|
|
|
438
473
|
if (leafKey) this._selectedKeys = /* @__PURE__ */ new Set([leafKey]);
|
|
439
474
|
}
|
|
440
475
|
this.requestUpdate();
|
|
441
|
-
this.#scrollNavigatedItemIntoView();
|
|
476
|
+
this.#scrollNavigatedItemIntoView(reveal === `center-if-hidden` ? `auto` : `smooth`, reveal);
|
|
442
477
|
return success;
|
|
443
478
|
}
|
|
444
479
|
/**
|
|
@@ -718,9 +753,11 @@ let MillerBaseElement = class MillerBaseElement extends LitElement {
|
|
|
718
753
|
return true;
|
|
719
754
|
}
|
|
720
755
|
#dispatchSelectEvent(previous) {
|
|
756
|
+
if (this.#selectSilent) return;
|
|
721
757
|
const detail = {
|
|
722
758
|
selected: new Set(this._selectedNodes),
|
|
723
|
-
previous
|
|
759
|
+
previous,
|
|
760
|
+
origin: this.#selectOrigin
|
|
724
761
|
};
|
|
725
762
|
this.dispatchEvent(new CustomEvent(`select`, {
|
|
726
763
|
detail,
|
|
@@ -990,13 +1027,26 @@ let MillerBaseElement = class MillerBaseElement extends LitElement {
|
|
|
990
1027
|
index
|
|
991
1028
|
};
|
|
992
1029
|
}
|
|
993
|
-
#scrollNavigatedItemIntoView(behavior = `smooth`) {
|
|
1030
|
+
#scrollNavigatedItemIntoView(behavior = `smooth`, reveal = `edge`) {
|
|
994
1031
|
requestAnimationFrame(() => {
|
|
995
1032
|
const tickled = this.#getTickledNodeAndDepth();
|
|
996
1033
|
if (!tickled) return;
|
|
997
1034
|
const container = this.shadowRoot?.querySelectorAll(`.column`)[tickled.depth]?.querySelector(`.column-content`);
|
|
998
1035
|
if (!container) return;
|
|
999
|
-
container.querySelectorAll(`.item`)[tickled.index]
|
|
1036
|
+
const item = container.querySelectorAll(`.item`)[tickled.index];
|
|
1037
|
+
if (!item) return;
|
|
1038
|
+
if (reveal === `center-if-hidden`) {
|
|
1039
|
+
const rect = item.getBoundingClientRect();
|
|
1040
|
+
const box = container.getBoundingClientRect();
|
|
1041
|
+
const hidden = rect.top < box.top || rect.bottom > box.bottom;
|
|
1042
|
+
item.scrollIntoView({
|
|
1043
|
+
block: hidden ? `center` : `nearest`,
|
|
1044
|
+
inline: `nearest`,
|
|
1045
|
+
behavior
|
|
1046
|
+
});
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
item.scrollIntoView({
|
|
1000
1050
|
block: `nearest`,
|
|
1001
1051
|
behavior
|
|
1002
1052
|
});
|
|
@@ -1562,6 +1612,10 @@ var MillerTreeController = class {
|
|
|
1562
1612
|
#options;
|
|
1563
1613
|
#nodeToTree = /* @__PURE__ */ new WeakMap();
|
|
1564
1614
|
#identityToMillerNode = /* @__PURE__ */ new Map();
|
|
1615
|
+
#keyToTreeNode = /* @__PURE__ */ new Map();
|
|
1616
|
+
#identityToTree = /* @__PURE__ */ new Map();
|
|
1617
|
+
/** `valueToItem` results per source node identity; replaced wholesale by `sync()`. */
|
|
1618
|
+
#itemCache = /* @__PURE__ */ new WeakMap();
|
|
1565
1619
|
#connectSignal;
|
|
1566
1620
|
constructor(root, element, options) {
|
|
1567
1621
|
this.#root = root;
|
|
@@ -1575,18 +1629,19 @@ var MillerTreeController = class {
|
|
|
1575
1629
|
connect() {
|
|
1576
1630
|
this.disconnect();
|
|
1577
1631
|
this.#connectSignal = new AbortController();
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
this.#
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
this.#element.
|
|
1632
|
+
this.#itemCache = /* @__PURE__ */ new WeakMap();
|
|
1633
|
+
this.#identityToMillerNode.clear();
|
|
1634
|
+
this.#identityToTree.clear();
|
|
1635
|
+
this.#keyToTreeNode.clear();
|
|
1636
|
+
const rootNode = { item: this.#itemOf(this.#root) };
|
|
1637
|
+
this.#nodeToTree.set(rootNode, this.#root);
|
|
1638
|
+
rootNode.children = this.#childrenOf(this.#root);
|
|
1639
|
+
this.#element.root = rootNode;
|
|
1640
|
+
this.#element.loadChildren = this.#options.loadChildren ?? (({ node }) => {
|
|
1586
1641
|
const treeNode = this.#nodeToTree.get(node);
|
|
1587
1642
|
if (!treeNode) return [];
|
|
1588
1643
|
return this.#childrenOf(treeNode);
|
|
1589
|
-
};
|
|
1644
|
+
});
|
|
1590
1645
|
}
|
|
1591
1646
|
/** Remove all event listeners and release internal state. */
|
|
1592
1647
|
disconnect() {
|
|
@@ -1606,46 +1661,110 @@ var MillerTreeController = class {
|
|
|
1606
1661
|
*
|
|
1607
1662
|
* Returns `false` if no matching node is found.
|
|
1608
1663
|
*/
|
|
1609
|
-
navigateToValue(predicate) {
|
|
1664
|
+
navigateToValue(predicate, options) {
|
|
1610
1665
|
const treePath = this.#findTreePath(this.#root, predicate);
|
|
1611
1666
|
if (!treePath) return false;
|
|
1667
|
+
return this.#navigateToPath(treePath, options);
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Navigate using item keys **relative to the element's current view root**
|
|
1671
|
+
* (the real root when not rebased). Ancestors with unloaded children are
|
|
1672
|
+
* populated first. Returns `false` if a key is missing.
|
|
1673
|
+
*/
|
|
1674
|
+
navigateToKeyPath(keys, options) {
|
|
1675
|
+
const base = this.#element.viewRoot ?? this.#element.root;
|
|
1676
|
+
if (!base || keys.length === 0) return false;
|
|
1677
|
+
this.#ensureChildren(base);
|
|
1678
|
+
let column = base.children ?? [];
|
|
1679
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
1680
|
+
const node = column.find((n) => n.item.key === keys[i]);
|
|
1681
|
+
if (!node) return false;
|
|
1682
|
+
this.#ensureChildren(node);
|
|
1683
|
+
column = node.children ?? [];
|
|
1684
|
+
}
|
|
1685
|
+
return this.#element.navigateTo(keys, options);
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* Navigate to the source node with this identity. Returns `false` when it is
|
|
1689
|
+
* unknown or lies outside a rebased view (keys are relative to the view root).
|
|
1690
|
+
*/
|
|
1691
|
+
navigateToIdentity(identity, options) {
|
|
1692
|
+
const target = this.#treeNodeByIdentity(identity);
|
|
1693
|
+
if (!target) return false;
|
|
1694
|
+
const path = [];
|
|
1695
|
+
const rootId = this.#root.getIdentity();
|
|
1696
|
+
for (let n = target; n; n = n.getParent()) {
|
|
1697
|
+
path.unshift(n);
|
|
1698
|
+
if (n.getIdentity() === rootId) return this.#navigateToPath(path, options);
|
|
1699
|
+
}
|
|
1700
|
+
return false;
|
|
1701
|
+
}
|
|
1702
|
+
/** The source node whose item key is `key`, if it has been materialised. */
|
|
1703
|
+
lookupByKey(key) {
|
|
1704
|
+
return this.#keyToTreeNode.get(key);
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Re-derive children for every already-materialised node from the source tree
|
|
1708
|
+
* and patch them in place. Children arrays are mutated (not replaced) so open
|
|
1709
|
+
* columns keep their references, and `MillerNode`s are reused by key so
|
|
1710
|
+
* selection survives. A rebased `viewRoot` is reset only if its node was
|
|
1711
|
+
* removed. `loadChildren` is left alone. Refreshes the element once.
|
|
1712
|
+
*/
|
|
1713
|
+
sync() {
|
|
1714
|
+
const root = this.#element.root;
|
|
1715
|
+
if (!root) return;
|
|
1716
|
+
this.#itemCache = /* @__PURE__ */ new WeakMap();
|
|
1717
|
+
this.#identityToMillerNode.clear();
|
|
1718
|
+
this.#identityToTree.clear();
|
|
1719
|
+
this.#keyToTreeNode.clear();
|
|
1720
|
+
this.#nodeToTree.set(root, this.#root);
|
|
1721
|
+
root.item = this.#itemOf(this.#root);
|
|
1722
|
+
const kept = /* @__PURE__ */ new Set([root]);
|
|
1723
|
+
if (root.children) this.#syncChildren(this.#root, root.children, kept);
|
|
1724
|
+
const view = this.#element.viewRoot;
|
|
1725
|
+
if (view && !kept.has(view)) this.#element.viewRoot = void 0;
|
|
1726
|
+
this.#element.invalidateStructure();
|
|
1727
|
+
}
|
|
1728
|
+
#navigateToPath(treePath, options) {
|
|
1612
1729
|
const base = this.#element.viewRoot ?? this.#element.root;
|
|
1613
1730
|
if (!base) return false;
|
|
1614
1731
|
let startIndex = 1;
|
|
1615
1732
|
if (this.#element.viewRoot) {
|
|
1616
1733
|
const mappedBase = this.#nodeToTree.get(base);
|
|
1617
1734
|
if (!mappedBase) return false;
|
|
1618
|
-
const
|
|
1735
|
+
const baseId = mappedBase.getIdentity();
|
|
1736
|
+
const baseIndex = treePath.findIndex((n) => n.getIdentity() === baseId);
|
|
1619
1737
|
if (baseIndex < 0) return false;
|
|
1620
1738
|
startIndex = baseIndex + 1;
|
|
1621
|
-
if (base.children === void 0) base.children = this.#childrenOf(mappedBase);
|
|
1622
1739
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1740
|
+
if (startIndex >= treePath.length) return false;
|
|
1741
|
+
return this.navigateToKeyPath(treePath.slice(startIndex).map((n) => this.#itemOf(n).key), options);
|
|
1742
|
+
}
|
|
1743
|
+
#ensureChildren(node) {
|
|
1744
|
+
if (node.children !== void 0) return;
|
|
1745
|
+
const mapped = this.#nodeToTree.get(node);
|
|
1746
|
+
if (mapped) node.children = this.#childrenOf(mapped);
|
|
1747
|
+
}
|
|
1748
|
+
#treeNodeByIdentity(identity) {
|
|
1749
|
+
return this.#identityToTree.get(identity);
|
|
1750
|
+
}
|
|
1751
|
+
#itemOf(treeNode) {
|
|
1752
|
+
const id = treeNode.getIdentity();
|
|
1753
|
+
let item = this.#itemCache.get(id);
|
|
1754
|
+
if (!item) {
|
|
1755
|
+
item = this.#options.valueToItem(treeNode.getValue(), treeNode);
|
|
1756
|
+
this.#itemCache.set(id, item);
|
|
1638
1757
|
}
|
|
1639
|
-
return
|
|
1758
|
+
return item;
|
|
1640
1759
|
}
|
|
1641
1760
|
/** Convert a TraversableTree node's children to MillerNode[]. */
|
|
1642
1761
|
#childrenOf(treeNode) {
|
|
1643
1762
|
return Array.from(treeNode.children(), (child) => this.#toMillerNode(child));
|
|
1644
1763
|
}
|
|
1645
1764
|
#toMillerNode(treeNode) {
|
|
1646
|
-
const value = treeNode.getValue();
|
|
1647
1765
|
const childList = [...treeNode.children()];
|
|
1648
|
-
const
|
|
1766
|
+
for (const child of childList) this.#identityToTree.set(child.getIdentity(), child);
|
|
1767
|
+
const userItem = this.#itemOf(treeNode);
|
|
1649
1768
|
const isLeaf = userItem.isLeaf ?? childList.length === 0;
|
|
1650
1769
|
const millerNode = {
|
|
1651
1770
|
item: {
|
|
@@ -1654,10 +1773,35 @@ var MillerTreeController = class {
|
|
|
1654
1773
|
},
|
|
1655
1774
|
children: isLeaf ? [] : void 0
|
|
1656
1775
|
};
|
|
1657
|
-
this.#
|
|
1658
|
-
this.#identityToMillerNode.set(treeNode.getIdentity(), millerNode);
|
|
1776
|
+
this.#register(millerNode, treeNode);
|
|
1659
1777
|
return millerNode;
|
|
1660
1778
|
}
|
|
1779
|
+
#register(node, treeNode) {
|
|
1780
|
+
this.#nodeToTree.set(node, treeNode);
|
|
1781
|
+
this.#identityToMillerNode.set(treeNode.getIdentity(), node);
|
|
1782
|
+
this.#identityToTree.set(treeNode.getIdentity(), treeNode);
|
|
1783
|
+
this.#keyToTreeNode.set(node.item.key, treeNode);
|
|
1784
|
+
}
|
|
1785
|
+
/** Patch `existing` (in place) to match the source children of `treeNode`; recurse into loaded nodes. */
|
|
1786
|
+
#syncChildren(treeNode, existing, kept) {
|
|
1787
|
+
const byKey = new Map(existing.map((n) => [n.item.key, n]));
|
|
1788
|
+
const next = Array.from(treeNode.children(), (child) => {
|
|
1789
|
+
const fresh = this.#toMillerNode(child);
|
|
1790
|
+
const old = byKey.get(fresh.item.key);
|
|
1791
|
+
if (!old) {
|
|
1792
|
+
kept.add(fresh);
|
|
1793
|
+
return fresh;
|
|
1794
|
+
}
|
|
1795
|
+
old.item = fresh.item;
|
|
1796
|
+
this.#register(old, child);
|
|
1797
|
+
kept.add(old);
|
|
1798
|
+
if (fresh.item.isLeaf) old.children = [];
|
|
1799
|
+
else if (old.children !== void 0) this.#syncChildren(child, old.children, kept);
|
|
1800
|
+
else old.children = void 0;
|
|
1801
|
+
return old;
|
|
1802
|
+
});
|
|
1803
|
+
existing.splice(0, existing.length, ...next);
|
|
1804
|
+
}
|
|
1661
1805
|
/** DFS through the ixfx tree returning the path of nodes to the first match. */
|
|
1662
1806
|
#findTreePath(node, predicate, ancestors = []) {
|
|
1663
1807
|
const path = [...ancestors, node];
|
|
@@ -2386,6 +2386,69 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
|
|
|
2386
2386
|
this.#dispatchSelect(prev);
|
|
2387
2387
|
this.requestUpdate();
|
|
2388
2388
|
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Expand every already-loaded branch in a single update. With `maxDepth`,
|
|
2391
|
+
* only branches at depth < `maxDepth` are expanded (the root's children are
|
|
2392
|
+
* depth 0), so rows at depth `maxDepth` become visible but stay collapsed.
|
|
2393
|
+
*
|
|
2394
|
+
* Unloaded (lazy) branches are not fetched — call again once their children
|
|
2395
|
+
* have loaded to expand further. Bulk expansion ignores `exclusivity`. Fires
|
|
2396
|
+
* one `expand` event per branch that actually opens — none for branches that
|
|
2397
|
+
* were already expanded, including ones opened by filter auto-expansion.
|
|
2398
|
+
*/
|
|
2399
|
+
expandAll(maxDepth) {
|
|
2400
|
+
this.#bulkExpand(true, maxDepth);
|
|
2401
|
+
}
|
|
2402
|
+
/**
|
|
2403
|
+
* Collapse expanded branches in a single update. With `maxDepth`, only
|
|
2404
|
+
* branches at depth >= `maxDepth` are collapsed, leaving the tree open down
|
|
2405
|
+
* to depth `maxDepth - 1` (`collapseAll(0)` is a full collapse). Fires one
|
|
2406
|
+
* `collapse` event per branch that actually closes.
|
|
2407
|
+
*/
|
|
2408
|
+
collapseAll(maxDepth) {
|
|
2409
|
+
this.#bulkExpand(false, maxDepth);
|
|
2410
|
+
}
|
|
2411
|
+
/** Shared bulk expand / collapse: one `_expandedKeys` write, per-node events for real changes only. */
|
|
2412
|
+
#bulkExpand(expand, maxDepth) {
|
|
2413
|
+
const root = this.root;
|
|
2414
|
+
if (!root) return;
|
|
2415
|
+
const inScope = (depth) => expand ? depth < (maxDepth ?? Number.POSITIVE_INFINITY) : depth >= (maxDepth ?? 0);
|
|
2416
|
+
const changed = [];
|
|
2417
|
+
let next = this._expandedKeys;
|
|
2418
|
+
const visit = (node, depth) => {
|
|
2419
|
+
const children = node.children;
|
|
2420
|
+
if (node.item.isLeaf !== true && children !== void 0 && children.length > 0 && inScope(depth)) {
|
|
2421
|
+
const keyed = next.has(node.item.key);
|
|
2422
|
+
if (expand && !keyed) {
|
|
2423
|
+
next = withKey(next, node.item.key);
|
|
2424
|
+
if (!this._isExpanded(node, depth)) changed.push({
|
|
2425
|
+
node,
|
|
2426
|
+
depth
|
|
2427
|
+
});
|
|
2428
|
+
} else if (!expand && keyed) {
|
|
2429
|
+
next = withoutKey(next, node.item.key);
|
|
2430
|
+
changed.push({
|
|
2431
|
+
node,
|
|
2432
|
+
depth
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
for (const child of children ?? []) visit(child, depth + 1);
|
|
2437
|
+
};
|
|
2438
|
+
for (const child of root.children ?? []) visit(child, 0);
|
|
2439
|
+
if (next === this._expandedKeys) return;
|
|
2440
|
+
this.#setExpanded(next);
|
|
2441
|
+
const type = expand ? `expand` : `collapse`;
|
|
2442
|
+
for (const { node, depth } of changed) this.dispatchEvent(new CustomEvent(type, {
|
|
2443
|
+
detail: {
|
|
2444
|
+
node,
|
|
2445
|
+
depth
|
|
2446
|
+
},
|
|
2447
|
+
bubbles: true,
|
|
2448
|
+
composed: true
|
|
2449
|
+
}));
|
|
2450
|
+
this.requestUpdate();
|
|
2451
|
+
}
|
|
2389
2452
|
navigateTo(keys, options = {}) {
|
|
2390
2453
|
if (!this.root?.children) return false;
|
|
2391
2454
|
const prevOrigin = this.#selectOrigin;
|
|
@@ -213,6 +213,14 @@ type TreeDropEvent = CustomEvent<TreeDropDetail>;
|
|
|
213
213
|
type TreeDragEndEvent = CustomEvent<{
|
|
214
214
|
dropped: boolean;
|
|
215
215
|
}>;
|
|
216
|
+
/** How `navigateTo` scrolls its target: edge-align it, or centre it when it is off screen. */
|
|
217
|
+
type TreeRevealMode = `edge` | `center-if-hidden`;
|
|
218
|
+
type TreeNavigateOptions = {
|
|
219
|
+
/** Scroll behaviour for the target (default `edge`). */
|
|
220
|
+
reveal?: TreeRevealMode;
|
|
221
|
+
/** Suppress the `select` event this navigation would otherwise fire. */
|
|
222
|
+
silent?: boolean;
|
|
223
|
+
};
|
|
216
224
|
/**
|
|
217
225
|
* Fired when the selection set changes by any means (click, keyboard, programmatic).
|
|
218
226
|
* Event name: `select`
|
|
@@ -413,4 +421,4 @@ interface TreeSearchHost extends TreeComponent {
|
|
|
413
421
|
readonly updateComplete: Promise<boolean>;
|
|
414
422
|
}
|
|
415
423
|
//#endregion
|
|
416
|
-
export {
|
|
424
|
+
export { TreeTickleEvent as A, ListNativeDragStartEvent as B, TreeItemDblClickEvent as C, TreeSelectDetail as D, TreeSearchHost as E, ListDragPreviewContext as F, ListDragPreviewRenderer as I, ListDragPreviewSourceResolver as L, TreeDataModelOptions as M, ListDragEntry as N, TreeSelectEvent as O, ListDragPreview as P, ListDragSource as R, TreeItemDblClickDetail as S, TreeRevealMode as T, TreeDropTarget as _, TreeComponent as a, TreeItemClickDetail as b, TreeDragContext as c, TreeDragStartEvent as d, TreeDropApplyContext as f, TreeDropPosition as g, TreeDropEvent as h, TreeCollapseEvent as i, TreeDataModel as j, TreeTickleDetail as k, TreeDragEndEvent as l, TreeDropDetail as m, TreeActivateEvent as n, TreeComponentEventMap as o, TreeDropContext as p, TreeCollapseDetail as r, TreeDndMode as s, TreeActivateDetail as t, TreeDragOverEvent as u, TreeExpandDetail as v, TreeNavigateOptions as w, TreeItemClickEvent as x, TreeExpandEvent as y, ListNativeDragStartDetail as z };
|
package/dist/tree.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as TreeTickleEvent, D as TreeSelectDetail, M as TreeDataModelOptions, O as TreeSelectEvent, T as TreeRevealMode, _ as TreeDropTarget, b as TreeItemClickDetail, c as TreeDragContext, d as TreeDragStartEvent, g as TreeDropPosition, h as TreeDropEvent, i as TreeCollapseEvent, j as TreeDataModel, k as TreeTickleDetail, l as TreeDragEndEvent, m as TreeDropDetail, n as TreeActivateEvent, o as TreeComponentEventMap, p as TreeDropContext, r as TreeCollapseDetail, s as TreeDndMode, t as TreeActivateDetail, u as TreeDragOverEvent, v as TreeExpandDetail, w as TreeNavigateOptions, x as TreeItemClickEvent, y as TreeExpandEvent } from "./tree-component-Tslymyyl.js";
|
|
2
2
|
import { a as TreeItem, n as TreeExclusivity, o as TreeNode, r as TreeFilterPredicate, s as TreeSelectionFilter, t as LoadChildrenCallback } from "./types-DQ7KOUIj.js";
|
|
3
|
-
import { a as TreeDropVisibleRow, c as TreeListElement, d as TreeBaseElementEventMap, f as
|
|
4
|
-
export { type LoadChildrenCallback, type TreeActivateDetail, type TreeActivateEvent, TreeBaseElement, TreeBaseElementEventMap, type TreeCheckboxState, type TreeCollapseDetail, type TreeCollapseEvent, type TreeComponentEventMap, TreeConfig, TreeController, TreeControllerOptions, TreeDataModel, type TreeDataModelOptions, type TreeDndMode, type TreeDragContext, type TreeDragEndEvent, type TreeDragOverEvent, type TreeDragStartEvent, type TreeDropContext, TreeDropController, TreeDropControllerConfig, type TreeDropDetail, type TreeDropEvent, type TreeDropPosition, TreeDropResolvedDrag, TreeDropRowInfo, type TreeDropTarget, TreeDropVisibleRow, type TreeExclusivity, type TreeExpandDetail, type TreeExpandEvent, type TreeFilterPredicate, type TreeItem, type TreeItemClickDetail, type TreeItemClickEvent, TreeListElement, TreeNavigateOptions, type TreeNode, TreeRevealMode, type TreeSelectDetail, type TreeSelectEvent, type TreeSelectionFilter, type TreeTickleDetail, type TreeTickleEvent, TreeVirtualize };
|
|
3
|
+
import { a as TreeDropVisibleRow, c as TreeListElement, d as TreeBaseElementEventMap, f as TreeVirtualize, i as TreeDropRowInfo, l as TreeCheckboxState, n as TreeDropControllerConfig, o as TreeController, p as TreeConfig, r as TreeDropResolvedDrag, s as TreeControllerOptions, t as TreeDropController, u as TreeBaseElement } from "./index-CSCrK0hR.js";
|
|
4
|
+
export { type LoadChildrenCallback, type TreeActivateDetail, type TreeActivateEvent, TreeBaseElement, TreeBaseElementEventMap, type TreeCheckboxState, type TreeCollapseDetail, type TreeCollapseEvent, type TreeComponentEventMap, TreeConfig, TreeController, TreeControllerOptions, TreeDataModel, type TreeDataModelOptions, type TreeDndMode, type TreeDragContext, type TreeDragEndEvent, type TreeDragOverEvent, type TreeDragStartEvent, type TreeDropContext, TreeDropController, TreeDropControllerConfig, type TreeDropDetail, type TreeDropEvent, type TreeDropPosition, TreeDropResolvedDrag, TreeDropRowInfo, type TreeDropTarget, TreeDropVisibleRow, type TreeExclusivity, type TreeExpandDetail, type TreeExpandEvent, type TreeFilterPredicate, type TreeItem, type TreeItemClickDetail, type TreeItemClickEvent, TreeListElement, type TreeNavigateOptions, type TreeNode, type TreeRevealMode, type TreeSelectDetail, type TreeSelectEvent, type TreeSelectionFilter, type TreeTickleDetail, type TreeTickleEvent, TreeVirtualize };
|
package/dist/tree.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TreeDataModel, i as TreeDropController, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-
|
|
1
|
+
import { a as TreeDataModel, i as TreeDropController, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-9Hs7VxuX.js";
|
|
2
2
|
export { TreeBaseElement, TreeController, TreeDataModel, TreeDropController, TreeListElement };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as ListNativeDragStartEvent, I as ListDragPreviewRenderer, L as ListDragPreviewSourceResolver } from "./tree-component-Tslymyyl.js";
|
|
2
2
|
import { n as ListViewportChangeEvent } from "./list-viewport-types-BRZsLcvp.js";
|
|
3
|
-
import { S as ListSearchController } from "./index-
|
|
3
|
+
import { S as ListSearchController } from "./index-CO6m-YC0.js";
|
|
4
4
|
import { i as ListInteractionMode, l as ListSelectionMode, r as ListElementEventMap } from "./list-selection-types-PjKdhF-a.js";
|
|
5
5
|
import "./editable-label-rzXkjf1H.js";
|
|
6
6
|
import { LitElement, PropertyValues } from "lit";
|
package/dist/vertical-list.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as ListNativeDragStartEvent, F as ListDragPreviewContext, I as ListDragPreviewRenderer, N as ListDragEntry, P as ListDragPreview, R as ListDragSource, z as ListNativeDragStartDetail } from "./tree-component-Tslymyyl.js";
|
|
2
2
|
import { n as ListViewportChangeEvent, t as ListViewportChangeDetail } from "./list-viewport-types-BRZsLcvp.js";
|
|
3
3
|
import { a as ListItemClickDetail, c as ListSelectEvent, d as ListTickleEvent, i as ListInteractionMode, l as ListSelectionMode, n as ListActivateEvent, o as ListItemClickEvent, r as ListElementEventMap, s as ListSelectDetail, t as ListActivateDetail, u as ListTickleDetail } from "./list-selection-types-PjKdhF-a.js";
|
|
4
|
-
import { a as ListContextMenuDetail, c as ListDragLeaveEvent, d as ListDropEvent, f as ListEditCancelDetail, h as ListEditCommitEvent, i as VerticalListElementEventMap, l as ListDragOverEvent, m as ListEditCommitDetail, n as CustomCheckboxOptions, o as ListContextMenuEvent, p as ListEditCancelEvent, r as VerticalListElement, s as ListDragLeaveDetail, t as AddItemOptions, u as ListDropDetail } from "./vertical-list-
|
|
4
|
+
import { a as ListContextMenuDetail, c as ListDragLeaveEvent, d as ListDropEvent, f as ListEditCancelDetail, h as ListEditCommitEvent, i as VerticalListElementEventMap, l as ListDragOverEvent, m as ListEditCommitDetail, n as CustomCheckboxOptions, o as ListContextMenuEvent, p as ListEditCancelEvent, r as VerticalListElement, s as ListDragLeaveDetail, t as AddItemOptions, u as ListDropDetail } from "./vertical-list-B1Fq06xr.js";
|
|
5
5
|
export { AddItemOptions, CustomCheckboxOptions, type ListActivateDetail, type ListActivateEvent, type ListContextMenuDetail, type ListContextMenuEvent, type ListDragEntry, type ListDragLeaveDetail, type ListDragLeaveEvent, type ListDragOverEvent, type ListDragPreview, type ListDragPreviewContext, type ListDragPreviewRenderer, type ListDragSource, type ListDropDetail, type ListDropEvent, type ListEditCancelDetail, type ListEditCancelEvent, type ListEditCommitDetail, type ListEditCommitEvent, type ListElementEventMap, type ListInteractionMode, type ListItemClickDetail, type ListItemClickEvent, type ListNativeDragStartDetail, type ListNativeDragStartEvent, type ListSelectDetail, type ListSelectEvent, type ListSelectionMode, type ListTickleDetail, type ListTickleEvent, type ListViewportChangeDetail, type ListViewportChangeEvent, VerticalListElement, VerticalListElementEventMap };
|
|
@@ -118,7 +118,7 @@ Labels keep their on-screen size while zooming, so zooming in spreads a crowd ap
|
|
|
118
118
|
|
|
119
119
|
### Child preview
|
|
120
120
|
|
|
121
|
-
On by default; `disable-child-preview` turns it off. Hovering or keyboard-tickling a node whose children aren't all legible opens a popover listing them. That means some children had their labels culled, or sit beyond `maxRadius`. It shows up to 12 children, then "+N more". Clicking an entry focuses that child and selects it, per `interactionMode`. The popover stays open while the pointer moves onto it, and Escape closes it. Style it with `::part(child-preview)`. Nodes that can show a preview drop their native `<title>` tooltip so the two don't stack. With the preview disabled, every node keeps its tooltip.
|
|
121
|
+
On by default; `disable-child-preview` turns it off. Hovering or keyboard-tickling a node whose children aren't all legible, and resting there for 600ms, opens a popover listing them. That means some children had their labels culled, or sit beyond `maxRadius`. It shows up to 12 children, then "+N more". Clicking an entry focuses that child and selects it, per `interactionMode`. The popover stays open while the pointer moves onto it, and Escape closes it. Starting a drag (pan) cancels a pending preview and closes an open one. Style it with `::part(child-preview)`. Nodes that can show a preview drop their native `<title>` tooltip so the two don't stack. With the preview disabled, every node keeps its tooltip.
|
|
122
122
|
|
|
123
123
|
### Crowded fans
|
|
124
124
|
|
package/docs-user/index.json
CHANGED
package/docs-user/miller.md
CHANGED
|
@@ -54,7 +54,11 @@ Inherited from theme:
|
|
|
54
54
|
// Navigate to a node by key path (root-to-target keys, excluding root itself).
|
|
55
55
|
// Only works with already-loaded children — returns false if any node along
|
|
56
56
|
// the path has unloaded children or if a key is not found.
|
|
57
|
-
navigateTo(keys: readonly string[]): boolean
|
|
57
|
+
navigateTo(keys: readonly string[], options?: { reveal?: 'edge' | 'center-if-hidden'; silent?: boolean }): boolean
|
|
58
|
+
|
|
59
|
+
// Re-sync after children arrays were patched in place (used by MillerTreeController.sync()):
|
|
60
|
+
// closes columns whose selected node was removed, then re-renders.
|
|
61
|
+
invalidateStructure(): void
|
|
58
62
|
|
|
59
63
|
// Navigate to the first loaded node (breadth-first) matching a predicate.
|
|
60
64
|
// Returns false if no match is found in the currently loaded tree.
|
|
@@ -398,11 +402,27 @@ type MillerTreeControllerOptions<T> = {
|
|
|
398
402
|
// key and label are required. If isLeaf is omitted, it is inferred
|
|
399
403
|
// from whether the TraversableTree node has any children.
|
|
400
404
|
valueToItem: (value: T, node: TraversableTree<T>) => MillerItem;
|
|
405
|
+
// Installed as element.loadChildren by connect(). Defaults to a synchronous
|
|
406
|
+
// read from the source tree. sync() never touches it.
|
|
407
|
+
loadChildren?: LoadChildrenCallback;
|
|
401
408
|
};
|
|
402
409
|
```
|
|
403
410
|
|
|
404
411
|
### Methods
|
|
405
412
|
|
|
413
|
+
```typescript
|
|
414
|
+
// Re-derive children of already-loaded nodes from the source tree and patch them
|
|
415
|
+
// in place (arrays mutated, MillerNodes reused by key). viewRoot is reset only if
|
|
416
|
+
// its node was removed. Refreshes the element once.
|
|
417
|
+
sync(): void
|
|
418
|
+
|
|
419
|
+
// Lookups that avoid searching the source tree. Key paths are RELATIVE TO THE
|
|
420
|
+
// VIEW ROOT; identities outside a rebased view return false.
|
|
421
|
+
lookupByKey(key: string): TraversableTree<T> | undefined
|
|
422
|
+
navigateToKeyPath(keys: readonly string[], options?: TreeNavigateOptions): boolean
|
|
423
|
+
navigateToIdentity(identity: unknown, options?: TreeNavigateOptions): boolean
|
|
424
|
+
```
|
|
425
|
+
|
|
406
426
|
```typescript
|
|
407
427
|
// Wire up the controller — sets element.root and provides loadChildren callback.
|
|
408
428
|
// Safe to call again after disconnect().
|
|
@@ -17,7 +17,7 @@ provider returns that column's choices; items may carry a `count` for display.
|
|
|
17
17
|
|
|
18
18
|
| Property | Type | Default | Description |
|
|
19
19
|
|----------|------|---------|-------------|
|
|
20
|
-
| `categories` | `readonly MulticolCategory[]` | `[]` | Facet definitions (tree, shown in the header picker menu). Each may carry `defaults`: `grouped`, `groupBy`, `allLabel`, `sort`. |
|
|
20
|
+
| `categories` | `readonly MulticolCategory[]` | `[]` | Facet definitions (tree, shown in the header picker menu). Each may carry `description` (shown as a tooltip on the column header label) and `defaults`: `grouped`, `groupBy`, `allLabel`, `sort`. |
|
|
21
21
|
| `dataProvider` | `IDataProvider<MulticolQuery, MulticolItem>` \| `undefined` | — | Called per column whenever that column's inputs change. Sync arrays, Promises and async-iterables are supported; the `AbortSignal` is honoured. |
|
|
22
22
|
| `selectionMode` | `ListSelectionMode` | `'multiple'` | `'none'`, `'single'` or `'multiple'` selection within a column. |
|
|
23
23
|
| `minColumns` | `number` | `1` | Floor for column removal. |
|
package/docs-user/tree.md
CHANGED
|
@@ -330,6 +330,39 @@ el.addEventListener('select', ({ detail }) => {
|
|
|
330
330
|
|
|
331
331
|
---
|
|
332
332
|
|
|
333
|
+
## Expansion
|
|
334
|
+
|
|
335
|
+
### Programmatic expansion
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// Expand every already-loaded branch
|
|
339
|
+
el.expandAll();
|
|
340
|
+
|
|
341
|
+
// Expand down to depth 1: top-level branches open, so their children
|
|
342
|
+
// (depth 1) are visible (root's children are depth 0)
|
|
343
|
+
el.expandAll(1);
|
|
344
|
+
|
|
345
|
+
// Collapse everything
|
|
346
|
+
el.collapseAll();
|
|
347
|
+
|
|
348
|
+
// Collapse branches at depth 1 and deeper — top-level branches stay open,
|
|
349
|
+
// so the tree remains open down to depth 0
|
|
350
|
+
el.collapseAll(1);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Notes:
|
|
354
|
+
|
|
355
|
+
- Both methods apply in a single update — one re-render for the whole tree.
|
|
356
|
+
- Only **already-loaded** branches are expanded; lazily loaded children are not
|
|
357
|
+
fetched. Call `expandAll()` again after children load to expand further.
|
|
358
|
+
- Bulk expansion ignores `exclusivity` — it is an explicit "open everything"
|
|
359
|
+
command.
|
|
360
|
+
- One `expand` / `collapse` event fires per branch whose state actually changes;
|
|
361
|
+
branches already in the target state (including those opened by filter
|
|
362
|
+
auto-expansion) stay silent.
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
333
366
|
## Events
|
|
334
367
|
|
|
335
368
|
All events **bubble** and are **composed** (cross shadow-DOM boundaries).
|