@ixfx/components 0.15.4 → 0.16.0
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 +1223 -48
- package/bundle/index.js +875 -700
- package/dist/ac-text.d.ts +1 -1
- package/dist/{ac-token-Cq7lsYf-.d.ts → ac-token-qEs3FsQv.d.ts} +1 -1
- package/dist/ac-token.d.ts +1 -1
- package/dist/colour-picker.d.ts +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-CbiubdG2.d.ts → data-grid-CIG2DVip.d.ts} +58 -9
- package/dist/{data-grid-Dx3l3dxw.js → data-grid-rMkLX2qo.js} +630 -215
- package/dist/data-grid.d.ts +3 -3
- package/dist/data-grid.js +1 -1
- package/dist/{detail-list-ClkMV0SN.d.ts → detail-list-6iMTNP4N.d.ts} +35 -5
- package/dist/{detail-list-q8bs67BM.js → detail-list-C4pDSoI8.js} +218 -7
- package/dist/detail-list.d.ts +4 -4
- package/dist/detail-list.js +1 -1
- package/dist/{flat-list-0xTZdXzh.d.ts → flat-list-JPgQSmLW.d.ts} +28 -5
- package/dist/flat-list.d.ts +2 -2
- package/dist/flat-list.js +123 -4
- package/dist/{grid-list-y6dWJYu3.js → grid-list-CdEkScn-.js} +276 -75
- package/dist/{grid-list-BnWJUIBC.d.ts → grid-list-D6n7WH8l.d.ts} +128 -22
- package/dist/grid-list.d.ts +3 -3
- package/dist/grid-list.js +1 -1
- package/dist/{grouped-item-lister-Bhyo-dkH.d.ts → grouped-item-lister-DETte0Dh.d.ts} +1 -1
- package/dist/grouped-item-lister.d.ts +1 -1
- package/dist/{icon-DR_3rSyZ.d.ts → icon-CDGprSdE.d.ts} +1 -1
- package/dist/icons.d.ts +2 -2
- package/dist/incr-search.d.ts +1 -1
- package/dist/index-9ttcdwlL.d.ts +71 -0
- package/dist/{index-BAuqR9El.d.ts → index-Bx0BXPxI.d.ts} +52 -6
- package/dist/index-CYRc6Dah.d.ts +475 -0
- package/dist/{index-garxxqgV.d.ts → index-CYndvTWH.d.ts} +1 -1
- package/dist/{index-BUB5SICW.d.ts → index-CoqtMSd9.d.ts} +1 -1
- package/dist/{index-Bz3loIAf.d.ts → index-DN2BWctP.d.ts} +1 -1
- package/dist/{index-BPTuCRGb.d.ts → index-Dr80Zdl6.d.ts} +2 -2
- package/dist/{index-cnaRPQPp.d.ts → index-DxyyVqzA.d.ts} +2 -2
- package/dist/{index-CJ5fshD3.d.ts → index-UvnoZUAo.d.ts} +1 -1
- package/dist/index.d.ts +380 -33
- package/dist/index.js +2247 -866
- package/dist/{labelled-input-base-Dp_9KP0G.d.ts → labelled-input-base-DiTme1JO.d.ts} +1 -1
- package/dist/labelled-radial-input.d.ts +2 -2
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-slider-input.d.ts +1 -1
- package/dist/list-group-header.d.ts +1 -1
- 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/{menu-item-B0zqw2xp.d.ts → menu-item-bF6narIz.d.ts} +1 -1
- package/dist/menu.d.ts +2 -2
- package/dist/miller-BFRfn0ve.js +2237 -0
- package/dist/miller.d.ts +4 -437
- package/dist/miller.js +1 -1815
- package/dist/panel.d.ts +1 -1
- package/dist/pending-BGidHppL.js +91 -0
- package/dist/scroll-hover-guard-Cjw6179Q.js +35 -0
- package/dist/{split-layout-C5SYbDrq.d.ts → split-layout-CophkNeU.d.ts} +42 -4
- 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 -0
- package/dist/sunburst-tree.js +2 -0
- package/dist/sunbursttree-list-B_VxOHlo.js +1556 -0
- package/dist/sunbursttree-list-D6j5BxVQ.d.ts +303 -0
- package/dist/{tab-list-wC0xNz1b.d.ts → tab-list-Df371rqw.d.ts} +1 -1
- package/dist/tabs.d.ts +1 -1
- package/dist/{titlebar-B5zSTZnF.d.ts → titlebar-Tqu-VvMU.d.ts} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/tree-CftHwtEb.js +146 -0
- package/dist/{tree-D8VxvR9u.js → tree-Crdpl4nc.js} +275 -47
- 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-1V1Vg2O5.js +1993 -0
- package/dist/{vertical-list-B8FoU-pU.d.ts → vertical-list-BPDmZx6T.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 +3 -0
- package/docs-user/crumbs.md +27 -0
- package/docs-user/data-grid.md +77 -5
- package/docs-user/detail-list.md +54 -0
- package/docs-user/dock.md +54 -12
- package/docs-user/flat-list.md +27 -0
- package/docs-user/grid-list.md +64 -11
- package/docs-user/hyperbolic-tree.md +31 -4
- package/docs-user/icicle-tree.md +36 -0
- package/docs-user/index.json +17 -2
- package/docs-user/llms.txt +3 -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 +223 -0
- package/docs-user/tree.md +46 -7
- 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 +3 -0
- package/package.json +5 -1
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
|
|
|
@@ -402,11 +403,11 @@ Parts: `part="icon"` and `part="badge"` on the wrappers this adds.
|
|
|
402
403
|
|
|
403
404
|
| Method | Effect |
|
|
404
405
|
|---|---|
|
|
405
|
-
| `setItems(items)` | Replace the collection. Selection / cursor keep any key still present. |
|
|
406
|
+
| `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
407
|
| `appendItems(items)` | Add to the end. Scroll, selection and cursor untouched; no reflow animation. |
|
|
407
408
|
| `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
|
|
409
|
+
| `removeItems(keys)` | Remove items. Removed keys drop out of selection / cursor silently and are held pending. |
|
|
410
|
+
| `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
411
|
|
|
411
412
|
Selection, cursor, keyboard nav, search, filtering, `count`, `renderedCount`,
|
|
412
413
|
`renderedKeys()` and `visibleKeys()` behave identically to element mode — all
|
|
@@ -442,7 +443,7 @@ grid.collapseAllGroups();
|
|
|
442
443
|
|
|
443
444
|
- 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
445
|
- **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.
|
|
446
|
+
- Changing `groupBy` (from any path) clears the collapse state. Saved collapse state from `setViewState()` that hasn't matched a group yet is kept.
|
|
446
447
|
- **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
448
|
- **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
449
|
- Events: `list-group-toggle` `{ groupKey, collapsed }`, `list-group-change` `{ groupBy }`.
|
|
@@ -450,6 +451,58 @@ grid.collapseAllGroups();
|
|
|
450
451
|
|
|
451
452
|
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
453
|
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## View state
|
|
457
|
+
|
|
458
|
+
Snapshot and restore how the grid is showing its data, separately from the
|
|
459
|
+
data. The contract is shared with the other list components; see
|
|
460
|
+
[`docs/infra-view-state.md`](../../docs/infra-view-state.md).
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
import { bindViewState } from '@ixfx/components';
|
|
464
|
+
|
|
465
|
+
bindViewState(grid, {
|
|
466
|
+
load: () => JSON.parse(localStorage.getItem(`photos-grid`) ?? `null`),
|
|
467
|
+
save: s => localStorage.setItem(`photos-grid`, JSON.stringify(s)),
|
|
468
|
+
});
|
|
469
|
+
grid.items = await fetchPhotos(); // pending state applies as items arrive
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
`kind`: `grid-list`. Type: `GridListViewState`.
|
|
473
|
+
|
|
474
|
+
| Field | Shape | Meaning |
|
|
475
|
+
|---|---|---|
|
|
476
|
+
| `selected` | `string[]` | Selected item keys. |
|
|
477
|
+
| `expanded` | `Record<string, boolean>` | Group key → open. Groups default open, so only collapsed groups (`false`) are recorded. |
|
|
478
|
+
| `focus` | `string` | Keyboard-cursor key. Recorded only while the cursor was last moved by keyboard. |
|
|
479
|
+
| `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). |
|
|
480
|
+
|
|
481
|
+
Members: `getViewState(options?)`, `setViewState(state)`,
|
|
482
|
+
`clearPendingViewState()`, and the debounced `view-state-change` event.
|
|
483
|
+
|
|
484
|
+
Grid-specific behaviour:
|
|
485
|
+
|
|
486
|
+
- Works the same in element mode (light-DOM children with `data-key`) and data
|
|
487
|
+
mode (`items` + `itemAdapter`). Keyless children have auto-assigned
|
|
488
|
+
positional keys, which never appear in view state.
|
|
489
|
+
- A key counts as present when its item is in the effective order: an item
|
|
490
|
+
that is filtered out, or inside a collapsed group, keeps its saved selection
|
|
491
|
+
pending until it shows. Restoring `expanded` and `selected` together re-opens
|
|
492
|
+
groups first, so a saved selection inside a saved-open group resolves.
|
|
493
|
+
- A collapsed group whose items all leave (data reset, filter) keeps its
|
|
494
|
+
collapsed state pending and is collapsed again when it comes back.
|
|
495
|
+
Restoring collapse state does not fire `list-group-toggle`.
|
|
496
|
+
- Items that leave the data are deselected **without** a `list-select`; their
|
|
497
|
+
keys go pending and are re-selected (with a `list-select`) if they return.
|
|
498
|
+
- A restored cursor puts the list in keyboard input mode, so it is shown and
|
|
499
|
+
recorded like a keyboard-moved cursor.
|
|
500
|
+
- Scroll restore waits until the anchor item is present and the viewport is
|
|
501
|
+
measured, then until the content is long enough. It is cancelled by the
|
|
502
|
+
user's own wheel, touch, pointer or navigation key. The anchor is re-laid out
|
|
503
|
+
with the current geometry, so a different column count still lands on the
|
|
504
|
+
same item's line.
|
|
505
|
+
|
|
453
506
|
## Virtualisation
|
|
454
507
|
|
|
455
508
|
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.0",
|
|
4
|
+
"generated": "2026-10-23T13:15:45.063Z",
|
|
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.",
|
|
@@ -228,6 +233,11 @@
|
|
|
228
233
|
"summary": "A Lit web component that lays out any number of panels along one axis with draggable gutters between them.",
|
|
229
234
|
"path": "docs-user/split-layout.md"
|
|
230
235
|
},
|
|
236
|
+
{
|
|
237
|
+
"name": "sunburst-tree",
|
|
238
|
+
"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",
|
|
239
|
+
"path": "docs-user/sunburst-tree.md"
|
|
240
|
+
},
|
|
231
241
|
{
|
|
232
242
|
"name": "swipe",
|
|
233
243
|
"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.",
|
|
@@ -268,6 +278,11 @@
|
|
|
268
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",
|
|
269
279
|
"path": "docs-user/tree.md"
|
|
270
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
|
+
},
|
|
271
286
|
{
|
|
272
287
|
"name": "twosplit-layout",
|
|
273
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
|
|
@@ -56,6 +57,7 @@ import '@ixfx/components/data-grid';
|
|
|
56
57
|
- [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
58
|
- [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
59
|
- [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.
|
|
60
|
+
- [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
61
|
- [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
62
|
- [tabs](docs-user/tabs.md): A complete tabbed interface system using several coordinated components:
|
|
61
63
|
- [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.
|
|
@@ -64,6 +66,7 @@ import '@ixfx/components/data-grid';
|
|
|
64
66
|
- [toolbar](docs-user/toolbar.md): Two components for building toolbars:
|
|
65
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.
|
|
66
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
|
|
67
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.
|
|
68
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).
|
|
69
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
|
|
@@ -22,14 +22,14 @@ provider returns that column's choices; items may carry a `count` for display.
|
|
|
22
22
|
| `selectionMode` | `ListSelectionMode` | `'multiple'` | `'none'`, `'single'` or `'multiple'` selection within a column. |
|
|
23
23
|
| `minColumns` | `number` | `1` | Floor for column removal. |
|
|
24
24
|
| `maxColumns` | `number` | `5` | Ceiling for column addition; `0` = unlimited. |
|
|
25
|
-
| `columns` | `readonly MulticolColumnState[]` (getter) | — | Current per-column config: `id`, `categoryKey`, `sort`, `grouped`, `menu` (default `true`), `width`. Persist this to restore a layout. |
|
|
26
25
|
|
|
27
26
|
### Methods
|
|
28
27
|
|
|
29
28
|
```typescript
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
// View state: column layout, per-column selection and scroll (see below)
|
|
30
|
+
getViewState(options?: GetViewStateOptions<MulticolListViewState>): MulticolListViewState
|
|
31
|
+
setViewState(state: Partial<MulticolListViewState>): void
|
|
32
|
+
clearPendingViewState(): void
|
|
33
33
|
|
|
34
34
|
// Add a column for a category key, optionally at a position.
|
|
35
35
|
addColumn(categoryKey: string, position?: number): void
|
|
@@ -49,15 +49,16 @@ clearSelection(): void
|
|
|
49
49
|
autoSizeColumns(options?: { minWidth?: number; maxWidth?: number }): Promise<void>
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
`categories` is assigned.
|
|
52
|
+
Unless a layout is restored with `setViewState({ columns })`, the element auto-seeds
|
|
53
|
+
one column (the first category) once `categories` is assigned.
|
|
54
54
|
|
|
55
55
|
### Events
|
|
56
56
|
|
|
57
57
|
| Event | Detail | Description |
|
|
58
58
|
|-------|--------|-------------|
|
|
59
59
|
| `multicol-select` | `{ columnId, columns }` | A column's selection changed. `columns` is a full snapshot (see `getSelection`). |
|
|
60
|
-
| `multicol-columns-change` | `{ columns, reason }` | Column layout changed. `reason` is one of `'add' \| 'remove' \| 'reorder' \| 'category' \| 'sort' \| 'grouping' \| 'resize'
|
|
60
|
+
| `multicol-columns-change` | `{ columns, reason }` | Column layout changed. `columns` is the live layout (`MulticolColumnState[]`, as in view state). `reason` is one of `'add' \| 'remove' \| 'reorder' \| 'category' \| 'sort' \| 'grouping' \| 'resize'` (a view-state restore reports `'add'`). Resize fires **once on pointer-up** with the final width — never during the drag. |
|
|
61
|
+
| `view-state-change` | `{ state, reason }` | Debounced view-state snapshot; see [View state](#view-state). |
|
|
61
62
|
|
|
62
63
|
---
|
|
63
64
|
|
|
@@ -78,6 +79,47 @@ at the top, selected by default. Choosing values clears All; choosing All clears
|
|
|
78
79
|
|
|
79
80
|
---
|
|
80
81
|
|
|
82
|
+
## View state
|
|
83
|
+
|
|
84
|
+
Implements the shared view-state contract (`docs/infra-view-state.md`); `kind` is
|
|
85
|
+
`multicol-list`. Persist it with `bindViewState()`:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { bindViewState } from '@ixfx/components';
|
|
89
|
+
|
|
90
|
+
bindViewState(el, {
|
|
91
|
+
load: () => JSON.parse(localStorage.getItem(`photos-facets`) ?? `null`),
|
|
92
|
+
save: s => localStorage.setItem(`photos-facets`, JSON.stringify(s)),
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Field | Type | Meaning |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `columns` | `MulticolColumnState[]` | Layout in display order: `{ id, categoryKey, sort, grouped, menu?, width? }`. |
|
|
99
|
+
| `selectedByColumn` | `Record<columnId, string[]>` | Selected item keys per column. A column that's absent (or `[]`) is at 'All'. |
|
|
100
|
+
| `scrollByColumn` | `Record<columnId, ScrollState>` | Each column list's scroll, anchored to the first visible item key. |
|
|
101
|
+
|
|
102
|
+
The shared `selected` / `scroll` / `focus` / `expanded` fields are not used.
|
|
103
|
+
|
|
104
|
+
- **`columns`** re-uses live columns by `id` (re-pointing the category if it differs,
|
|
105
|
+
which resets that column to 'All' and applies the category defaults, then the
|
|
106
|
+
saved `sort` / `grouped` / `width`), creates the others, reorders to match and
|
|
107
|
+
removes columns not listed. A non-empty layout suppresses auto-seeding. A column
|
|
108
|
+
whose category isn't in `categories` stays **pending** and is inserted at its
|
|
109
|
+
saved position once `categories` includes it.
|
|
110
|
+
- **Selection** keys a column's data doesn't contain stay pending and are selected
|
|
111
|
+
when items with those keys arrive (firing the normal `multicol-select` and
|
|
112
|
+
cascade). Keys that leave a column's data are deselected and go pending. Any
|
|
113
|
+
explicit selection change (click, the All row, `clearSelection()`) drops all
|
|
114
|
+
pending selection. Selection saved for a column id that doesn't exist yet is
|
|
115
|
+
handed over when that column is restored.
|
|
116
|
+
- **Scroll** waits until the anchor item has rendered; the user scrolling the
|
|
117
|
+
column cancels it.
|
|
118
|
+
- `getViewState()` includes pending state by default (`{ includePending: false }`
|
|
119
|
+
for live state only).
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
81
123
|
## Header interactions
|
|
82
124
|
|
|
83
125
|
- **Category label** — opens a menu of all categories; the current one is disabled.
|
|
@@ -126,7 +168,7 @@ el.dataProvider = (query: MulticolQuery, signal) => {
|
|
|
126
168
|
};
|
|
127
169
|
|
|
128
170
|
el.addEventListener(`multicol-select`, (event) => {
|
|
129
|
-
|
|
171
|
+
showResults(event.detail.columns);
|
|
130
172
|
});
|
|
131
173
|
```
|
|
132
174
|
|
|
@@ -12,7 +12,7 @@ A Lit web component that lays out any number of panels along one axis with dragg
|
|
|
12
12
|
|-----------|------|---------|-------------|
|
|
13
13
|
| `direction` | `row` \| `column` | `row` | Split direction. `row` creates side-by-side panels, `column` creates stacked panels. |
|
|
14
14
|
| `sizes` | string | `''` | Positional weight shorthand, e.g. `"1 2 1"`, applied to children lacking an explicit `size`. |
|
|
15
|
-
| `
|
|
15
|
+
| `forget-absent` | boolean | `false` | Don't keep the saved size of children that leave (see [View state](#view-state)). |
|
|
16
16
|
| `gutterSize` | string | — | Convenience mirror of `--split-gutter-size`. |
|
|
17
17
|
| `snap` | string | — | Convenience mirror of `--split-snap`. |
|
|
18
18
|
|
|
@@ -116,7 +116,8 @@ Nested `<ixfx-split-layout>` elements create sub-groups:
|
|
|
116
116
|
| `ixfx-split-collapse` | `{ id: string; index: number }` | no | Panel collapsed (drag or API). |
|
|
117
117
|
| `ixfx-split-expand` | `{ id: string; index: number }` | no | Panel expanded. |
|
|
118
118
|
| `ixfx-split-close` | `{ id: string; index: number }` | **yes** | Before a panel is removed; `preventDefault()` vetoes. |
|
|
119
|
-
| `ixfx-split-change` | `{ layout: LayoutSnapshot }` | no | After any size/structure change
|
|
119
|
+
| `ixfx-split-change` | `{ layout: LayoutSnapshot }` | no | After any size/structure change. For persistence use [view state](#view-state). |
|
|
120
|
+
| `view-state-change` | `{ state: SplitLayoutViewState; reason }` | no | Debounced; see [View state](#view-state). |
|
|
120
121
|
|
|
121
122
|
---
|
|
122
123
|
|
|
@@ -194,25 +195,52 @@ split.beginGutterDrag(0, event.pointerId, event.clientX, event.clientY);
|
|
|
194
195
|
handle that starts this drag alongside perpendicular drags on other nested
|
|
195
196
|
layouts, resizing several splits with one gesture.
|
|
196
197
|
|
|
197
|
-
|
|
198
|
+
---
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
// Get current layout
|
|
201
|
-
const layout = split.getLayout();
|
|
202
|
-
// { version: 1, root: { kind: 'group', id: 'g-0', children: [...] } }
|
|
200
|
+
## View state
|
|
203
201
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
202
|
+
`getViewState()` / `setViewState()` snapshot and restore the size and
|
|
203
|
+
collapse of each direct child that has an `id`, as plain JSON (`kind:
|
|
204
|
+
'split-layout'`). The component never persists anything itself — see
|
|
205
|
+
[docs/infra-view-state.md](../../docs/infra-view-state.md) and
|
|
206
|
+
`bindViewState()`.
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
```ts
|
|
209
|
+
import { bindViewState } from '@ixfx/components';
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
bindViewState(split, {
|
|
212
|
+
load: () => JSON.parse(localStorage.getItem(`editor-split`) ?? `null`),
|
|
213
|
+
save: s => localStorage.setItem(`editor-split`, JSON.stringify(s)),
|
|
214
|
+
});
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
+
split.getViewState();
|
|
217
|
+
// { v: 1, kind: 'split-layout',
|
|
218
|
+
// layout: { editor: { weight: 0.7 }, terminal: { weight: 0.3, collapsed: true } } }
|
|
216
219
|
```
|
|
217
220
|
|
|
218
|
-
|
|
221
|
+
| Field | Type | Meaning |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| `layout` | `Record<id, { weight: number; collapsed?: boolean }>` | `weight` is the child's share (0..1) of the weighted flow; for a collapsed child, the share it expands back to. `collapsed` absent ⇒ expanded. |
|
|
224
|
+
|
|
225
|
+
- **Keyed by `id`.** Children without an `id` (and nested layouts without
|
|
226
|
+
one) are not saved — generated ids are positional. A nested
|
|
227
|
+
`ixfx-split-layout` with an `id` is saved as one entry (its share in this
|
|
228
|
+
layout); its own children are in *its* view state.
|
|
229
|
+
- **Pending.** An entry whose child isn't slotted yet is kept and applied when
|
|
230
|
+
a child with that id appears; a child that leaves goes back to pending, so
|
|
231
|
+
removing and re-adding it restores its size. `getViewState()` includes
|
|
232
|
+
pending entries (pass `includePending: false` to leave them out);
|
|
233
|
+
`clearPendingViewState()` drops them.
|
|
234
|
+
- **Partial.** Children without an entry keep their relative size and share
|
|
235
|
+
what the restored children leave.
|
|
236
|
+
- **Events.** `view-state-change` fires (debounced) after a gutter drag ends,
|
|
237
|
+
collapse / expand, close, `setSizes()`, and when pending entries are applied
|
|
238
|
+
(`reason: 'restore'`).
|
|
239
|
+
- `forget-absent` (boolean attribute) turns off the pending behaviour for
|
|
240
|
+
children that leave — for hosts that drive child sizes themselves (used
|
|
241
|
+
by `ixfx-dock-host`).
|
|
242
|
+
|
|
243
|
+
A rebuild caused by children being added / removed keeps the live (e.g.
|
|
244
|
+
dragged) sizes of children that stayed; a changed `size` / `weight` /
|
|
245
|
+
`sizes` / `fixed` / `collapsed` attribute on a direct child or the layout
|
|
246
|
+
makes the DOM the source of truth again.
|