@ixfx/components 0.15.5 → 0.16.1
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 +986 -49
- package/bundle/index.js +765 -729
- package/dist/ac-token.js +1 -1
- package/dist/crumb-navigation-BbqfmkYs.d.ts +147 -0
- package/dist/crumb-navigation-YnFVFqrx.js +1290 -0
- package/dist/crumbs.d.ts +2 -115
- package/dist/crumbs.js +1 -1179
- package/dist/{data-grid-C1sZX6n9.js → data-grid-Cqxuwgbb.js} +615 -210
- package/dist/{data-grid-D7qzmMMp.d.ts → data-grid-Csh5EJW9.d.ts} +55 -4
- package/dist/data-grid.d.ts +3 -3
- package/dist/data-grid.js +1 -1
- package/dist/{detail-list-q8bs67BM.js → detail-list-Bo5ze3SN.js} +234 -9
- package/dist/{detail-list-CKIKlszv.d.ts → detail-list-DSZ1DDor.d.ts} +48 -4
- package/dist/detail-list.d.ts +4 -4
- package/dist/detail-list.js +1 -1
- package/dist/{flat-list-DXMyDiSH.d.ts → flat-list-CT9kX3TQ.d.ts} +27 -4
- package/dist/flat-list.d.ts +2 -2
- package/dist/flat-list.js +123 -4
- package/dist/{grid-list-y6dWJYu3.js → grid-list-D0dJNVEF.js} +278 -76
- package/dist/{grid-list-0e7snW6t.d.ts → grid-list-DDLrWRxm.d.ts} +128 -22
- package/dist/grid-list.d.ts +3 -3
- package/dist/grid-list.js +1 -1
- package/dist/{incr-search-JIhwB49y.js → incr-search-CgbL-OlS.js} +140 -10
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +1 -1
- package/dist/index-9ttcdwlL.d.ts +71 -0
- package/dist/index-Bnm-kKGC.d.ts +475 -0
- package/dist/{index-KN2MW_xD.d.ts → index-CUErGhCg.d.ts} +31 -4
- package/dist/{index-DEVyVIn8.d.ts → index-D-lZNlHa.d.ts} +52 -6
- package/dist/index.d.ts +341 -21
- package/dist/index.js +2286 -884
- package/dist/{list-native-drag-controller-O2Nruqpi.d.ts → list-native-drag-controller-D_UrXipS.d.ts} +1 -1
- package/dist/{list-selection-controller-BeE70lbB.js → list-selection-controller-5L0Nj9tR.js} +87 -2
- package/dist/miller-DUqiUAWQ.js +2230 -0
- package/dist/miller.d.ts +4 -437
- package/dist/miller.js +1 -1815
- package/dist/narrowed-text.js +1 -1
- package/dist/pending-BGidHppL.js +91 -0
- package/dist/scroll-hover-guard-Cjw6179Q.js +35 -0
- package/dist/{split-layout-JK3xWVX2.d.ts → split-layout-CophkNeU.d.ts} +41 -3
- package/dist/{split-layout-Deoxy47d.js → split-layout-DTY4PJRp.js} +251 -19
- package/dist/split-layout.d.ts +1 -1
- package/dist/split-layout.js +1 -1
- package/dist/sunburst-tree.d.ts +2 -2
- package/dist/sunburst-tree.js +1 -1
- package/dist/{sunbursttree-list-yXwS-uEI.js → sunbursttree-list-B_M0YTEA.js} +299 -80
- package/dist/{sunbursttree-list--Z0uyh8O.d.ts → sunbursttree-list-DePuFYlU.d.ts} +40 -3
- package/dist/tree-CftHwtEb.js +146 -0
- package/dist/{tree-D8VxvR9u.js → tree-DAlgvaHB.js} +276 -48
- package/dist/{tree-component-C6UHO-w9.d.ts → tree-component-DgfkxmVY.d.ts} +19 -8
- package/dist/tree.d.ts +3 -3
- package/dist/tree.js +1 -1
- package/dist/types-LU1u0CB8.d.ts +87 -0
- package/dist/vertical-list-DyKPKGxK.js +1993 -0
- package/dist/{vertical-list-D8MksoTp.d.ts → vertical-list-SAx56U-l.d.ts} +31 -3
- package/dist/vertical-list.d.ts +3 -3
- package/dist/vertical-list.js +1 -1789
- package/dist/view-state-controller-BdyXWUUM.js +305 -0
- package/docs-user/README.md +2 -0
- package/docs-user/crumbs.md +27 -0
- package/docs-user/data-grid.md +79 -5
- package/docs-user/detail-list.md +69 -0
- package/docs-user/dock.md +63 -12
- package/docs-user/flat-list.md +27 -0
- package/docs-user/grid-list.md +69 -13
- package/docs-user/hyperbolic-tree.md +31 -4
- package/docs-user/icicle-tree.md +36 -0
- package/docs-user/index.json +12 -2
- package/docs-user/llms.txt +2 -0
- package/docs-user/miller.md +52 -21
- package/docs-user/multicol-list.md +50 -8
- package/docs-user/split-layout.md +45 -17
- package/docs-user/sunburst-tree.md +32 -2
- package/docs-user/tree.md +45 -6
- package/docs-user/treemap.md +32 -0
- package/docs-user/twosplit-layout.md +22 -0
- package/docs-user/user-catalog.md +1 -1
- package/docs-user/vertical-list.md +55 -7
- package/llms.txt +2 -0
- package/package.json +2 -2
package/docs-user/dock.md
CHANGED
|
@@ -63,7 +63,7 @@ The tree is plain and mutable, owned exclusively by the host.
|
|
|
63
63
|
|
|
64
64
|
Named containers: give a stack leaf `name` (e.g. `"primary-sidebar"`) in the
|
|
65
65
|
initial layout JSON. Names must be unique per layout (first match wins),
|
|
66
|
-
|
|
66
|
+
are saved and restored with the [view state](#view-state), and render as
|
|
67
67
|
`data-dock-name` on the leaf. Explicit leaf close still removes the stack.
|
|
68
68
|
When a `tabs` drop empties a named stack into an *unnamed* one, the name
|
|
69
69
|
(and its content rules) follows the panels to the merged leaf instead of
|
|
@@ -117,7 +117,7 @@ split resizes, and host resizes; handles are hidden during dock drags.
|
|
|
117
117
|
`setLeafExclusiveContents(name, kinds)` admits only those kinds into the
|
|
118
118
|
named leaf, `setLeafDenyContents(name, kinds)` refuses those kinds.
|
|
119
119
|
`clearLeafContentRestrictions(name)` lifts both; `[]` seals (exclusive) or
|
|
120
|
-
lifts (deny). Rules are host policy — kept across `
|
|
120
|
+
lifts (deny). Rules are host policy — kept across `setViewState()`, never
|
|
121
121
|
serialized; already-docked panels are grandfathered.
|
|
122
122
|
- Leaf placement policy — `setLeafPlacementPolicy(name, policy)` is a superset
|
|
123
123
|
of the two calls above (`allowKinds` / `denyKinds`) plus `allowEdgeSplit`
|
|
@@ -181,7 +181,7 @@ While hidden:
|
|
|
181
181
|
## Events
|
|
182
182
|
|
|
183
183
|
`ixfx-dock-host`: `dock-change` (`{ layout }` after structural mutations),
|
|
184
|
-
`dock-panel-added`, `dock-panel-removed`, `dock-panel-activated`,
|
|
184
|
+
`view-state-change` (debounced, see [View state](#view-state)), `dock-panel-added`, `dock-panel-removed`, `dock-panel-activated`,
|
|
185
185
|
`dock-panel-drag-start`, `dock-panel-moved`, `dock-stack-empty`,
|
|
186
186
|
`dock-tab-contextmenu`.
|
|
187
187
|
|
|
@@ -196,7 +196,7 @@ While hidden:
|
|
|
196
196
|
- `dock-stack-empty` — `{ name, hostEmpty }` when a stack loses its last panel
|
|
197
197
|
(`name` is the container name or `null`). `hostEmpty` is true when the host
|
|
198
198
|
holds no panels at all — the cue to close an owning group. Fires before the
|
|
199
|
-
matching `dock-change`; not fired by `
|
|
199
|
+
matching `dock-change`; not fired by `setViewState()`.
|
|
200
200
|
- `dock-tab-contextmenu` — `{ panelId, clientX, clientY }`, cancelable
|
|
201
201
|
(`preventDefault()` suppresses the native menu). Visible tabs only; with a
|
|
202
202
|
hidden strip handle `contextmenu` on your own element.
|
|
@@ -226,8 +226,11 @@ The host consumes `ixfx-split-layout` events: `ixfx-split-resize` /
|
|
|
226
226
|
## Public API — `ixfx-dock-host`
|
|
227
227
|
|
|
228
228
|
```ts
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
getViewState(options?): DockHostViewState // { v, kind: 'dock-host', layout }; folds live split weights
|
|
230
|
+
setViewState(state: Partial<DockHostViewState>): void
|
|
231
|
+
clearPendingViewState(): void // drops placeholders (panels the host doesn't hold)
|
|
232
|
+
saveLayout(): DockLayout // deprecated alias of getViewState().layout
|
|
233
|
+
loadLayout(layout: DockLayout): void // deprecated alias of setViewState({ layout })
|
|
231
234
|
addPanel(panel: DockPanelElement | DockPanelInit, target?: DockAddPanelTarget): boolean
|
|
232
235
|
type DockAddPanelTarget = {
|
|
233
236
|
container?: string; // named stack leaf — takes precedence over `stack`
|
|
@@ -258,6 +261,7 @@ getNeighbours(leaf: DockLeafRef): DockNeighbours | null
|
|
|
258
261
|
moveLeaf(leaf: DockLeafRef, direction: 'up' | 'down' | 'left' | 'right'): boolean
|
|
259
262
|
moveLeafToEdge(leaf: DockLeafRef, edge: 'top' | 'bottom' | 'left' | 'right'): boolean
|
|
260
263
|
canAccept?: (panelId: string, op: DockOperation) => boolean
|
|
264
|
+
placeholderLabel?: (panelId: string) => string // tab label for placeholders (default: the id)
|
|
261
265
|
|
|
262
266
|
// Cross-window hooks (serializable in/out; no element handoff)
|
|
263
267
|
getPanelSnapshot(panelId: string): DockPanelSnapshot | null
|
|
@@ -427,13 +431,60 @@ it can measure.
|
|
|
427
431
|
window. `acceptExternalPanel` returns `null` if that id is already docked in
|
|
428
432
|
the target host (adopt / focus it yourself instead).
|
|
429
433
|
|
|
430
|
-
##
|
|
434
|
+
## View state
|
|
431
435
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
436
|
+
The dock implements the shared view-state API
|
|
437
|
+
([docs/infra-view-state.md](../../docs/infra-view-state.md)). The component
|
|
438
|
+
never persists anything; the host does, e.g. with `bindViewState()`:
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
import { bindViewState } from '@ixfx/components';
|
|
442
|
+
|
|
443
|
+
bindViewState(dock, {
|
|
444
|
+
load: () => JSON.parse(localStorage.getItem(`workspace`) ?? `null`),
|
|
445
|
+
save: s => localStorage.setItem(`workspace`, JSON.stringify(s)),
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
| Field | Type | Meaning |
|
|
450
|
+
|---|---|---|
|
|
451
|
+
| `layout` | `DockLayout` | The whole tree: splits with `sizes`, stacks with tab order, `active`, `name`, `collapsed` / `preCollapseSize`, strip flags. Keyed by panel id. |
|
|
452
|
+
|
|
453
|
+
The dock layout is close to structure, so the saved state *is* the tree
|
|
454
|
+
(collapse and the active tab stay inside it rather than in separate
|
|
455
|
+
`expanded` / `selected` fields).
|
|
456
|
+
|
|
457
|
+
- `getViewState()` folds live gutter weights from each rendered
|
|
458
|
+
`ixfx-split-layout` (via `getSizes()`) into `DockSplitNode.sizes` first, so
|
|
459
|
+
a save → drag → save round-trip preserves user adjustments.
|
|
460
|
+
- **Panels not in the host yet.** `setViewState()` keeps every panel id the
|
|
461
|
+
layout names, held or not. One the host doesn't hold is a *placeholder*:
|
|
462
|
+
it keeps its place in the tree (shown as a tab labelled by the
|
|
463
|
+
`placeholderLabel(panelId)` property, or its id when unset) and
|
|
464
|
+
the panel slots into that place when it arrives — appended as a light-DOM
|
|
465
|
+
child, `addPanel()` **without** a target, `acceptExternalPanel()` without
|
|
466
|
+
`at`, or a cross-host adoption. An explicit `addPanel()` target still wins.
|
|
467
|
+
Placeholders are saved by `getViewState()`; `includePending: false` leaves
|
|
468
|
+
them out (stacks they empty are dropped unless named). Remove one with
|
|
469
|
+
`removePanel(id)`, or all of them with `clearPendingViewState()`.
|
|
470
|
+
- **Persisting with the default keeps stale ids.** A placeholder for a panel
|
|
471
|
+
that no longer exists is saved again on every `getViewState()`, so it never
|
|
472
|
+
goes away. Apps whose panels can disappear between save and restore should
|
|
473
|
+
call `clearPendingViewState()` once they have added the panels they hold,
|
|
474
|
+
or save with `includePending: false`.
|
|
475
|
+
- Pooled panels the layout doesn't name are appended to the first stack
|
|
476
|
+
admitting their kind.
|
|
477
|
+
- A panel that is closed / removed (`removePanel`, close button, removed from
|
|
478
|
+
the DOM, moved to another host) leaves the layout — closing is a structural
|
|
479
|
+
change, so its place is not remembered.
|
|
480
|
+
- State set before the host is connected wins over the `layout` attribute.
|
|
481
|
+
- A malformed `layout` (or a state for another `kind`) is ignored with a
|
|
482
|
+
warning.
|
|
483
|
+
- `view-state-change` fires (debounced) after structural changes (drops,
|
|
484
|
+
add / remove, close, leaf moves), gutter resizes, collapse / expand and
|
|
485
|
+
tab activation. `reason` is `data` when the pool changed under the host,
|
|
486
|
+
`restore` for `setViewState()`. The dock's internal split layouts' own
|
|
487
|
+
`view-state-change` events don't escape the host.
|
|
437
488
|
|
|
438
489
|
## CSS variables
|
|
439
490
|
|
package/docs-user/flat-list.md
CHANGED
|
@@ -51,6 +51,7 @@ A node counts as a branch unless `item.isLeaf` is `true` or its `children` is `[
|
|
|
51
51
|
| `beginEdit(key)` | Rename a row in place. Needs `validateEdit` and `item.editable`; `false` otherwise |
|
|
52
52
|
| `registry` / `dispatch(id, args)` | The inner list's command registry, e.g. `dispatch('list.search.filter')` |
|
|
53
53
|
| `list` | The inner `ixfx-vertical-list` |
|
|
54
|
+
| `getViewState(options?)` / `setViewState(state)` / `clearPendingViewState()` | See [View state](#view-state) |
|
|
54
55
|
|
|
55
56
|
## Events
|
|
56
57
|
|
|
@@ -77,6 +78,32 @@ The inner list's `list-*` events also cross the shadow boundary, with row elemen
|
|
|
77
78
|
|
|
78
79
|
Selection is cleared on every level change. Arrow Left and Backspace are ignored while the search field is focused.
|
|
79
80
|
|
|
81
|
+
## View state
|
|
82
|
+
|
|
83
|
+
The level shown and its selection, cursor and scroll, as plain JSON for the
|
|
84
|
+
host to persist (see [docs/infra-view-state.md](../../docs/infra-view-state.md)):
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
{ v: 1, kind: 'flat-list', path: ['docs', 'work'], selected: ['a.txt'], focus: 'a.txt', scroll: { top: 0 } }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`selected`, `focus` and `scroll` belong to the level at `path`. They are held
|
|
91
|
+
until that level is shown, then handed to the inner list, which applies them
|
|
92
|
+
as its rows arrive (see the vertical-list README).
|
|
93
|
+
|
|
94
|
+
- Keys not loaded yet stay pending: the list shows the deepest level it can
|
|
95
|
+
reach and continues as data arrives. Showing a level loads it (via
|
|
96
|
+
`loadChildren` / `model`), so a saved path through lazy levels loads one
|
|
97
|
+
level at a time.
|
|
98
|
+
- If new data drops the level shown, the list steps up to the closest
|
|
99
|
+
ancestor and the level (with its selection) goes pending; it is returned to
|
|
100
|
+
when its keys reappear. This also applies to a `path` assigned before the
|
|
101
|
+
data has loaded.
|
|
102
|
+
- An explicit navigation (gesture, `navigate*()`, or assigning `path`) drops
|
|
103
|
+
a pending level; an explicit selection drops a pending selection.
|
|
104
|
+
- The inner crumb's and list's own `view-state-change` events are stopped at
|
|
105
|
+
this element; it fires its own.
|
|
106
|
+
|
|
80
107
|
## Drops
|
|
81
108
|
|
|
82
109
|
With `items-droppable`, a drag over a row, or over empty space, is accepted unless `canDrop` refuses it. A drop fires `flat-list-drop` with that row's node, or the **current level's node** for empty space. Dropping on a branch row reports it and does **not** enter it. Native OS drags work: `dataTransfer` carries `files`, `text/uri-list` and any custom MIME type the source wrote. The row under the drag gets the vertical-list [drop highlight](../vertical-list/README.md#drop-targets).
|
package/docs-user/grid-list.md
CHANGED
|
@@ -20,10 +20,11 @@ single vertical column, or a horizontally-scrolling strip.
|
|
|
20
20
|
9. [Native drag-out](#native-drag-out)
|
|
21
21
|
10. [Keyboard](#keyboard)
|
|
22
22
|
11. [Grouping](#grouping)
|
|
23
|
-
12. [
|
|
24
|
-
13. [
|
|
25
|
-
14. [
|
|
26
|
-
15. [
|
|
23
|
+
12. [View state](#view-state)
|
|
24
|
+
13. [Virtualisation](#virtualisation)
|
|
25
|
+
14. [Performance](#performance)
|
|
26
|
+
15. [CSS parts](#css-parts)
|
|
27
|
+
16. [Choosing a list component](#choosing-a-list-component)
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -214,7 +215,7 @@ grid.tickledKey; // key under the cursor, or undefined
|
|
|
214
215
|
| Property / attribute | Default | Description |
|
|
215
216
|
|---|---|---|
|
|
216
217
|
| `disableHoverTickle` / `disable-hover-tickle` | `false` | When set, pointer hover no longer moves the cursor, fires `list-tickle`, or changes where arrow-key navigation resumes from, and the hover highlight is dropped so nothing *looks* interactive. Keyboard navigation, selection, and `focusKey()` are unaffected. |
|
|
217
|
-
| `
|
|
218
|
+
| `scrollHoverTickle` / `scroll-hover-tickle` | `false` | Let hover follow cells that scroll beneath a stationary pointer. Off by default: during a scroll gesture, virtualised `pointerenter` events are ignored so `list-tickle` and the highlight hold still. Hover resumes on the next real pointer movement, or after the pointer leaves and re-enters the list. Click selection, keyboard navigation, and `focusKey()` are unaffected. |
|
|
218
219
|
|
|
219
220
|
```html
|
|
220
221
|
<ixfx-grid-list disable-hover-tickle></ixfx-grid-list>
|
|
@@ -229,8 +230,8 @@ Because cells are virtualised, wheel / trackpad scrolling inserts new cells
|
|
|
229
230
|
under a stationary pointer, and each fresh cell fires `pointerenter` — the
|
|
230
231
|
cursor and any cursor-driven rendering (e.g. a large preview panel) churn for
|
|
231
232
|
as long as the gesture lasts, even though the pointer never moved.
|
|
232
|
-
|
|
233
|
-
|
|
233
|
+
By default those synthetic enters are ignored while the gesture is active
|
|
234
|
+
(set `scroll-hover-tickle` to let hover follow the content instead). While suppressed, the host carries a `data-scroll-hover-suppress`
|
|
234
235
|
attribute (the CSS `:hover` highlight is gated on it), which is removed on the
|
|
235
236
|
next real pointer movement or when the pointer leaves the list.
|
|
236
237
|
|
|
@@ -310,8 +311,11 @@ grid.registry.invoke('list.search.filter'); // or .navigate / .select
|
|
|
310
311
|
|
|
311
312
|
Search matches `data-label` (or `[data-grid-list-caption]` text, then
|
|
312
313
|
`data-search-label`, then `textContent`). Filter mode genuinely removes items
|
|
313
|
-
from the virtualised layout.
|
|
314
|
-
|
|
314
|
+
from the virtualised layout. Matched characters are highlighted under the
|
|
315
|
+
`grid-list-search` key. In data mode (`itemAdapter`) the captions live in the
|
|
316
|
+
shadow root and the grid styles them itself (`--search-highlight-bg` /
|
|
317
|
+
`--search-highlight-color`, default `--accent` / `--accent-text`). In element
|
|
318
|
+
mode the items are light DOM, so add a page-level rule:
|
|
315
319
|
|
|
316
320
|
```css
|
|
317
321
|
::highlight(grid-list-search) { background: var(--accent); color: var(--accent-text); }
|
|
@@ -402,11 +406,11 @@ Parts: `part="icon"` and `part="badge"` on the wrappers this adds.
|
|
|
402
406
|
|
|
403
407
|
| Method | Effect |
|
|
404
408
|
|---|---|
|
|
405
|
-
| `setItems(items)` | Replace the collection. Selection / cursor keep any key still present. |
|
|
409
|
+
| `setItems(items)` | Replace the collection. Selection / cursor keep any key still present; vanished keys are deselected silently and held pending (see [View state](#view-state)). |
|
|
406
410
|
| `appendItems(items)` | Add to the end. Scroll, selection and cursor untouched; no reflow animation. |
|
|
407
411
|
| `updateItem(key, item)` | Replace one item. A visible cell re-renders in place; an offscreen one updates when scrolled in. |
|
|
408
|
-
| `removeItems(keys)` | Remove items. Removed keys drop out of selection / cursor. |
|
|
409
|
-
| `clearItems()` | Drop everything. Resets selection / cursor and emits one empty `list-select
|
|
412
|
+
| `removeItems(keys)` | Remove items. Removed keys drop out of selection / cursor silently and are held pending. |
|
|
413
|
+
| `clearItems()` | Drop everything. Resets selection / cursor and emits one empty `list-select`; the keys are held pending, so re-adding them restores the selection. |
|
|
410
414
|
|
|
411
415
|
Selection, cursor, keyboard nav, search, filtering, `count`, `renderedCount`,
|
|
412
416
|
`renderedKeys()` and `visibleKeys()` behave identically to element mode — all
|
|
@@ -442,7 +446,7 @@ grid.collapseAllGroups();
|
|
|
442
446
|
|
|
443
447
|
- Groups appear in first-encounter order. Filters (`filterPredicate`, `itemFilterPredicate`, incremental filter) run first, so an emptied group loses its header and counts follow the filter.
|
|
444
448
|
- **Collapsed groups** leave the effective order: not navigable, not rendered, not in select-all, range, marquee or drag-out. Their selection state is kept. A collapsed key is not auto-expanded by `focusKey()`.
|
|
445
|
-
- Changing `groupBy` (from any path) clears the collapse state.
|
|
449
|
+
- Changing `groupBy` (from any path) clears the collapse state. Saved collapse state from `setViewState()` that hasn't matched a group yet is kept.
|
|
446
450
|
- **Layout:** in `grid` / `column` a header spans the full width and its items restart at column 0 beneath it. In `row` orientation the header is an inline cell of `group-header-width`.
|
|
447
451
|
- **Keyboard:** Left/Right step through items, crossing groups. Up/Down keep the column, hop across headers and clamp on a short last row. Headers are never focused; collapsing is by click or the API.
|
|
448
452
|
- Events: `list-group-toggle` `{ groupKey, collapsed }`, `list-group-change` `{ groupBy }`.
|
|
@@ -450,6 +454,58 @@ grid.collapseAllGroups();
|
|
|
450
454
|
|
|
451
455
|
Differences from data-grid: group state is reset on every `groupBy` change, the empty key is labelled `(none)`, headers are skipped by arrow keys, and the collapse API and events are exposed.
|
|
452
456
|
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## View state
|
|
460
|
+
|
|
461
|
+
Snapshot and restore how the grid is showing its data, separately from the
|
|
462
|
+
data. The contract is shared with the other list components; see
|
|
463
|
+
[`docs/infra-view-state.md`](../../docs/infra-view-state.md).
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
import { bindViewState } from '@ixfx/components';
|
|
467
|
+
|
|
468
|
+
bindViewState(grid, {
|
|
469
|
+
load: () => JSON.parse(localStorage.getItem(`photos-grid`) ?? `null`),
|
|
470
|
+
save: s => localStorage.setItem(`photos-grid`, JSON.stringify(s)),
|
|
471
|
+
});
|
|
472
|
+
grid.items = await fetchPhotos(); // pending state applies as items arrive
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
`kind`: `grid-list`. Type: `GridListViewState`.
|
|
476
|
+
|
|
477
|
+
| Field | Shape | Meaning |
|
|
478
|
+
|---|---|---|
|
|
479
|
+
| `selected` | `string[]` | Selected item keys. |
|
|
480
|
+
| `expanded` | `Record<string, boolean>` | Group key → open. Groups default open, so only collapsed groups (`false`) are recorded. |
|
|
481
|
+
| `focus` | `string` | Keyboard-cursor key. Recorded only while the cursor was last moved by keyboard. |
|
|
482
|
+
| `scroll` | `{ top, left?, anchor? }` | `anchor` is the first item of the first visible line plus the px offset into that line, along the main axis (`left` in `row` orientation, `top` otherwise). |
|
|
483
|
+
|
|
484
|
+
Members: `getViewState(options?)`, `setViewState(state)`,
|
|
485
|
+
`clearPendingViewState()`, and the debounced `view-state-change` event.
|
|
486
|
+
|
|
487
|
+
Grid-specific behaviour:
|
|
488
|
+
|
|
489
|
+
- Works the same in element mode (light-DOM children with `data-key`) and data
|
|
490
|
+
mode (`items` + `itemAdapter`). Keyless children have auto-assigned
|
|
491
|
+
positional keys, which never appear in view state.
|
|
492
|
+
- A key counts as present when its item is in the effective order: an item
|
|
493
|
+
that is filtered out, or inside a collapsed group, keeps its saved selection
|
|
494
|
+
pending until it shows. Restoring `expanded` and `selected` together re-opens
|
|
495
|
+
groups first, so a saved selection inside a saved-open group resolves.
|
|
496
|
+
- A collapsed group whose items all leave (data reset, filter) keeps its
|
|
497
|
+
collapsed state pending and is collapsed again when it comes back.
|
|
498
|
+
Restoring collapse state does not fire `list-group-toggle`.
|
|
499
|
+
- Items that leave the data are deselected **without** a `list-select`; their
|
|
500
|
+
keys go pending and are re-selected (with a `list-select`) if they return.
|
|
501
|
+
- A restored cursor puts the list in keyboard input mode, so it is shown and
|
|
502
|
+
recorded like a keyboard-moved cursor.
|
|
503
|
+
- Scroll restore waits until the anchor item is present and the viewport is
|
|
504
|
+
measured, then until the content is long enough. It is cancelled by the
|
|
505
|
+
user's own wheel, touch, pointer or navigation key. The anchor is re-laid out
|
|
506
|
+
with the current geometry, so a different column count still lands on the
|
|
507
|
+
same item's line.
|
|
508
|
+
|
|
453
509
|
## Virtualisation
|
|
454
510
|
|
|
455
511
|
Only the cells in (or near) the viewport are in the DOM — in both element and
|
|
@@ -17,10 +17,11 @@ It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](.
|
|
|
17
17
|
7. [Targeting a specific node or edge](#targeting-a-specific-node-or-edge)
|
|
18
18
|
8. [Custom edge rendering](#custom-edge-rendering)
|
|
19
19
|
9. [Focus, selection, navigation](#focus-selection-navigation)
|
|
20
|
-
10. [
|
|
21
|
-
11. [
|
|
22
|
-
12. [
|
|
23
|
-
13. [
|
|
20
|
+
10. [View state](#view-state)
|
|
21
|
+
11. [Events](#events)
|
|
22
|
+
12. [Keyboard / pointer interaction](#keyboard--pointer-interaction)
|
|
23
|
+
13. [CSS variables and parts](#css-variables-and-parts)
|
|
24
|
+
14. [Types reference](#types-reference)
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -56,6 +57,7 @@ el.root = {
|
|
|
56
57
|
| `root` | `TreeNode \| undefined` | `undefined` | Root node of the tree |
|
|
57
58
|
| `model` | `TreeDataModel \| undefined` | `undefined` | Shared data model; takes precedence over `loadChildren` |
|
|
58
59
|
| `loadChildren` | `LoadChildrenCallback \| undefined` | `undefined` | Callback for lazy child loading |
|
|
60
|
+
| `restoreLoads` | `boolean` | `true` | Whether restoring a saved `center` may load its unloaded ancestors. See [View state](#view-state) |
|
|
59
61
|
| `interactionMode` (`interaction-mode`) | `'implicit' \| 'standard' \| 'manual'` | `'standard'` | Gesture mapping — see [`src/tree/README.md`](../tree/README.md#interactionmode-ixfx-tree-list--ixfx-miller-list) (`'checked'` is unsupported here) |
|
|
60
62
|
| `selectionFilter` (`selection-filter`) | `'none' \| 'leaf' \| 'branch' \| 'all'` | `'all'` | Which node kinds can be selected |
|
|
61
63
|
| `exclusivity` | `TreeExclusivity` | `'none'` | Accepted for interface compatibility, ignored |
|
|
@@ -252,6 +254,31 @@ el.navigateToNode(n => n.item.key === 'readme.md');
|
|
|
252
254
|
const nodes = el.getNodes(); // ReadonlyArray<{ node: TreeNode; depth: number }>
|
|
253
255
|
```
|
|
254
256
|
|
|
257
|
+
## View state
|
|
258
|
+
|
|
259
|
+
Implements the shared view-state API (`getViewState()` / `setViewState()` / `clearPendingViewState()` / `view-state-change`) — see [`docs/infra-view-state.md`](../../docs/infra-view-state.md). `kind` is `hyperbolictree-list`.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
type HyperbolicTreeListViewState = ViewStateBase & {
|
|
263
|
+
center?: string[]; // key path from the root's children to the centred node; [] = root
|
|
264
|
+
zoom?: number; // visual magnification (the `zoom` property)
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| Field | Meaning |
|
|
269
|
+
|---|---|
|
|
270
|
+
| `selected` | Selected item keys. |
|
|
271
|
+
| `focus` | Keyboard cursor key — only recorded when the cursor was moved by keyboard or search navigation. |
|
|
272
|
+
| `center` | The centred node, as a key path. A path rather than a lone key because keys may repeat across branches here (e.g. `readme.md` in many folders). |
|
|
273
|
+
| `zoom` | Magnification. Not keyed: it doesn't depend on the data. |
|
|
274
|
+
|
|
275
|
+
- **What isn't saved:** a free pan (dragging empty space) and the disk's rotation are raw transforms in the centred node's local frame — they break as soon as the data changes, so they are left out. A restored view sits exactly on `center`, unpanned.
|
|
276
|
+
- **Data changes keep state.** A new `root`, a model `root` / `invalidate` event or a lazy load re-applies state by key. If the centred node goes away the view falls back to the root silently and `center` stays pending; selected nodes that go away are deselected silently and their keys stay pending.
|
|
277
|
+
- **Lazy data.** With `restoreLoads` (default `true`), a saved `center` whose ancestors aren't loaded is reached by loading each one in turn via `loadChildren` / `model`.
|
|
278
|
+
- **Events.** A restored selection fires `select`; a restored centre fires `focus` (no `expand`/`collapse`) and snaps rather than animating.
|
|
279
|
+
- An explicit `focusOn()` (click, key, navigation) drops a pending `center`; any explicit selection change drops pending `selected`.
|
|
280
|
+
- With repeated keys, a saved selection resolves to the first node (depth-first) with that key.
|
|
281
|
+
|
|
255
282
|
## Events
|
|
256
283
|
|
|
257
284
|
All events **bubble** and are **composed**.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Icicle Tree
|
|
2
|
+
|
|
3
|
+
`<ixfx-icicletree-list>` — an icicle (partition) view over tree data. Each depth is a band; each node a rectangle sized by its `valueAccessor` share of its parent. Clicking a branch zooms so it becomes the visual root, with its ancestors in a breadcrumb strip.
|
|
4
|
+
|
|
5
|
+
It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](../tree/README.md)). The [sunburst](../sunburst-tree/) is its polar counterpart.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## View state
|
|
10
|
+
|
|
11
|
+
Implements the shared view-state API (`getViewState()` / `setViewState()` / `clearPendingViewState()` / `view-state-change`) — see [`docs/infra-view-state.md`](../../docs/infra-view-state.md). `kind` is `icicletree-list`.
|
|
12
|
+
|
|
13
|
+
| Field | Type | Meaning |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `selected` | `string[]` | Selected `item.key`s |
|
|
16
|
+
| `zoom` | `string[]` | Key path from the root's children to the zoom node; `[]` = full tree |
|
|
17
|
+
| `focus` | `string` | Keyboard-cursor `item.key` (recorded only when the cursor was moved by keyboard) |
|
|
18
|
+
| `scroll` | `{ top, left? }` | The host's scroll offset (pixels; rects aren't a linear list, so there is no anchor) |
|
|
19
|
+
|
|
20
|
+
There is no `expanded` (expansion *is* zoom).
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
const stop = bindViewState(el, {
|
|
24
|
+
load: () => JSON.parse(localStorage.getItem(`org`) ?? `null`),
|
|
25
|
+
save: s => localStorage.setItem(`org`, JSON.stringify(s)),
|
|
26
|
+
});
|
|
27
|
+
el.root = await fetchOrg(); // zoom + selection apply as their nodes arrive
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- State for nodes not loaded yet stays pending and applies when they appear — whether the tree is reassigned, grown in place (then `requestUpdate()`), or lazily loaded. **Reassigning `root` keeps zoom, selection and cursor** where keys still exist; state for keys that have gone moves to pending (silently) and returns with them.
|
|
31
|
+
- `zoom` is a path so a lazy tree can be opened down to it: while pending, the nodes along it are loaded one at a time via `loadChildren` / `model` (set `restoreLoads = false` to only apply it as the host loads them). A zoom node that moved elsewhere in the tree is still found by its key.
|
|
32
|
+
- Restored selection fires `select`; a restored zoom fires `zoom` (and `expand`). Any explicit selection change drops pending selection; any explicit zoom (`zoomTo()`, `zoomOut()`, clicks, crumbs, `navigateTo()`…) drops a pending zoom.
|
|
33
|
+
|
|
34
|
+
| Property | Type | Default | Description |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `restoreLoads` | `boolean` | `true` | Load nodes along a pending saved zoom path |
|
package/docs-user/index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ixfx/components",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"generated": "2026-10-
|
|
3
|
+
"version": "0.16.1",
|
|
4
|
+
"generated": "2026-10-26T15:36:19.925Z",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"name": "ac-text",
|
|
@@ -98,6 +98,11 @@
|
|
|
98
98
|
"summary": "`<ixfx-hyperbolictree-list>` — a hyperbolic (Poincaré disk) browser over tree data. The focused node sits at the disk's center; its neighbourhood fans outward, shrinking toward the edge with tree-grap",
|
|
99
99
|
"path": "docs-user/hyperbolic-tree.md"
|
|
100
100
|
},
|
|
101
|
+
{
|
|
102
|
+
"name": "icicle-tree",
|
|
103
|
+
"summary": "`<ixfx-icicletree-list>` — an icicle (partition) view over tree data. Each depth is a band; each node a rectangle sized by its `valueAccessor` share of its parent. Clicking a branch zooms so it become",
|
|
104
|
+
"path": "docs-user/icicle-tree.md"
|
|
105
|
+
},
|
|
101
106
|
{
|
|
102
107
|
"name": "icons",
|
|
103
108
|
"summary": "A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.",
|
|
@@ -273,6 +278,11 @@
|
|
|
273
278
|
"summary": "Three components share a common `TreeComponent` interface: **`ixfx-tree-list`**, **`ixfx-miller-list`**, and **`ixfx-crumb-navigation`**. This document describes the shared API, plus component-specifi",
|
|
274
279
|
"path": "docs-user/tree.md"
|
|
275
280
|
},
|
|
281
|
+
{
|
|
282
|
+
"name": "treemap",
|
|
283
|
+
"summary": "`<ixfx-treemap-list>` — a nested-area (treemap) view over tree data. The zoom root fills the viewport; each node's rectangle is subdivided among its children by `valueAccessor` share. Clicking a branc",
|
|
284
|
+
"path": "docs-user/treemap.md"
|
|
285
|
+
},
|
|
276
286
|
{
|
|
277
287
|
"name": "twosplit-layout",
|
|
278
288
|
"summary": "A Lit web component that lays out two elements within a container with a split bar to adjust their proportions.",
|
package/docs-user/llms.txt
CHANGED
|
@@ -30,6 +30,7 @@ import '@ixfx/components/data-grid';
|
|
|
30
30
|
- [grid-list](docs-user/grid-list.md): A virtualised grid of items — thumbnails, cards, or arbitrary slotted markup —
|
|
31
31
|
- [grouped-item-lister](docs-user/grouped-item-lister.md): A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own
|
|
32
32
|
- [hyperbolic-tree](docs-user/hyperbolic-tree.md): `<ixfx-hyperbolictree-list>` — a hyperbolic (Poincaré disk) browser over tree data. The focused node sits at the disk's center; its neighbourhood fans outward, shrinking toward the edge with tree-grap
|
|
33
|
+
- [icicle-tree](docs-user/icicle-tree.md): `<ixfx-icicletree-list>` — an icicle (partition) view over tree data. Each depth is a band; each node a rectangle sized by its `valueAccessor` share of its parent. Clicking a branch zooms so it become
|
|
33
34
|
- [icons](docs-user/icons.md): A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.
|
|
34
35
|
- [incr-search](docs-user/incr-search.md): Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
|
|
35
36
|
- [labelled-radial-input](docs-user/labelled-radial-input.md): A composite radial input that pairs an editable label (`ixfx-editable-number`) with a rotary dial (`ixfx-radial-input`). The two controls are always in sync — turning the dial updates the number, and
|
|
@@ -65,6 +66,7 @@ import '@ixfx/components/data-grid';
|
|
|
65
66
|
- [toolbar](docs-user/toolbar.md): Two components for building toolbars:
|
|
66
67
|
- [transitory-label](docs-user/transitory-label.md): An inline text label that animates between values instead of changing instantly. Displays as `inline-block` and inherits text styling from its parent.
|
|
67
68
|
- [tree](docs-user/tree.md): Three components share a common `TreeComponent` interface: **`ixfx-tree-list`**, **`ixfx-miller-list`**, and **`ixfx-crumb-navigation`**. This document describes the shared API, plus component-specifi
|
|
69
|
+
- [treemap](docs-user/treemap.md): `<ixfx-treemap-list>` — a nested-area (treemap) view over tree data. The zoom root fills the viewport; each node's rectangle is subdivided among its children by `valueAccessor` share. Clicking a branc
|
|
68
70
|
- [twosplit-layout](docs-user/twosplit-layout.md): A Lit web component that lays out two elements within a container with a split bar to adjust their proportions.
|
|
69
71
|
- [util](docs-user/util.md): A tooltip component that renders a value with optional icon and formatted number display. Uses native render root (no shadow DOM).
|
|
70
72
|
- [vertical-list](docs-user/vertical-list.md): A scrollable vertical list of items with selection, keyboard navigation, and incremental search. Items can be plain `<li>` elements declared in HTML or rich custom elements added programmatically. Lar
|
package/docs-user/miller.md
CHANGED
|
@@ -16,7 +16,8 @@ A Miller column component displays hierarchical data as multiple vertical column
|
|
|
16
16
|
|
|
17
17
|
| Property | Type | Default | Description |
|
|
18
18
|
|----------|------|---------|-------------|
|
|
19
|
-
| `root` | `MillerNode \| undefined` | — | The root node of the tree. Assigning a new root
|
|
19
|
+
| `root` | `MillerNode \| undefined` | — | The root node of the tree. Assigning a new root re-applies the open path, selection and cursor by key (see [View state](#view-state)); a rebased view is reset. |
|
|
20
|
+
| `restoreLoads` | `boolean` | `true` | Whether restoring view state may call `loadChildren` / `model` to open a saved path through unloaded branches. |
|
|
20
21
|
| `selectionMode` | `MillerSelectionMode` | `'implicit'` | Controls how items are selected. See [Selection Modes](#selection-modes) below. |
|
|
21
22
|
| `selectedKeys` | `ReadonlySet<string>` (getter) / `Iterable<string>` (setter) | — | The set of currently selected item keys. Assigning replaces the entire selection and triggers a re-render. |
|
|
22
23
|
| `loadChildren` | `MillerLoadChildrenCallback \| undefined` | — | Callback to load children for lazy nodes. If undefined, clicking a lazy node opens an empty column. |
|
|
@@ -57,18 +58,18 @@ Inherited from theme:
|
|
|
57
58
|
navigateTo(keys: readonly string[], options?: { reveal?: 'edge' | 'center-if-hidden'; silent?: boolean }): boolean
|
|
58
59
|
|
|
59
60
|
// Re-sync after children arrays were patched in place (used by MillerTreeController.sync()):
|
|
60
|
-
//
|
|
61
|
+
// the open path and selection re-resolve by key; removed nodes close their
|
|
62
|
+
// columns / are deselected, and come back if their key reappears.
|
|
61
63
|
invalidateStructure(): void
|
|
62
64
|
|
|
63
65
|
// Navigate to the first loaded node (breadth-first) matching a predicate.
|
|
64
66
|
// Returns false if no match is found in the currently loaded tree.
|
|
65
67
|
navigateToNode(predicate: (node: MillerNode) => boolean): boolean
|
|
66
68
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
setConfig(config: MillerConfig): void
|
|
69
|
+
// View state — see "View state" below
|
|
70
|
+
getViewState(options?): MillerListViewState
|
|
71
|
+
setViewState(state: Partial<MillerListViewState>): void
|
|
72
|
+
clearPendingViewState(): void
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
### Events
|
|
@@ -190,10 +191,6 @@ type MillerNode = {
|
|
|
190
191
|
item: MillerItem;
|
|
191
192
|
children?: MillerNode[]; // undefined = unloaded, [] = confirmed empty
|
|
192
193
|
};
|
|
193
|
-
|
|
194
|
-
type MillerConfig = {
|
|
195
|
-
columnWidths: Record<number, number>; // depth -> px
|
|
196
|
-
};
|
|
197
194
|
```
|
|
198
195
|
|
|
199
196
|
### Per-item icon colour, text colour and badges
|
|
@@ -338,24 +335,58 @@ el.navigateToNode(node => node.item.label === 'System32');
|
|
|
338
335
|
|
|
339
336
|
### Column Resizing
|
|
340
337
|
|
|
341
|
-
Columns can be resized by dragging the handle on the right edge of each
|
|
338
|
+
Columns can be resized by dragging the handle on the right edge of each
|
|
339
|
+
column. Widths are part of the view state (`columnWidths`, keyed by depth):
|
|
342
340
|
|
|
343
341
|
```javascript
|
|
344
|
-
|
|
345
|
-
const config = el.getConfig();
|
|
346
|
-
localStorage.setItem('millerConfig', JSON.stringify(config));
|
|
347
|
-
|
|
348
|
-
// Restore saved state
|
|
349
|
-
el.setConfig(JSON.parse(localStorage.getItem('millerConfig')));
|
|
342
|
+
el.setViewState({ columnWidths: { 0: 150, 1: 200, 2: 180 } });
|
|
350
343
|
```
|
|
351
344
|
|
|
352
|
-
###
|
|
345
|
+
### View state
|
|
346
|
+
|
|
347
|
+
`getViewState()` / `setViewState()` snapshot and restore how the columns are
|
|
348
|
+
showing the data, as plain JSON keyed by `item.key`. The component never
|
|
349
|
+
stores it; `bindViewState()` wires it to storage. See
|
|
350
|
+
[docs/infra-view-state.md](../../docs/infra-view-state.md).
|
|
353
351
|
|
|
354
352
|
```javascript
|
|
355
|
-
|
|
356
|
-
|
|
353
|
+
import { bindViewState } from '@ixfx/components';
|
|
354
|
+
|
|
355
|
+
bindViewState(el, {
|
|
356
|
+
load: () => JSON.parse(localStorage.getItem('miller') ?? 'null'),
|
|
357
|
+
save: s => localStorage.setItem('miller', JSON.stringify(s)),
|
|
358
|
+
});
|
|
359
|
+
el.root = await fetchRoot(); // saved state applies as nodes arrive
|
|
357
360
|
```
|
|
358
361
|
|
|
362
|
+
`kind: 'miller-list'`. Fields:
|
|
363
|
+
|
|
364
|
+
| Field | Type | Meaning |
|
|
365
|
+
|---|---|---|
|
|
366
|
+
| `path` | `string[]` | Key of the open branch at each depth, column 0 first, relative to the view root. |
|
|
367
|
+
| `selected` | `string[]` | `selectedKeys` (the checked set in `checked` mode). |
|
|
368
|
+
| `focus` | `string` | Keyboard cursor key, only when last moved by keyboard. |
|
|
369
|
+
| `columnWidths` | `Record<string, number>` | User-resized widths in px, keyed by depth. |
|
|
370
|
+
| `columnScroll` | `Record<string, ScrollState>` | Scroll of each scrolled column, keyed by the key of the node whose children it lists (the view root's key for column 0). |
|
|
371
|
+
|
|
372
|
+
`path` is an ordered array rather than the shared `expanded` record because a
|
|
373
|
+
Miller view has exactly one open branch per column.
|
|
374
|
+
|
|
375
|
+
- State for keys not loaded yet stays **pending** and applies as nodes appear:
|
|
376
|
+
a new `root`, `invalidateStructure()` after children were patched in place,
|
|
377
|
+
or a lazy load. A saved path `a > b > c` opens as each node arrives.
|
|
378
|
+
- With `restoreLoads` (default `true`) a saved path through unloaded branches
|
|
379
|
+
is opened via `loadChildren` / `model`, one load at a time, so it cascades.
|
|
380
|
+
- New `root` data keeps the open path, selection and cursor for keys that
|
|
381
|
+
still exist; keys that left go pending (deselected silently) and come back
|
|
382
|
+
if they reappear. Restored selection fires `select` with
|
|
383
|
+
`origin: 'programmatic'`.
|
|
384
|
+
- Any explicit selection change drops pending selection; any explicit
|
|
385
|
+
navigation (click, arrow keys, `navigateTo`, rebasing) drops the pending
|
|
386
|
+
path. The view root itself is not part of the state.
|
|
387
|
+
- `view-state-change` fires (debounced) on selection, navigation, keyboard
|
|
388
|
+
cursor moves, column resize and column scroll.
|
|
389
|
+
|
|
359
390
|
---
|
|
360
391
|
|
|
361
392
|
## MillerTreeController
|