@ixfx/components 0.15.3 → 0.15.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/bundle/index.d.ts +473 -17
  2. package/bundle/index.js +432 -225
  3. package/dist/crumbs.d.ts +1 -1
  4. package/dist/{data-grid-Dx3l3dxw.js → data-grid-C1sZX6n9.js} +37 -9
  5. package/dist/{data-grid-DmkId8bu.d.ts → data-grid-D7qzmMMp.d.ts} +13 -6
  6. package/dist/data-grid.d.ts +2 -2
  7. package/dist/data-grid.js +1 -1
  8. package/dist/{detail-list-CmQqeXvp.d.ts → detail-list-CKIKlszv.d.ts} +3 -3
  9. package/dist/detail-list.d.ts +3 -3
  10. package/dist/{flat-list-BmQBB3ZC.d.ts → flat-list-DXMyDiSH.d.ts} +2 -2
  11. package/dist/flat-list.d.ts +1 -1
  12. package/dist/{grid-list-BVsUEvgd.d.ts → grid-list-0e7snW6t.d.ts} +3 -3
  13. package/dist/grid-list.d.ts +2 -2
  14. package/dist/incr-search.d.ts +1 -1
  15. package/dist/{index-DuhzWWTr.d.ts → index-DEVyVIn8.d.ts} +28 -8
  16. package/dist/{index-BvoXLnBf.d.ts → index-KN2MW_xD.d.ts} +1 -1
  17. package/dist/index.d.ts +176 -15
  18. package/dist/index.js +274 -64
  19. package/dist/{list-native-drag-controller-NMspr0E1.d.ts → list-native-drag-controller-O2Nruqpi.d.ts} +1 -1
  20. package/dist/miller.d.ts +2 -2
  21. package/dist/sunburst-tree.d.ts +2 -0
  22. package/dist/sunburst-tree.js +2 -0
  23. package/dist/sunbursttree-list--Z0uyh8O.d.ts +266 -0
  24. package/dist/sunbursttree-list-yXwS-uEI.js +1337 -0
  25. package/dist/{tree-2_TRrLAV.js → tree-D8VxvR9u.js} +44 -5
  26. package/dist/{tree-component-CDsYTBDi.d.ts → tree-component-C6UHO-w9.d.ts} +12 -3
  27. package/dist/tree.d.ts +3 -3
  28. package/dist/tree.js +1 -1
  29. package/dist/{vertical-list-ztrdFvyT.d.ts → vertical-list-D8MksoTp.d.ts} +2 -2
  30. package/dist/vertical-list.d.ts +2 -2
  31. package/docs-user/README.md +1 -0
  32. package/docs-user/data-grid.md +11 -4
  33. package/docs-user/dock.md +62 -1
  34. package/docs-user/index.json +7 -2
  35. package/docs-user/llms.txt +1 -0
  36. package/docs-user/sunburst-tree.md +193 -0
  37. package/docs-user/tree.md +40 -1
  38. package/llms.txt +1 -0
  39. package/package.json +5 -1
@@ -3,7 +3,7 @@ import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrA
3
3
  import { t as themeFallbacks } from "./fallbacks-DQ_5OsmR.js";
4
4
  import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
5
5
  import { i as ListSearchController, n as listSearchStyles, o as registerListSearchCommands, r as LIST_SEARCH_MODES, t as createTreeListSearchAdapter } from "./incr-search-JIhwB49y.js";
6
- import { i as animateListItemOut, r as animateListItemIn, t as ListNativeDragController } from "./list-native-drag-controller-CcXoS0YX.js";
6
+ import { i as animateListItemOut, o as prefersReducedMotion, r as animateListItemIn, t as ListNativeDragController } from "./list-native-drag-controller-CcXoS0YX.js";
7
7
  import "./interaction-E-uMGHkJ.js";
8
8
  import { t as collectDataResult } from "./data-provider-35TyWclX.js";
9
9
  import { LitElement, css, html, nothing } from "lit";
@@ -1211,6 +1211,7 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
1211
1211
  this.stickyHeaders = true;
1212
1212
  this.virtualize = `auto`;
1213
1213
  this.virtualizeThreshold = 200;
1214
+ this.animation = `auto`;
1214
1215
  this.applyInternalDrop = true;
1215
1216
  this._expandedKeys = /* @__PURE__ */ new Set();
1216
1217
  this.#structureVersion = 0;
@@ -1650,6 +1651,12 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
1650
1651
  #leaving;
1651
1652
  #leavingAnimations;
1652
1653
  #treeAnimReady;
1654
+ /**
1655
+ * Per-call animation override. When set (by methods like `expandAll` with `animate: false`),
1656
+ * it takes precedence over the `animation` property for one update cycle.
1657
+ * Cleared after each update.
1658
+ */
1659
+ #animationOverride;
1653
1660
  #dragSourceId;
1654
1661
  #postDragSuppressTimer;
1655
1662
  #dropState;
@@ -1901,6 +1908,11 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
1901
1908
  return false;
1902
1909
  }
1903
1910
  #runItemAnimations() {
1911
+ if (!this.#shouldAnimate()) {
1912
+ this.#enteringKeys.clear();
1913
+ this.#cancelAllLeavingAnimations();
1914
+ this.#leaving.clear();
1915
+ }
1904
1916
  if (this.#enteringKeys.size > 0) {
1905
1917
  for (const el of this.renderRoot.querySelectorAll(`.item`)) {
1906
1918
  const key = el.dataset.key;
@@ -1944,6 +1956,17 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
1944
1956
  for (const entry of visible) this.#lastNodeByKey.set(entry.node.item.key, entry);
1945
1957
  this.#treeAnimReady = true;
1946
1958
  }
1959
+ /**
1960
+ * Determines whether row enter/leave animations should run for this update.
1961
+ * Checks the per-call `#animationOverride` first, then the `animation` property,
1962
+ * and finally `prefersReducedMotion()`.
1963
+ */
1964
+ #shouldAnimate() {
1965
+ if (this.#animationOverride !== void 0) return this.#animationOverride;
1966
+ if (this.animation === `never`) return false;
1967
+ if (this.animation === `always`) return true;
1968
+ return !prefersReducedMotion();
1969
+ }
1947
1970
  updated(changedProps) {
1948
1971
  this.#rowsCache = void 0;
1949
1972
  this.#syncVirtualObservers();
@@ -1994,6 +2017,7 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
1994
2017
  if (this.#measureMetrics()) queueMicrotask(() => this.requestUpdate());
1995
2018
  this.#repaintSearchHighlights();
1996
2019
  } else this.#runItemAnimations();
2020
+ this.#animationOverride = void 0;
1997
2021
  }
1998
2022
  /** Highlights are painted on rendered labels, so they move with the window. */
1999
2023
  #repaintSearchHighlights() {
@@ -2496,8 +2520,12 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
2496
2520
  * have loaded to expand further. Bulk expansion ignores `exclusivity`. Fires
2497
2521
  * one `expand` event per branch that actually opens — none for branches that
2498
2522
  * were already expanded, including ones opened by filter auto-expansion.
2523
+ *
2524
+ * @param maxDepth - Maximum depth to expand to
2525
+ * @param animate - Whether to animate row enter/leave. When omitted, uses the `animation` property value.
2499
2526
  */
2500
- expandAll(maxDepth) {
2527
+ expandAll(maxDepth, animate) {
2528
+ this.#animationOverride = animate;
2501
2529
  this.#bulkExpand(true, maxDepth);
2502
2530
  }
2503
2531
  /**
@@ -2505,8 +2533,12 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
2505
2533
  * branches at depth >= `maxDepth` are collapsed, leaving the tree open down
2506
2534
  * to depth `maxDepth - 1` (`collapseAll(0)` is a full collapse). Fires one
2507
2535
  * `collapse` event per branch that actually closes.
2536
+ *
2537
+ * @param maxDepth - Maximum depth to collapse from
2538
+ * @param animate - Whether to animate row enter/leave. When omitted, uses the `animation` property value.
2508
2539
  */
2509
- collapseAll(maxDepth) {
2540
+ collapseAll(maxDepth, animate) {
2541
+ this.#animationOverride = animate;
2510
2542
  this.#bulkExpand(false, maxDepth);
2511
2543
  }
2512
2544
  /** Shared bulk expand / collapse: one `_expandedKeys` write, per-node events for real changes only. */
@@ -2556,6 +2588,7 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
2556
2588
  const prevSilent = this.#selectSilent;
2557
2589
  this.#selectOrigin = `programmatic`;
2558
2590
  this.#selectSilent = options.silent === true;
2591
+ if (options.animate !== void 0) this.#animationOverride = options.animate;
2559
2592
  try {
2560
2593
  return this.#navigateTo(keys, options.reveal ?? `edge`);
2561
2594
  } finally {
@@ -2587,15 +2620,17 @@ let TreeBaseElement = class TreeBaseElement extends LitElement {
2587
2620
  this.#scrollKeyIntoView(keys.at(-1), reveal);
2588
2621
  return true;
2589
2622
  }
2590
- navigateToNode(predicate) {
2623
+ navigateToNode(predicate, animate) {
2591
2624
  if (!this.root?.children) return false;
2625
+ if (animate !== void 0) this.#animationOverride = animate;
2592
2626
  const keyPath = this.#findNodePath(this.root.children, predicate);
2593
2627
  if (!keyPath) return false;
2594
2628
  return this.navigateTo(keyPath);
2595
2629
  }
2596
2630
  /** Expand ancestors + move the cursor to `key`, without touching the selection (search "navigate" mode). */
2597
- revealKey(key) {
2631
+ revealKey(key, animate) {
2598
2632
  if (!this.root?.children) return false;
2633
+ if (animate !== void 0) this.#animationOverride = animate;
2599
2634
  const path = this.#findNodePath(this.root.children, (n) => n.item.key === key);
2600
2635
  if (!path) return false;
2601
2636
  for (let i = 0; i < path.length - 1; i++) {
@@ -3542,6 +3577,10 @@ __decorate([property({
3542
3577
  type: Number,
3543
3578
  attribute: `virtualize-threshold`
3544
3579
  })], TreeBaseElement.prototype, "virtualizeThreshold", void 0);
3580
+ __decorate([property({
3581
+ reflect: true,
3582
+ attribute: `animation`
3583
+ })], TreeBaseElement.prototype, "animation", void 0);
3545
3584
  __decorate([property({ attribute: false })], TreeBaseElement.prototype, "dragPreviewRenderer", void 0);
3546
3585
  __decorate([property({ attribute: false })], TreeBaseElement.prototype, "dragPreviewSource", void 0);
3547
3586
  __decorate([property({ attribute: false })], TreeBaseElement.prototype, "canDrag", void 0);
@@ -220,6 +220,11 @@ type TreeNavigateOptions = {
220
220
  reveal?: TreeRevealMode;
221
221
  /** Suppress the `select` event this navigation would otherwise fire. */
222
222
  silent?: boolean;
223
+ /**
224
+ * Whether to animate row enter/leave for this operation.
225
+ * When omitted, uses the `animation` property value (`'auto'` by default).
226
+ */
227
+ animate?: boolean;
223
228
  };
224
229
  /**
225
230
  * Fired when the selection set changes by any means (click, keyboard, programmatic).
@@ -385,18 +390,22 @@ interface TreeComponent extends EventTarget {
385
390
  * Expand ancestors and select the node reached by following `keys` from the root's children.
386
391
  * Returns `true` if the path was found and applied.
387
392
  */
388
- navigateTo: (keys: readonly string[]) => boolean;
393
+ navigateTo: (keys: readonly string[], options?: TreeNavigateOptions) => boolean;
389
394
  /**
390
395
  * Expand ancestors and select the first node for which `predicate` returns `true`.
391
396
  * Returns `true` if a matching node was found.
397
+ * @param predicate - Function to find the target node
398
+ * @param animate - Whether to animate row enter/leave. When omitted, uses the `animation` property value.
392
399
  */
393
- navigateToNode: (predicate: (node: TreeNode) => boolean) => boolean;
400
+ navigateToNode: (predicate: (node: TreeNode) => boolean, animate?: boolean) => boolean;
394
401
  /**
395
402
  * Expand ancestors of the node with `key` and move the keyboard cursor to it,
396
403
  * scrolling it into view — **without** changing the selection. Returns `true`
397
404
  * if the node was found. Used by incremental-search "navigate" mode.
405
+ * @param key - Key of the node to reveal
406
+ * @param animate - Whether to animate row enter/leave. When omitted, uses the `animation` property value.
398
407
  */
399
- revealKey: (key: string) => boolean;
408
+ revealKey: (key: string, animate?: boolean) => boolean;
400
409
  /** Replace the selection with `nodes` (subject to `selectionFilter`). Used by incremental-search "select" mode. */
401
410
  selectMany: (nodes: Iterable<TreeNode>) => void;
402
411
  /**
package/dist/tree.d.ts CHANGED
@@ -1,4 +1,4 @@
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-CDsYTBDi.js";
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-C6UHO-w9.js";
2
2
  import { a as TreeItem, n as TreeExclusivity, o as TreeNode, r as TreeFilterPredicate, s as TreeSelectionFilter, t as LoadChildrenCallback } from "./types-Jky6oNAp.js";
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-DuhzWWTr.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 };
3
+ import { a as TreeDropVisibleRow, c as TreeListElement, d as TreeBaseElement, f as TreeBaseElementEventMap, i as TreeDropRowInfo, l as TreeCheckboxState, m as TreeConfig, n as TreeDropControllerConfig, o as TreeController, p as TreeVirtualize, r as TreeDropResolvedDrag, s as TreeControllerOptions, t as TreeDropController, u as TreeAnimation } from "./index-DEVyVIn8.js";
4
+ export { type LoadChildrenCallback, type TreeActivateDetail, type TreeActivateEvent, TreeAnimation, 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-2_TRrLAV.js";
1
+ import { a as TreeDataModel, i as TreeDropController, n as TreeListElement, r as TreeBaseElement, t as TreeController } from "./tree-D8VxvR9u.js";
2
2
  export { TreeBaseElement, TreeController, TreeDataModel, TreeDropController, TreeListElement };
@@ -1,6 +1,6 @@
1
- import { B as ListNativeDragStartEvent, I as ListDragPreviewRenderer, L as ListDragPreviewSourceResolver } from "./tree-component-CDsYTBDi.js";
1
+ import { B as ListNativeDragStartEvent, I as ListDragPreviewRenderer, L as ListDragPreviewSourceResolver } from "./tree-component-C6UHO-w9.js";
2
2
  import { n as ListViewportChangeEvent } from "./list-viewport-types-BRZsLcvp.js";
3
- import { S as ListSearchController } from "./index-BvoXLnBf.js";
3
+ import { S as ListSearchController } from "./index-KN2MW_xD.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";
@@ -1,5 +1,5 @@
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-CDsYTBDi.js";
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-C6UHO-w9.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-ztrdFvyT.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-D8MksoTp.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 };
@@ -49,6 +49,7 @@ This directory contains documentation for each component and some architectural
49
49
  - [snackbar](./snackbar.md)
50
50
  - [snap-container](./snap-container.md)
51
51
  - [split-layout](./split-layout.md)
52
+ - [sunburst-tree](./sunburst-tree.md)
52
53
  - [swipe](./swipe.md)
53
54
  - [tabs](./tabs.md)
54
55
  - [text-shimmer](./text-shimmer.md)
@@ -306,9 +306,12 @@ for plain text columns. It returns `false` and does nothing for:
306
306
 
307
307
  - an unknown column key,
308
308
  - a column with `resizable: false`,
309
- - a column with a `renderer` — its rendered output (icons, badges, custom
310
- markup, etc.) can't be reliably measured from the raw value, so sizing to
311
- it would be a guess.
309
+ - a column with a `renderer` that gives no hint of its size — its rendered
310
+ output (icons, badges, custom markup, etc.) can't be reliably measured from
311
+ the raw value. Opt in with either:
312
+ - `fixedExtraWidth: n` — pixels added to the measured text width (e.g. a
313
+ thumbnail beside the text), or
314
+ - `measure: (row, col) => px` — your own content-width measure per row.
312
315
 
313
316
  `--data-grid-column-resize-duration` (default `180ms`) controls the
314
317
  transition speed.
@@ -459,6 +462,10 @@ Demo: `Score` is random float `0–100` with 0–3 decimals, `align:'right'`, `f
459
462
 
460
463
  Gear menu (`ixfx-menu-trigger` placement `bottom-end`, `inset-area: block-end span-inline-end`) lists `Columns` (check-items) and `Group by` (radio). Toggle visibility via `col.visible=false` or menu. Event: `datagrid-column-visibility` `{key, visible}`.
461
464
 
465
+ Set `col.group` to a heading (e.g. the facet a column came from) and the menu
466
+ lists columns sharing that group in one submenu; ungrouped columns stay at the
467
+ top level.
468
+
462
469
  `column-menu` attribute: `none` (default) hides the gear menu; `auto` opts in to
463
470
  rendering it. `col.visible` + `datagrid-column-visibility` give full external
464
471
  control, so a host can own the column-visibility UI.
@@ -542,7 +549,7 @@ still shadow-internal.
542
549
  | `appendRows` | `(rows)` | Add rows without a full re-sort/rebuild — for streaming |
543
550
  | `selectKeys` / `set selectedKeys` | `(keys)` | Replace the selection by row key |
544
551
  | `tickleRow` | `(key)` | Move the row cursor and scroll it into view |
545
- | `autoSizeColumn` | `(key)=>boolean` | Resize a column to fit its contents, animating the width. Returns `false` (no-op) for an unknown column, `resizable: false`, or a column with a `renderer` — see [Auto-sizing a column](#auto-sizing-a-column). |
552
+ | `autoSizeColumn` | `(key)=>boolean` | Resize a column to fit its contents, animating the width. Returns `false` (no-op) for an unknown column, `resizable: false`, or a `renderer` column without `measure`/`fixedExtraWidth` — see [Auto-sizing a column](#auto-sizing-a-column). |
546
553
 
547
554
  | Getter | Type | |
548
555
  |---|---|---|
package/docs-user/dock.md CHANGED
@@ -127,6 +127,22 @@ split resizes, and host resizes; handles are hidden during dock drags.
127
127
  stored policy; `clearLeafPlacementPolicy(name)` resets. `getLeafPlacementPolicy(name)`
128
128
  returns the resolved `{ allowKinds, denyKinds, allowEdgeSplit }`.
129
129
 
130
+ - Host `isolated` attribute: panels can be rearranged freely *inside* this
131
+ host but cannot be dragged **out** to another host. It is checked on the
132
+ **source** host only — it does not stop other hosts dropping panels *into*
133
+ an isolated host. To keep unrelated docks from exchanging panels in both
134
+ directions use `family` (below).
135
+ - Host `family` attribute (default `""`): two hosts exchange panels by drag
136
+ only when their families are equal. Give an inner dock its own family and
137
+ it neither receives outer-dock panels nor lets its own leave.
138
+ - Host `accepts-kinds` attribute / `acceptsKinds` property (space-separated
139
+ panel kinds; default unrestricted): panels arriving from **other** hosts, or
140
+ from another window via `queryDropTarget` / `acceptExternalPanel`, must be of
141
+ one of these kinds. Panels already in the host are unaffected. Combine with
142
+ `family` and `isolated` for a closed two-way exchange of one kind.
143
+ - A host whose `accepts` allows no splits (e.g. `accepts="tabs"`) treats the
144
+ whole leaf as the tabs drop zone, instead of leaving the edge bands inert.
145
+
130
146
  A zone lights up only if every applicable rule allows it; disallowed zones
131
147
  are inert. Content rules govern tabbing *into* a leaf (`tabs` drops,
132
148
  `addPanel`, re-homing). Edge splits beside a leaf stay allowed **unless** that
@@ -138,11 +154,56 @@ propagated to it. Programmatic fallbacks (`addPanel` without a target,
138
154
  external panels, leaf-close re-homing) use the first stack admitting the
139
155
  panel's kind.
140
156
 
157
+ ## Hiding the strip of a lone tab
158
+
159
+ `hide-single-tab-strip` (host attribute, `hideSingleTabStrip` property) drops
160
+ a leaf's tab strip while it holds exactly one panel; the strip appears when a
161
+ second panel arrives and goes again when one leaves. `DockStackNode.hideSingleTabStrip`
162
+ overrides it per stack (serialized with the layout). The leaf's outer box is
163
+ fixed by its split, so only the body shifts by one tab height when the strip
164
+ appears — the strip never reserves height while hidden.
165
+
166
+ While hidden:
167
+
168
+ - **Drops.** During a drag the host overlays a compact stand-in strip (part
169
+ `drop-hint`) on the leaf — only if that leaf would accept the dragged panel —
170
+ with a caret showing the insertion gap. It takes no layout space. Dropping on
171
+ it, or on the leaf centre, adds a tab; the real strip then appears.
172
+ - **Dragging the lone panel.** There is no tab to grab, so call
173
+ `host.beginPanelDrag(panelId, pointerEvent)` from your own handle's
174
+ `pointerdown`. It returns `false` for an unknown panel or non-primary press.
175
+ The drag starts after a few pixels of movement and is identical to a tab
176
+ drag (ghost, drop zones, `dock-panel-drag-*`, cross-host adoption,
177
+ tear-out). Prefer `touch-action: none` and `user-select: none` on the handle.
178
+ - **Toolbars.** Panel toolbars and `toolbar-<name>` slots live in the strip, so
179
+ they are not shown while it is hidden.
180
+
141
181
  ## Events
142
182
 
143
183
  `ixfx-dock-host`: `dock-change` (`{ layout }` after structural mutations),
144
184
  `dock-panel-added`, `dock-panel-removed`, `dock-panel-activated`,
145
- `dock-panel-drag-start`.
185
+ `dock-panel-drag-start`, `dock-panel-moved`, `dock-stack-empty`,
186
+ `dock-tab-contextmenu`.
187
+
188
+ - `dock-panel-added` — `{ panelId, sourceHost, targetHost }`. `sourceHost` is
189
+ the host the panel came from (`null` when new or from another window).
190
+ - `dock-panel-removed` — `{ panelId, sourceHost, targetHost }`. `targetHost` is
191
+ the host it moved to, `null` when it is gone (closed, detached, torn out).
192
+ - `dock-panel-moved` — `{ panelId, sourceHost, targetHost }`, fired on the
193
+ target host once a cross-host drop completes. A move therefore fires
194
+ `removed` (source), `added` (target), then `moved` (target), and the source
195
+ now also emits `dock-change`.
196
+ - `dock-stack-empty` — `{ name, hostEmpty }` when a stack loses its last panel
197
+ (`name` is the container name or `null`). `hostEmpty` is true when the host
198
+ holds no panels at all — the cue to close an owning group. Fires before the
199
+ matching `dock-change`; not fired by `loadLayout()`.
200
+ - `dock-tab-contextmenu` — `{ panelId, clientX, clientY }`, cancelable
201
+ (`preventDefault()` suppresses the native menu). Visible tabs only; with a
202
+ hidden strip handle `contextmenu` on your own element.
203
+
204
+ All of these bubble and are composed, so a nested host's events also reach
205
+ outer hosts and their listeners — compare `targetHost` / `sourceHost` to the
206
+ host you care about. A nested host's drags are owned by the nearest host only.
146
207
 
147
208
  Cross-window (see [Cross-window docking](#cross-window-docking-electron-etc)):
148
209
  `dock-panel-drag-out` / `dock-panel-drag-in`
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
- "version": "0.15.3",
4
- "generated": "2026-10-18T19:35:32.045Z",
3
+ "version": "0.15.5",
4
+ "generated": "2026-10-23T07:10:01.881Z",
5
5
  "components": [
6
6
  {
7
7
  "name": "ac-text",
@@ -228,6 +228,11 @@
228
228
  "summary": "A Lit web component that lays out any number of panels along one axis with draggable gutters between them.",
229
229
  "path": "docs-user/split-layout.md"
230
230
  },
231
+ {
232
+ "name": "sunburst-tree",
233
+ "summary": "`<ixfx-sunbursttree-list>` — a zoomable sunburst over weighted tree data. The zoom node is the centre disc; each ring outward is one generation, and each arc's angle is its share of its parent's value",
234
+ "path": "docs-user/sunburst-tree.md"
235
+ },
231
236
  {
232
237
  "name": "swipe",
233
238
  "summary": "A touch-friendly swipe/nudge component. User taps and drags to generate a value in range [-1, 1]. Value is emitted only on pointer release.",
@@ -56,6 +56,7 @@ import '@ixfx/components/data-grid';
56
56
  - [snackbar](docs-user/snackbar.md): A small floating toolbar anchored in visual proximity to another element on the page (the "target"), built on the native Popover and CSS Anchor Positioning APIs.
57
57
  - [snap-container](docs-user/snap-container.md): A container that positions its child element at configurable snap positions along an axis. The child can be dragged, clicked to snap, or controlled via keyboard.
58
58
  - [split-layout](docs-user/split-layout.md): A Lit web component that lays out any number of panels along one axis with draggable gutters between them.
59
+ - [sunburst-tree](docs-user/sunburst-tree.md): `<ixfx-sunbursttree-list>` — a zoomable sunburst over weighted tree data. The zoom node is the centre disc; each ring outward is one generation, and each arc's angle is its share of its parent's value
59
60
  - [swipe](docs-user/swipe.md): A touch-friendly swipe/nudge component. User taps and drags to generate a value in range [-1, 1]. Value is emitted only on pointer release.
60
61
  - [tabs](docs-user/tabs.md): A complete tabbed interface system using several coordinated components:
61
62
  - [text-shimmer](docs-user/text-shimmer.md): A label with a sweeping highlight band across the text. Useful as a lightweight "still working…" or "loading…" indicator.
@@ -0,0 +1,193 @@
1
+ # Sunburst Tree
2
+
3
+ `<ixfx-sunbursttree-list>` — a zoomable sunburst over weighted tree data. The zoom node is the centre disc; each ring outward is one generation, and each arc's angle is its share of its parent's value. Every change — data arriving, a different weighting, a zoom — animates from what's on screen to the new layout.
4
+
5
+ It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](../tree/README.md)), so `TreeDataModel`, `IncrSearchTreeController`, and `TreeComponent`-typed code work unmodified. Geometrically it is a polar [icicle](../icicle-tree/), and shares its API shape.
6
+
7
+ ---
8
+
9
+ ## Contents
10
+
11
+ 1. [Differences from `ixfx-tree-list`](#differences-from-ixfx-tree-list)
12
+ 2. [Data model](#data-model)
13
+ 3. [Values and weighting](#values-and-weighting)
14
+ 4. [Progressive data](#progressive-data)
15
+ 5. [Colour](#colour)
16
+ 6. [Properties](#properties)
17
+ 7. [Zoom, selection, navigation](#zoom-selection-navigation)
18
+ 8. [Events](#events)
19
+ 9. [Keyboard / pointer interaction](#keyboard--pointer-interaction)
20
+ 10. [CSS variables and parts](#css-variables-and-parts)
21
+
22
+ ---
23
+
24
+ ## Differences from `ixfx-tree-list`
25
+
26
+ - **Expand/collapse is reinterpreted as zoom.** `expand` fires on zoom-in, `collapse` on zoom-out.
27
+ - **`interactionMode` drops `'checked'`**; **`exclusivity` is accepted but ignored**.
28
+ - **`filterPredicate` dims** non-matching arcs instead of hiding them.
29
+ - **Reassigning `root` keeps zoom and selection** wherever the same key paths still exist (the icicle and treemap reset instead). See [Progressive data](#progressive-data).
30
+ - Drag-and-drop is out of scope.
31
+
32
+ ## Data model
33
+
34
+ Same `TreeNode` / `TreeItem` shapes as the rest of the tree family, including `root`, `model` and `loadChildren` for lazy loading (children are fetched for every visible arc).
35
+
36
+ Keys only need to be unique **among siblings**: arcs are identified by their key *path* from the root, so file-system data — where `readme.md` recurs in many folders — works. Lookups that only have a bare key (`revealKey`) match the first node, depth-first, carrying it.
37
+
38
+ ## Values and weighting
39
+
40
+ Every node — branches included — has a value. A child's arc is its share of its parent's:
41
+
42
+ ```
43
+ childAngle = parentAngle × child / max(parent, Σ siblings)
44
+ ```
45
+
46
+ - Children summing to **less** than their parent leave an unfilled gap — "unaccounted for" space (unscanned disk, an unknown remainder).
47
+ - Children summing to **more** are scaled down to fit.
48
+
49
+ Give values as a plain number on `item.weight` (the default accessor reads it), or as a function:
50
+
51
+ ```typescript
52
+ el.root = {
53
+ item: { key: 'hd', label: 'Disk', weight: 500e9 },
54
+ children: [
55
+ { item: { key: 'apps', label: 'Applications', weight: 120e9 }, children: [] },
56
+ { item: { key: 'users', label: 'Users', weight: 300e9 }, children: [] },
57
+ ],
58
+ };
59
+
60
+ // Swap weighting scheme: no DOM is rebuilt, the chart animates to the new layout.
61
+ el.valueAccessor = (item, node) => (item.data as Stats).fileCount;
62
+ ```
63
+
64
+ `valueAccessor` is `(item, node) => number | undefined`. When it returns `undefined` (or a negative / non-finite number) the node falls back to the **sum of its loaded children**, or `1` for a leaf or not-yet-loaded node. So unweighted data shows descendant counts, and an accessor can value only leaves and let branches sum:
65
+
66
+ ```typescript
67
+ el.valueAccessor = (item, node) => node.children ? undefined : Math.log2(bytes(item) + 1);
68
+ ```
69
+
70
+ `valueFormatter` (`(value, node) => string`) formats the value in the centre readout:
71
+
72
+ ```typescript
73
+ el.valueFormatter = v => `${(v / 1e9).toFixed(1)} GB`;
74
+ ```
75
+
76
+ ## Progressive data
77
+
78
+ Any update re-lays out and animates: new arcs grow from zero width, removed ones shrink away, the rest slide to their new angles. An update arriving mid-animation retargets from wherever things currently are.
79
+
80
+ Either rebuild the tree immutably and reassign it:
81
+
82
+ ```typescript
83
+ el.root = snapshotOfScanSoFar(); // zoom + selection carry over by key path
84
+ ```
85
+
86
+ or mutate in place and nudge the element — or let a `TreeDataModel` notify it:
87
+
88
+ ```typescript
89
+ folder.children.push(newFile);
90
+ el.requestUpdate();
91
+ ```
92
+
93
+ ## Colour
94
+
95
+ Hue is set by the ring around the centre: each first-ring arc takes the hue at its angular midpoint (so the ring spans the colour wheel), and every descendant takes a tone of that hue — lighter and less saturated each ring out, with a small per-sibling shift so neighbours stay distinct. Zooming re-anchors hues to the new first ring, cross-fading as it goes. The centre disc mixes towards `--sunbursttree-center-color`.
96
+
97
+ All of it is computed in CSS from per-arc custom properties, so the palette is themeable:
98
+
99
+ | Per-arc property | Value |
100
+ |---|---|
101
+ | `--arc-hue` | Degrees, added to `--sunbursttree-hue-start` |
102
+ | `--arc-depth` | Ring index from the centre (`0` = first ring) |
103
+ | `--arc-shade` | Sibling-position offset, `-0.5..0.5` |
104
+ | `--arc-centre` | `0..1`, how far the arc has become the centre disc |
105
+
106
+ ## Properties
107
+
108
+ | Property (attribute) | Type | Default | Description |
109
+ |---|---|---|---|
110
+ | `root` | `TreeNode \| undefined` | `undefined` | Root node of the tree |
111
+ | `model` | `TreeDataModel \| undefined` | `undefined` | Shared data model; takes precedence over `loadChildren` |
112
+ | `loadChildren` | `LoadChildrenCallback \| undefined` | `undefined` | Callback for lazy child loading |
113
+ | `valueAccessor` | `SunburstValueAccessor` | `item => item.weight` | Node value. See [Values and weighting](#values-and-weighting) |
114
+ | `valueFormatter` | `SunburstValueFormatter` | locale number | Formats the centre readout's value |
115
+ | `interactionMode` (`interaction-mode`) | `'implicit' \| 'standard' \| 'manual'` | `'standard'` | Gesture mapping |
116
+ | `selectionFilter` (`selection-filter`) | `'none' \| 'leaf' \| 'branch' \| 'all'` | `'all'` | Which node kinds can be selected |
117
+ | `maxDepth` (`max-depth`) | `number` | `5` | Rings drawn around the centre |
118
+ | `centerRatio` (`center-ratio`) | `number` | `0.22` | Centre disc radius as a fraction of the overall radius |
119
+ | `minArcLength` (`min-arc-length`) | `number` | `1.5` | Arcs whose outer edge is shorter than this (px) aren't drawn — bounds DOM size for big trees |
120
+ | `disableLabels` (`disable-labels`) | `boolean` | `false` | Hide in-arc labels (the centre readout remains) |
121
+ | `curvedLabels` (`curved-labels`) | `boolean` | `false` | Curve labels along their ring instead of drawing them straight. See below |
122
+ | `animationDuration` (`animation-duration`) | `number` | `600` | Transition length in ms; `0` snaps. Respects `prefers-reduced-motion` |
123
+ | `filterPredicate` | `TreeFilterPredicate \| undefined` | `undefined` | Non-matching arcs (with no matching descendant) are dimmed |
124
+ | `disableKeyboardNavigation` (`disable-keyboard-navigation`) | `boolean` | `false` | Turn off keyboard handling |
125
+ | `searchModes` (`search-modes`) | `string \| undefined` | `undefined` | Subset of `navigate select filter` — see [`docs/infra-incremental.md`](../../docs/infra-incremental.md) |
126
+ | `exclusivity` | `TreeExclusivity` | `'none'` | Accepted for interface compatibility, ignored |
127
+ | `selectedNode` / `selectedNodes` | | | Same contract as `ixfx-tree-list` |
128
+ | `focusedNode` | `TreeNode \| undefined` (read-only) | `undefined` | Current zoom node; `undefined` when showing the full tree |
129
+
130
+ In-arc labels run along the ring when the arc is long enough, otherwise radially (truncated to the ring width); both flip to stay upright. With `curved-labels`, ring-following labels bend to the arc (an SVG `<textPath>` along the ring's middle, reversed in the bottom half so it reads upright). Since the whole arc length is usable, long labels are truncated with `…` rather than dropped; arcs too narrow for a few characters still fall back to straight radial text. Arcs too thin for either are unlabelled — hover or keyboard-cursor them and the centre readout shows their label, value and share of the centre.
131
+
132
+ ## Zoom, selection, navigation
133
+
134
+ ```typescript
135
+ el.zoomTo(node); // node becomes the centre
136
+ el.zoomUp(); // centre's parent becomes the centre
137
+ el.zoomOut(); // back to the full tree
138
+
139
+ el.select(node); el.deselect(node); el.clearSelection(); el.selectAll(); el.deselectAll();
140
+
141
+ // Zooms so the target sits in the first ring (its parent at the centre), then selects it.
142
+ el.navigateTo(['users', 'me', 'photos']);
143
+ el.navigateToNode(n => n.item.key === 'photos');
144
+
145
+ el.getNodes(); // visible arcs (centre excluded), ring-major
146
+ ```
147
+
148
+ ## Events
149
+
150
+ All events **bubble** and are **composed**. `depth` counts from the root's children (`0`).
151
+
152
+ | Event | Detail | When |
153
+ |---|---|---|
154
+ | `zoom` | `SunburstZoomDetail` — `{ node: TreeNode \| undefined; depth }` | Zoom node changes (`node` undefined = full tree) |
155
+ | `expand` / `collapse` | `{ node; depth }` | Zoomed into a descendant / out of the previous centre |
156
+ | `select` | `TreeSelectDetail` | Selection changes |
157
+ | `tickle` | `TreeTickleDetail` | Pointer or keyboard cursor enters an arc |
158
+ | `activate` | `TreeActivateDetail` | Enter or double-click |
159
+ | `item-click` / `item-dblclick` | `{ node; depth }` | Raw pointer clicks on an arc |
160
+
161
+ ## Keyboard / pointer interaction
162
+
163
+ | Input | Action |
164
+ |---|---|
165
+ | Click an arc | Select (per `interactionMode`) and zoom in, if it has children |
166
+ | Ctrl/⌘-click, Shift-click | Toggle / range-select (`standard` mode), without zooming |
167
+ | Click the centre | Zoom up one level |
168
+ | `←` / `→` | Previous / next sibling (wraps) |
169
+ | `↑` / `↓` | Parent (inward) / first child (outward) |
170
+ | `Home` / `End` | First / last visible arc |
171
+ | `Enter` | Select, activate and zoom in |
172
+ | `Escape` / `Backspace` | Zoom up one level |
173
+
174
+ ## CSS variables and parts
175
+
176
+ | Variable | Default | Description |
177
+ |---|---|---|
178
+ | `--sunbursttree-hue-start` | `250` | Hue at 12 o'clock |
179
+ | `--sunbursttree-chroma` | `0.14` | First-ring chroma |
180
+ | `--sunbursttree-chroma-step` | `0.015` | Chroma lost per ring outward |
181
+ | `--sunbursttree-lightness` | `60%` | First-ring lightness |
182
+ | `--sunbursttree-lightness-step` | `5%` | Lightness gained per ring outward |
183
+ | `--sunbursttree-shade-step` | `10%` | Lightness spread across siblings |
184
+ | `--sunbursttree-center-color` | `var(--surface-4)` | Centre disc fill |
185
+ | `--sunbursttree-center-text` | `var(--surface-4-text)` | Centre readout text |
186
+ | `--sunbursttree-gap-color` | `var(--surface-2)` | Stroke between arcs — match it to the background |
187
+ | `--sunbursttree-gap` | `1px` | Width of that stroke |
188
+ | `--sunbursttree-font-size` | `11px` | Label size (px; read for label fitting) |
189
+ | `--sunbursttree-label-lightness` | `22%` | Label lightness (hue follows the arc) |
190
+
191
+ Parts: `arc`, `center`, `label`, `center-readout`.
192
+
193
+ The host defaults to `aspect-ratio: 1` with a `200px` minimum height; give it a width (or both dimensions) to size it.
package/docs-user/tree.md CHANGED
@@ -37,7 +37,7 @@ type TreeItem = {
37
37
  icon?: string; // Icon name (passed to <ixfx-icon>)
38
38
  tooltip?: string; // Tooltip content
39
39
  value?: string; // Optional machine-readable value
40
- weight?: number; // Relative importance/magnitude; only ixfx-hyperbolictree-list uses it today
40
+ weight?: number; // Relative importance/magnitude; used by ixfx-hyperbolictree-list and ixfx-sunbursttree-list
41
41
  };
42
42
 
43
43
  type TreeNode = {
@@ -153,6 +153,7 @@ These properties are present on all three components (`ixfx-tree-list`, `ixfx-mi
153
153
  | `stickyHeaders` (`sticky-headers`) | `boolean` | `true` | VS Code-style sticky headers; set `sticky-headers="false"` to opt out (`ixfx-tree-list` only) |
154
154
  | `virtualize` | `'auto' \| 'on' \| 'off'` | `'auto'` | Render only the rows near the viewport; `auto` switches on above `virtualizeThreshold` visible rows (`ixfx-tree-list` only). See [Virtualisation](#virtualisation-ixfx-tree-list) |
155
155
  | `virtualizeThreshold` (`virtualize-threshold`) | `number` | `200` | Visible-row count above which `virtualize="auto"` windows the list |
156
+ | `animation` | `'auto' \| 'always' \| 'never'` | `'auto'` | Controls row enter/leave animations. `'auto'` respects `prefers-reduced-motion`; `'always'` always animates; `'never'` skips all animations. Can be overridden per-call on `expandAll`, `collapseAll`, `navigateTo`, `revealKey`, and `navigateToNode` |
156
157
 
157
158
  ### Replacing `root`
158
159
 
@@ -280,6 +281,44 @@ On by default: while scrolling vertically, the ancestors of the top-visible row
280
281
  - An ancestor that is itself still visible is never duplicated in the header.
281
282
  - Style via `--tree-sticky-bg`, `--tree-sticky-border`, `--tree-sticky-shadow`, and the `sticky-headers` / `sticky-header` parts.
282
283
 
284
+ ### Animation control
285
+
286
+ Row enter/leave animations are controlled by the `animation` property:
287
+
288
+ | Value | Behaviour |
289
+ |-------|-----------|
290
+ | `'auto'` (default) | Animate normally, respecting `prefers-reduced-motion` and the `MAX_ANIMATED_ROWS` (50) threshold |
291
+ | `'always'` | Always animate, even when many rows change at once |
292
+ | `'never'` | Skip all row enter/leave animations |
293
+
294
+ ```html
295
+ <ixfx-tree-list animation="never"></ixfx-tree-list>
296
+ ```
297
+
298
+ **Per-call override**: Methods that change the tree structure accept an optional `animate` parameter to override the property for that single call:
299
+
300
+ ```typescript
301
+ // Expand with animation (default)
302
+ tree.expandAll();
303
+
304
+ // Expand without animation
305
+ tree.expandAll(undefined, false);
306
+
307
+ // Collapse without animation
308
+ tree.collapseAll(0, false);
309
+
310
+ // Navigate with animation
311
+ tree.navigateTo(['root', 'child']);
312
+
313
+ // Navigate without animation
314
+ tree.navigateTo(['root', 'child'], { animate: false });
315
+
316
+ // Reveal without animation
317
+ tree.revealKey('some-key', false);
318
+ ```
319
+
320
+ This is useful when populating a tree initially or when performance matters more than visual polish.
321
+
283
322
  ### Virtualisation (`ixfx-tree-list`)
284
323
 
285
324
  A node with 10,000 children needs only about 25 `<li>` elements in the DOM. When
package/llms.txt CHANGED
@@ -56,6 +56,7 @@ import '@ixfx/components/data-grid';
56
56
  - [snackbar](docs-user/snackbar.md): A small floating toolbar anchored in visual proximity to another element on the page (the "target"), built on the native Popover and CSS Anchor Positioning APIs.
57
57
  - [snap-container](docs-user/snap-container.md): A container that positions its child element at configurable snap positions along an axis. The child can be dragged, clicked to snap, or controlled via keyboard.
58
58
  - [split-layout](docs-user/split-layout.md): A Lit web component that lays out any number of panels along one axis with draggable gutters between them.
59
+ - [sunburst-tree](docs-user/sunburst-tree.md): `<ixfx-sunbursttree-list>` — a zoomable sunburst over weighted tree data. The zoom node is the centre disc; each ring outward is one generation, and each arc's angle is its share of its parent's value
59
60
  - [swipe](docs-user/swipe.md): A touch-friendly swipe/nudge component. User taps and drags to generate a value in range [-1, 1]. Value is emitted only on pointer release.
60
61
  - [tabs](docs-user/tabs.md): A complete tabbed interface system using several coordinated components:
61
62
  - [text-shimmer](docs-user/text-shimmer.md): A label with a sweeping highlight band across the text. Useful as a lightweight "still working…" or "loading…" indicator.