@ixfx/components 0.3.2 → 0.4.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 +1086 -603
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +16153 -15296
- package/bundle/index.js.map +1 -1
- package/bundle/style.css +14 -4
- package/dist/ac-text.d.ts.map +1 -1
- package/dist/ac-text.js +11 -6
- package/dist/ac-text.js.map +1 -1
- package/dist/{button-DMt5XWUK.js → button-C3fLMKUv.js} +4 -4
- package/dist/{button-DMt5XWUK.js.map → button-C3fLMKUv.js.map} +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/checkbox.d.ts +2 -2
- package/dist/checkbox.d.ts.map +1 -1
- package/dist/checkbox.js +14 -8
- package/dist/checkbox.js.map +1 -1
- package/dist/{colour-C3MQIjFJ-BLXg8W3U.js → colour-C3MQIjFJ-vRCdr8W-.js} +146 -552
- package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +1 -0
- package/dist/{colour-picker-K6zu82lm.js → colour-picker-0sgAha1T.js} +14 -9
- package/dist/colour-picker-0sgAha1T.js.map +1 -0
- package/dist/colour-picker.d.ts +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +11 -6
- package/dist/crumbs.js.map +1 -1
- package/dist/data-display.d.ts +9 -4
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +270 -43
- package/dist/data-display.js.map +1 -1
- package/dist/data-provider-DgI909FP.js +23 -0
- package/dist/data-provider-DgI909FP.js.map +1 -0
- package/dist/{decorate-D7rC1gLP.js → decorate-DdjvVHS-.js} +2 -2
- package/dist/{decorate-D7rC1gLP.js.map → decorate-DdjvVHS-.js.map} +1 -1
- package/dist/{defaults-C7elhuyJ.js → defaults-C3b9OWJD.js} +2 -2
- package/dist/{defaults-C7elhuyJ.js.map → defaults-C3b9OWJD.js.map} +1 -1
- package/dist/{dist-TAGYwaju.js → dist-CA13FvMn.js} +7 -66
- package/dist/dist-CA13FvMn.js.map +1 -0
- package/dist/editable-label.d.ts.map +1 -1
- package/dist/editable-label.js +3 -2
- package/dist/editable-label.js.map +1 -1
- package/dist/fallbacks-pNMOMx38.js +168 -0
- package/dist/fallbacks-pNMOMx38.js.map +1 -0
- package/dist/{hex-editor-DlfoA65y.d.ts → hex-editor-CMIxtNgT.d.ts} +1 -1
- package/dist/hex-editor-CMIxtNgT.d.ts.map +1 -0
- package/dist/{hex-editor-CsN_ySkH.js → hex-editor-WZrjRtvL.js} +3 -3
- package/dist/{hex-editor-CsN_ySkH.js.map → hex-editor-WZrjRtvL.js.map} +1 -1
- package/dist/hex.d.ts +1 -1
- package/dist/hex.js +1 -1
- package/dist/{highlight-TPbSHruI.js → highlight-B-eQrhhb.js} +1 -1
- package/dist/{highlight-TPbSHruI.js.map → highlight-B-eQrhhb.js.map} +1 -1
- package/dist/{icon-Dm_DHDz6.d.ts → icon-Bd5BiU4b.d.ts} +3 -3
- package/dist/icon-Bd5BiU4b.d.ts.map +1 -0
- package/dist/{icon-9NWTXtE1.js → icon-Ndo40kNO.js} +6 -5
- package/dist/icon-Ndo40kNO.js.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +3 -3
- package/dist/{incr-search-DSKKsk6w.js → incr-search-CeERfijK.js} +2 -2
- package/dist/{incr-search-DSKKsk6w.js.map → incr-search-CeERfijK.js.map} +1 -1
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +2 -2
- package/dist/{index-hXFn14tM.d.ts → index-BMjri2-S.d.ts} +2 -2
- package/dist/{index-hXFn14tM.d.ts.map → index-BMjri2-S.d.ts.map} +1 -1
- package/dist/{index-DSlPeWLK.d.ts → index-CWpNcQKE.d.ts} +5 -5
- package/dist/index-CWpNcQKE.d.ts.map +1 -0
- package/dist/{index-DLkSKL4T.d.ts → index-CowKi2mo.d.ts} +1 -1
- package/dist/index-CowKi2mo.d.ts.map +1 -0
- package/dist/{index-B1hNR7Z9.d.ts → index-D87kRcEs.d.ts} +1 -1
- package/dist/{index-B1hNR7Z9.d.ts.map → index-D87kRcEs.d.ts.map} +1 -1
- package/dist/{index-CnL9krsH.d.ts → index-DgXpCg_G.d.ts} +32 -26
- package/dist/index-DgXpCg_G.d.ts.map +1 -0
- package/dist/{index-iZLld8Ca.d.ts → index-s_IhSnwc.d.ts} +2 -2
- package/dist/index-s_IhSnwc.d.ts.map +1 -0
- package/dist/index.d.ts +432 -121
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2369 -1706
- package/dist/index.js.map +1 -1
- package/dist/interaction-D6efrBLo.js +1 -0
- package/dist/{keyboard-De_vOHLg.js → keyboard-CimiEskD.js} +1 -1
- package/dist/{keyboard-De_vOHLg.js.map → keyboard-CimiEskD.js.map} +1 -1
- package/dist/{labelled-input-base-CqPL5rKm.js → labelled-input-base-9K_w3oLj.js} +2 -2
- package/dist/{labelled-input-base-CqPL5rKm.js.map → labelled-input-base-9K_w3oLj.js.map} +1 -1
- package/dist/{labelled-input-base-BVvNeb5R.d.ts → labelled-input-base-ORb1rGq6.d.ts} +2 -2
- package/dist/labelled-input-base-ORb1rGq6.d.ts.map +1 -0
- package/dist/labelled-radial-input.d.ts +3 -3
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-radial-input.js +7 -6
- package/dist/labelled-radial-input.js.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-range-input.js +2 -2
- package/dist/led.d.ts +1 -1
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +2 -2
- package/dist/led.js.map +1 -1
- package/dist/{menu-item-CKRfP0pN.js → menu-item-BrIuORYD.js} +20 -10
- package/dist/menu-item-BrIuORYD.js.map +1 -0
- package/dist/{menu-item-ClQF2DKN.d.ts → menu-item-COx-Azgj.d.ts} +1 -1
- package/dist/menu-item-COx-Azgj.d.ts.map +1 -0
- package/dist/{menu-DJJuCm-5.js → menu-vjoiHZ13.js} +30 -17
- package/dist/menu-vjoiHZ13.js.map +1 -0
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +2 -2
- package/dist/miller.d.ts.map +1 -1
- package/dist/miller.js +18 -11
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.d.ts.map +1 -1
- package/dist/narrowed-text.js +7 -5
- package/dist/narrowed-text.js.map +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +5 -4
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +2 -2
- package/dist/plots.js +2 -2
- package/dist/polar-pad.d.ts +1 -1
- package/dist/polar-pad.d.ts.map +1 -1
- package/dist/polar-pad.js +10 -4
- package/dist/polar-pad.js.map +1 -1
- package/dist/{popup-ImtS9KnS.js → popup-DiWMxsWj.js} +1 -1
- package/dist/{popup-ImtS9KnS.js.map → popup-DiWMxsWj.js.map} +1 -1
- package/dist/prominence-DlFShic3.js +104 -0
- package/dist/prominence-DlFShic3.js.map +1 -0
- package/dist/{radial-input-LUSBEycd.d.ts → radial-input-CpQPs1Mp.d.ts} +1 -1
- package/dist/radial-input-CpQPs1Mp.d.ts.map +1 -0
- package/dist/{radial-input-DZA6WaFu.js → radial-input-DZ7eQCcM.js} +11 -6
- package/dist/radial-input-DZ7eQCcM.js.map +1 -0
- package/dist/radial-input.d.ts +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.d.ts +1 -1
- package/dist/range-input.d.ts.map +1 -1
- package/dist/range-input.js +9 -4
- package/dist/range-input.js.map +1 -1
- package/dist/range.d.ts +1 -1
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js +5 -3
- package/dist/range.js.map +1 -1
- package/dist/{registry-Q2gYQCHM.js → registry-CkW7q09Q.js} +1 -1
- package/dist/{registry-Q2gYQCHM.js.map → registry-CkW7q09Q.js.map} +1 -1
- package/dist/selecthorizontal.d.ts +1 -1
- package/dist/selecthorizontal.d.ts.map +1 -1
- package/dist/selecthorizontal.js +4 -3
- package/dist/selecthorizontal.js.map +1 -1
- package/dist/snap-container.d.ts.map +1 -1
- package/dist/snap-container.js +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/split-layout.d.ts.map +1 -1
- package/dist/split-layout.js +6 -5
- package/dist/split-layout.js.map +1 -1
- package/dist/style.css +14 -4
- package/dist/swipe.d.ts.map +1 -1
- package/dist/swipe.js +9 -4
- package/dist/swipe.js.map +1 -1
- package/dist/{tab-list-BnqISNvO.d.ts → tab-list-DLzb-m-I.d.ts} +2 -2
- package/dist/tab-list-DLzb-m-I.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +10 -4
- package/dist/tabs.js.map +1 -1
- package/dist/{tickled-controller-Cq1Va0rR.d.ts → tickled-controller-BjYCzGrU.d.ts} +2 -2
- package/dist/tickled-controller-BjYCzGrU.d.ts.map +1 -0
- package/dist/{tickled-styles-Bg3QbcrD.js → tickled-styles-fNDdqf6l.js} +1 -17
- package/dist/{tickled-styles-Bg3QbcrD.js.map → tickled-styles-fNDdqf6l.js.map} +1 -1
- package/dist/{timeline-Bh0PhlKW.js → timeline-2OFtN4Ta.js} +2 -2
- package/dist/{timeline-Bh0PhlKW.js.map → timeline-2OFtN4Ta.js.map} +1 -1
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/{tooltip-DMxmBNky.js → tooltip-CJt3WLmN.js} +1 -1
- package/dist/{tooltip-DMxmBNky.js.map → tooltip-CJt3WLmN.js.map} +1 -1
- package/dist/{tooltip-CwGfb4lp.d.ts → tooltip-RGVIwHrZ.d.ts} +1 -1
- package/dist/{tooltip-CwGfb4lp.d.ts.map → tooltip-RGVIwHrZ.d.ts.map} +1 -1
- package/dist/{tree-CvvKbO-f.js → tree-CJWpMUvD.js} +113 -103
- package/dist/tree-CJWpMUvD.js.map +1 -0
- package/dist/{tree-component-D8Bg48tt.d.ts → tree-component-BcO26Xvt.d.ts} +65 -3
- package/dist/tree-component-BcO26Xvt.d.ts.map +1 -0
- package/dist/tree.d.ts +15 -15
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +1 -1
- package/dist/{types-BKaqnpwx.d.ts → types-DSfbtHs3.d.ts} +1 -1
- package/dist/types-DSfbtHs3.d.ts.map +1 -0
- package/dist/{xy-axis-DL0tfJDr.js → xy-axis-CK64haaK.js} +1831 -359
- package/dist/xy-axis-CK64haaK.js.map +1 -0
- package/dist/{xy-axis-D9uwpGTT.d.ts → xy-axis-McdUpcYr.d.ts} +102 -3
- package/dist/xy-axis-McdUpcYr.d.ts.map +1 -0
- package/dist/xy-pad.d.ts +1 -1
- package/dist/xy-pad.d.ts.map +1 -1
- package/dist/xy-pad.js +11 -6
- package/dist/xy-pad.js.map +1 -1
- package/docs-user/README.md +45 -0
- package/docs-user/ac-text.md +67 -0
- package/docs-user/ac-token.md +187 -0
- package/docs-user/button.md +355 -0
- package/docs-user/checkbox.md +62 -0
- package/docs-user/colour-picker.md +237 -0
- package/docs-user/crumbs.md +66 -0
- package/docs-user/data-display.md +98 -0
- package/docs-user/editable-label.md +244 -0
- package/docs-user/grouped-item-lister.md +424 -0
- package/docs-user/icons.md +191 -0
- package/docs-user/incr-search.md +94 -0
- package/docs-user/labelled-radial-input.md +204 -0
- package/docs-user/labelled-range-input.md +194 -0
- package/docs-user/led.md +40 -0
- package/docs-user/menu.md +319 -0
- package/docs-user/miller.md +453 -0
- package/docs-user/narrowed-text.md +72 -0
- package/docs-user/panel.md +250 -0
- package/docs-user/plots.md +361 -0
- package/docs-user/polar-pad.md +228 -0
- package/docs-user/radial-input.md +559 -0
- package/docs-user/range-input.md +264 -0
- package/docs-user/range.md +12 -0
- package/docs-user/selecthorizontal.md +32 -0
- package/docs-user/snap-container.md +132 -0
- package/docs-user/split-layout.md +232 -0
- package/docs-user/swipe.md +44 -0
- package/docs-user/tabs.md +217 -0
- package/docs-user/timeline.md +116 -0
- package/docs-user/transitory-label.md +120 -0
- package/docs-user/tree.md +592 -0
- package/docs-user/user-catalog.md +165 -0
- package/docs-user/user-theming.md +379 -0
- package/docs-user/util.md +122 -0
- package/docs-user/vertical-list.md +351 -0
- package/docs-user/xy-pad.md +94 -0
- package/package.json +18 -17
- package/dist/colour-C3MQIjFJ-BLXg8W3U.js.map +0 -1
- package/dist/colour-picker-K6zu82lm.js.map +0 -1
- package/dist/dist-BX0OVIm7.js +0 -1290
- package/dist/dist-BX0OVIm7.js.map +0 -1
- package/dist/dist-TAGYwaju.js.map +0 -1
- package/dist/hex-editor-DlfoA65y.d.ts.map +0 -1
- package/dist/icon-9NWTXtE1.js.map +0 -1
- package/dist/icon-Dm_DHDz6.d.ts.map +0 -1
- package/dist/index-CnL9krsH.d.ts.map +0 -1
- package/dist/index-DLkSKL4T.d.ts.map +0 -1
- package/dist/index-DSlPeWLK.d.ts.map +0 -1
- package/dist/index-iZLld8Ca.d.ts.map +0 -1
- package/dist/interaction-DG7X1rhI.js +0 -1
- package/dist/labelled-input-base-BVvNeb5R.d.ts.map +0 -1
- package/dist/menu-DJJuCm-5.js.map +0 -1
- package/dist/menu-item-CKRfP0pN.js.map +0 -1
- package/dist/menu-item-ClQF2DKN.d.ts.map +0 -1
- package/dist/prominence--ic9uWBl.js +0 -98
- package/dist/prominence--ic9uWBl.js.map +0 -1
- package/dist/radial-input-DZA6WaFu.js.map +0 -1
- package/dist/radial-input-LUSBEycd.d.ts.map +0 -1
- package/dist/tab-list-BnqISNvO.d.ts.map +0 -1
- package/dist/tickled-controller-Cq1Va0rR.d.ts.map +0 -1
- package/dist/tree-CvvKbO-f.js.map +0 -1
- package/dist/tree-component-D8Bg48tt.d.ts.map +0 -1
- package/dist/types-BKaqnpwx.d.ts.map +0 -1
- package/dist/xy-axis-D9uwpGTT.d.ts.map +0 -1
- package/dist/xy-axis-DL0tfJDr.js.map +0 -1
- /package/bundle/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
- /package/dist/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# Miller Columns
|
|
2
|
+
|
|
3
|
+
A Miller column component displays hierarchical data as multiple vertical columns, like macOS Finder or email clients. Each column shows the children of the selected item in the previous column.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## MillerBaseElement / MillerListElement
|
|
8
|
+
|
|
9
|
+
### Attributes
|
|
10
|
+
|
|
11
|
+
| Attribute | Type | Default | Description |
|
|
12
|
+
|-----------|------|---------|-------------|
|
|
13
|
+
| `headers` | boolean | `false` | Show column headers labeling each column with its parent item |
|
|
14
|
+
|
|
15
|
+
### Properties
|
|
16
|
+
|
|
17
|
+
| Property | Type | Default | Description |
|
|
18
|
+
|----------|------|---------|-------------|
|
|
19
|
+
| `root` | `MillerNode \| undefined` | — | The root node of the tree. Assigning a new root resets all columns and selection state. |
|
|
20
|
+
| `selectionMode` | `MillerSelectionMode` | `'implicit'` | Controls how items are selected. See [Selection Modes](#selection-modes) below. |
|
|
21
|
+
| `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
|
+
| `loadChildren` | `MillerLoadChildrenCallback \| undefined` | — | Callback to load children for lazy nodes. If undefined, clicking a lazy node opens an empty column. |
|
|
23
|
+
|
|
24
|
+
### CSS Variables
|
|
25
|
+
|
|
26
|
+
These can be overridden in your CSS:
|
|
27
|
+
|
|
28
|
+
| Variable | Default | Description |
|
|
29
|
+
|----------|---------|-------------|
|
|
30
|
+
| `--column-bg` | `var(--surface-l)` | Background color of columns |
|
|
31
|
+
| `--column-header-bg` | `var(--surface)` | Background color of column headers |
|
|
32
|
+
| `--column-header-text` | `var(--surface-muted-text)` | Text color of column headers |
|
|
33
|
+
| `--column-separator` | `var(--surface-h)` | Color of column borders/dividers |
|
|
34
|
+
| `--column-resize-handle` | `var(--surface-h)` | Color of resize handles |
|
|
35
|
+
| `--column-resize-handle-hover` | `var(--accent)` | Color of resize handle on hover |
|
|
36
|
+
| `--column-resize-width` | `4px` | Width of resize handles |
|
|
37
|
+
| `--item-hover-bg` | `var(--item-bg-tickled)` | Background on item hover |
|
|
38
|
+
| `--item-selected-bg` | `var(--item-bg-selected)` | Background of selected item |
|
|
39
|
+
| `--item-selected-text` | `var(--item-text-selected)` | Text color of selected item |
|
|
40
|
+
|
|
41
|
+
Inherited from theme:
|
|
42
|
+
- `--font-family` - Font family
|
|
43
|
+
- `--font-weight-bold` - Bold font weight
|
|
44
|
+
- `--text-s`, `--text-m`, `--text-l` - Font sizes
|
|
45
|
+
- `--space-xs`, `--space-s`, `--space-m` - Spacing
|
|
46
|
+
- `--radius-s` - Border radius
|
|
47
|
+
- `--transition` - Transition timing
|
|
48
|
+
|
|
49
|
+
### Methods
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Navigate to a node by key path (root-to-target keys, excluding root itself).
|
|
53
|
+
// Only works with already-loaded children — returns false if any node along
|
|
54
|
+
// the path has unloaded children or if a key is not found.
|
|
55
|
+
navigateTo(keys: readonly string[]): boolean
|
|
56
|
+
|
|
57
|
+
// Navigate to the first loaded node (breadth-first) matching a predicate.
|
|
58
|
+
// Returns false if no match is found in the currently loaded tree.
|
|
59
|
+
navigateToNode(predicate: (node: MillerNode) => boolean): boolean
|
|
60
|
+
|
|
61
|
+
// Get current configuration (column widths, etc.)
|
|
62
|
+
getConfig(): MillerConfig
|
|
63
|
+
|
|
64
|
+
// Set configuration. Requires a full MillerConfig object.
|
|
65
|
+
setConfig(config: MillerConfig): void
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Events
|
|
69
|
+
|
|
70
|
+
All events bubble and are composed.
|
|
71
|
+
|
|
72
|
+
| Event | Detail | Description |
|
|
73
|
+
|-------|--------|-------------|
|
|
74
|
+
| `expand` | `{ node: MillerNode, depth: number }` | FYI - fired after a column opens. |
|
|
75
|
+
| `collapse` | `{ node: MillerNode, depth: number }` | FYI - fired when a column closes. |
|
|
76
|
+
| `item-click` | `{ node: MillerNode, depth: number }` | Fired when any item is clicked. |
|
|
77
|
+
| `select` | `{ node: MillerNode, depth: number }` | Fired when Enter is pressed on an item. |
|
|
78
|
+
| `header-click` | `{ depth: number, label: string, path: MillerNode[] }` | Fired when a column header is clicked (requires `headers` attribute). `path` is the ancestry chain from root down to the node whose label is shown in the header. |
|
|
79
|
+
| `change` | — | Fired when selection changes in `checked` mode (checkbox toggled). |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Selection Modes
|
|
84
|
+
|
|
85
|
+
Set via the `selectionMode` property (not an HTML attribute). Default: `'implicit'`.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
type MillerSelectionMode = 'implicit' | 'standard' | 'manual' | 'checked' | 'vscode';
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `implicit` (default)
|
|
92
|
+
|
|
93
|
+
Selection follows the cursor path — whatever is open in the column pane is considered selected. This is the classic Miller column behaviour and is unchanged from prior versions.
|
|
94
|
+
|
|
95
|
+
### `standard`
|
|
96
|
+
|
|
97
|
+
Explicit multi-select similar to a file manager:
|
|
98
|
+
|
|
99
|
+
| Input | Action |
|
|
100
|
+
|-------|--------|
|
|
101
|
+
| Click | Single-select item and expand (sets anchor, clears others) |
|
|
102
|
+
| Shift+Click | Contiguous range from anchor to item (within same depth) |
|
|
103
|
+
| Cmd/Ctrl+Click | Toggle item without expanding (no anchor change) |
|
|
104
|
+
| Arrow Up/Down | Move focus; re-seats anchor |
|
|
105
|
+
| Shift+Arrow | Extend range from anchor (bootstraps anchor from current position if unset) |
|
|
106
|
+
| Enter | Select focused item (sets anchor) |
|
|
107
|
+
| Shift+Enter | Extend range to focused item |
|
|
108
|
+
|
|
109
|
+
### `vscode`
|
|
110
|
+
|
|
111
|
+
Extends `standard` with persistent multi-selection across cursor movement:
|
|
112
|
+
|
|
113
|
+
| Input | Action |
|
|
114
|
+
|-------|--------|
|
|
115
|
+
| Click | Single-select item and expand (clears others) — same as `standard` |
|
|
116
|
+
| Shift+Click | Contiguous range from anchor — same as `standard` |
|
|
117
|
+
| Cmd/Ctrl+Click | Toggle item without expanding — same as `standard` |
|
|
118
|
+
| Arrow Up/Down | Move cursor **only** — selection is preserved, anchor is not re-seated |
|
|
119
|
+
| Shift+Arrow | Extend range from anchor — same as `standard` |
|
|
120
|
+
| Arrow Right | Expand/navigate into branch — selection is preserved (does not clear) |
|
|
121
|
+
| Enter | Add focused item to selection (sets anchor) |
|
|
122
|
+
| Shift+Enter | Extend range to focused item |
|
|
123
|
+
|
|
124
|
+
The key difference from `standard`: in `vscode` mode the cursor is free to move without disturbing the accumulated multi-selection. Only a plain unmodified click clears the selection.
|
|
125
|
+
|
|
126
|
+
### `manual`
|
|
127
|
+
|
|
128
|
+
The component never modifies `selectedKeys`. All clicks and keyboard actions fire events as normal but selection state is entirely driven by the caller via the public API. Use this when you want to implement custom selection logic.
|
|
129
|
+
|
|
130
|
+
### `checked`
|
|
131
|
+
|
|
132
|
+
A checkbox appears beside each item. Clicking the row navigates/expands as usual; clicking the checkbox toggles selection. Selecting a branch recursively selects all loaded descendants. Checkbox state follows a three-state cycle: **indeterminate → checked → unchecked**.
|
|
133
|
+
|
|
134
|
+
- **Checked**: item key is in `selectedKeys`
|
|
135
|
+
- **Unchecked**: item key is not in `selectedKeys` and no loaded descendants are selected
|
|
136
|
+
- **Indeterminate**: some (but not all) loaded descendants are selected
|
|
137
|
+
|
|
138
|
+
The `change` event is fired on the component after every checkbox toggle.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Selection API
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Read current selection
|
|
146
|
+
const keys: ReadonlySet<string> = el.selectedKeys;
|
|
147
|
+
|
|
148
|
+
// Replace entire selection (triggers re-render)
|
|
149
|
+
el.selectedKeys = ['key-a', 'key-b'];
|
|
150
|
+
|
|
151
|
+
// Select or deselect a single node
|
|
152
|
+
// In 'checked' mode, selecting a branch recursively selects all loaded descendants
|
|
153
|
+
el.setSelected(node: MillerNode, selected: boolean): void
|
|
154
|
+
|
|
155
|
+
// Return all currently-selected MillerNodes (searches loaded tree only)
|
|
156
|
+
el.getSelected(): MillerNode[]
|
|
157
|
+
|
|
158
|
+
// Clear all selection state
|
|
159
|
+
el.clearSelection(): void
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Types
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
type MillerItem = {
|
|
168
|
+
key: string; // Unique identifier
|
|
169
|
+
label: string; // Display text
|
|
170
|
+
isLeaf?: boolean; // If true, clicking never fires 'expand'
|
|
171
|
+
data?: unknown; // Arbitrary user data
|
|
172
|
+
icon?: string; // Optional icon name
|
|
173
|
+
tooltip?: string; // Optional tooltip text
|
|
174
|
+
value?: string; // Optional string value
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
type MillerNode = {
|
|
178
|
+
item: MillerItem;
|
|
179
|
+
children?: MillerNode[]; // undefined = unloaded, [] = confirmed empty
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
type MillerConfig = {
|
|
183
|
+
columnWidths: Record<number, number>; // depth -> px
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Basic Usage
|
|
190
|
+
|
|
191
|
+
### Static Columns
|
|
192
|
+
|
|
193
|
+
```html
|
|
194
|
+
<ixfx-miller-list id="miller" headers></ixfx-miller-list>
|
|
195
|
+
|
|
196
|
+
<script type="module">
|
|
197
|
+
const el = document.getElementById('miller');
|
|
198
|
+
|
|
199
|
+
el.root = {
|
|
200
|
+
item: { key: 'root', label: 'My Computer' },
|
|
201
|
+
children: [
|
|
202
|
+
{
|
|
203
|
+
item: { key: 'c', label: 'C:' },
|
|
204
|
+
children: [
|
|
205
|
+
{
|
|
206
|
+
item: { key: 'users', label: 'Users' },
|
|
207
|
+
children: [
|
|
208
|
+
{ item: { key: 'jane', label: 'Jane', isLeaf: true } },
|
|
209
|
+
{ item: { key: 'barry', label: 'Barry', isLeaf: true } }
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
{ item: { key: 'windows', label: 'Windows', isLeaf: true } }
|
|
213
|
+
]
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
item: { key: 'd', label: 'D:' },
|
|
217
|
+
children: [
|
|
218
|
+
{ item: { key: 'games', label: 'Games', isLeaf: true } }
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Respond to clicks
|
|
225
|
+
el.addEventListener('item-click', ({ detail }) => {
|
|
226
|
+
console.log('Clicked:', detail.node.item.label, 'at column', detail.depth);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Respond to header clicks (if headers enabled)
|
|
230
|
+
el.addEventListener('header-click', ({ detail }) => {
|
|
231
|
+
console.log('Header clicked:', detail.label, 'at depth', detail.depth);
|
|
232
|
+
console.log('Path:', detail.path.map(n => n.item.label));
|
|
233
|
+
});
|
|
234
|
+
</script>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Lazy Loading
|
|
238
|
+
|
|
239
|
+
Set the `loadChildren` callback to handle loading children on demand. The callback
|
|
240
|
+
receives the node, depth, and an AbortSignal. It should return the children array
|
|
241
|
+
(or a Promise that resolves to it). The callback is debounced by 150ms.
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<ixfx-miller-list id="miller"></ixfx-miller-list>
|
|
245
|
+
|
|
246
|
+
<script type="module">
|
|
247
|
+
const el = document.getElementById('miller');
|
|
248
|
+
|
|
249
|
+
// Children at root level are pre-loaded; deeper nodes will be loaded on demand
|
|
250
|
+
el.root = {
|
|
251
|
+
item: { key: 'root', label: 'Root' },
|
|
252
|
+
children: [
|
|
253
|
+
{ item: { key: 'a', label: 'Alpha' } }, // children: undefined → lazy
|
|
254
|
+
{ item: { key: 'b', label: 'Beta' } },
|
|
255
|
+
]
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
el.loadChildren = async (node, depth, signal) => {
|
|
259
|
+
// Return children array (sync or async)
|
|
260
|
+
return fetchChildren(node.item.key, signal);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
async function fetchChildren(key, signal) {
|
|
264
|
+
const response = await fetch(`/api/children/${key}`, { signal });
|
|
265
|
+
return response.json();
|
|
266
|
+
}
|
|
267
|
+
</script>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
If no `loadChildren` callback is set, clicking a lazy node opens an empty column.
|
|
271
|
+
|
|
272
|
+
**FYI Events:**
|
|
273
|
+
- `expand`: fired after a column opens. Detail: `{ node, depth }`
|
|
274
|
+
- `collapse`: fired when a column closes (user navigates away or load is aborted). Detail: `{ node, depth }`
|
|
275
|
+
|
|
276
|
+
### Programmatic Navigation
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
// Navigate by key path — keys are item.key values from root's children downwards.
|
|
280
|
+
// Returns false (silently) if any node along the path has not yet been loaded.
|
|
281
|
+
el.navigateTo(['c', 'users', 'jane']);
|
|
282
|
+
|
|
283
|
+
// Or find by predicate across already-loaded nodes
|
|
284
|
+
el.navigateToNode(node => node.item.label === 'System32');
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Column Resizing
|
|
288
|
+
|
|
289
|
+
Columns can be resized by dragging the handle on the right edge of each column. The widths are persisted in the config:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
// Serialize current state (e.g. to localStorage)
|
|
293
|
+
const config = el.getConfig();
|
|
294
|
+
localStorage.setItem('millerConfig', JSON.stringify(config));
|
|
295
|
+
|
|
296
|
+
// Restore saved state
|
|
297
|
+
el.setConfig(JSON.parse(localStorage.getItem('millerConfig')));
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Configuration
|
|
301
|
+
|
|
302
|
+
```javascript
|
|
303
|
+
// Set custom column widths (depth index → width in px)
|
|
304
|
+
el.setConfig({ columnWidths: { 0: 150, 1: 200, 2: 180 } });
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## MillerTreeController
|
|
310
|
+
|
|
311
|
+
Bridge an ixfx `TraversableTree` to a Miller component. Children are populated
|
|
312
|
+
lazily — the controller handles `expand` events by synchronously reading from
|
|
313
|
+
the underlying tree.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { Trees } from '@ixfx/collections';
|
|
317
|
+
import { MillerTreeController } from './miller-tree-controller.js';
|
|
318
|
+
|
|
319
|
+
const root = Trees.Mutable.createNode({ key: 'root', label: 'Root' });
|
|
320
|
+
Trees.Mutable.addValue({ key: 'a', label: 'Alpha' }, root);
|
|
321
|
+
|
|
322
|
+
const ctrl = new MillerTreeController(
|
|
323
|
+
Trees.Mutable.asDynamicTraversable(root),
|
|
324
|
+
document.querySelector('ixfx-miller-list'),
|
|
325
|
+
{
|
|
326
|
+
// Both the value and the raw tree node are provided so you can inspect
|
|
327
|
+
// child count, identity, etc. when building the MillerItem.
|
|
328
|
+
valueToItem: (value, treeNode) => ({
|
|
329
|
+
key: value.key,
|
|
330
|
+
label: value.label,
|
|
331
|
+
isLeaf: value.isLeaf
|
|
332
|
+
})
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
ctrl.connect();
|
|
337
|
+
|
|
338
|
+
// Navigate to a value — ancestors with unloaded children are populated first
|
|
339
|
+
ctrl.navigateToValue(v => v.label === 'Target');
|
|
340
|
+
|
|
341
|
+
// Clean up event listeners when done
|
|
342
|
+
ctrl.disconnect();
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### MillerTreeControllerOptions
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
type MillerTreeControllerOptions<T> = {
|
|
349
|
+
// Convert a tree node's value to a MillerItem.
|
|
350
|
+
// key and label are required. If isLeaf is omitted, it is inferred
|
|
351
|
+
// from whether the TraversableTree node has any children.
|
|
352
|
+
valueToItem: (value: T, node: TraversableTree<T>) => MillerItem;
|
|
353
|
+
};
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Methods
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
// Wire up the controller — sets element.root and provides loadChildren callback.
|
|
360
|
+
// Safe to call again after disconnect().
|
|
361
|
+
connect(): void
|
|
362
|
+
|
|
363
|
+
// Remove all event listeners and release internal state.
|
|
364
|
+
disconnect(): void
|
|
365
|
+
|
|
366
|
+
// Navigate the component to the first node (depth-first) whose value matches predicate.
|
|
367
|
+
// Any ancestor MillerNodes with unloaded children are populated synchronously first.
|
|
368
|
+
// Returns false if no matching node is found.
|
|
369
|
+
navigateToValue(predicate: (value: T) => boolean): boolean
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Keyboard Navigation
|
|
375
|
+
|
|
376
|
+
Miller columns support full keyboard navigation when the component has focus:
|
|
377
|
+
|
|
378
|
+
| Key | Action |
|
|
379
|
+
|-----|--------|
|
|
380
|
+
| `Arrow Up` | Move focus to previous item in column |
|
|
381
|
+
| `Arrow Down` | Move focus to next item in column |
|
|
382
|
+
| `Arrow Right` | Select focused item and open its children (if any) |
|
|
383
|
+
| `Arrow Left` | Go back to previous column |
|
|
384
|
+
| `Enter` | Fire `select` event on focused item; in `standard` mode also selects and sets anchor |
|
|
385
|
+
| `Shift+Arrow` | In `standard` mode: extend selection range from anchor |
|
|
386
|
+
| `Shift+Enter` | In `standard` mode: extend selection range to focused item |
|
|
387
|
+
| `Escape` | Cancel pending lazy load / go back one column |
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Subclassing MillerBaseElement
|
|
392
|
+
|
|
393
|
+
Create custom column renderers by subclassing `MillerBaseElement`. Override
|
|
394
|
+
`renderColumnContent` to control how items are rendered. The following protected
|
|
395
|
+
members are available for subclasses to use:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
// True if node is currently selected at the given depth
|
|
399
|
+
_isSelected(node: MillerNode, depth: number): boolean
|
|
400
|
+
|
|
401
|
+
// True if node has keyboard focus at the given depth
|
|
402
|
+
_isFocused(node: MillerNode, depth: number): boolean
|
|
403
|
+
|
|
404
|
+
// Returns the index of the focused item at the given depth
|
|
405
|
+
_getFocusedIndex(depth: number): number
|
|
406
|
+
|
|
407
|
+
// Call this to handle an item click (selection, expand, events)
|
|
408
|
+
_handleItemClick(node: MillerNode, depth: number): void
|
|
409
|
+
|
|
410
|
+
// Handle a checkbox click for the given node (used in 'checked' mode).
|
|
411
|
+
// Returns the new checked state (true = selected) after the toggle.
|
|
412
|
+
// Also fires the 'change' event on the element.
|
|
413
|
+
_handleCheckboxClick(event: MouseEvent, node: MillerNode, depth: number): boolean
|
|
414
|
+
|
|
415
|
+
// The current selection set — read/write in subclasses
|
|
416
|
+
_selectedKeys: Set<string>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Example:
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
import { MillerBaseElement } from './miller-base.js';
|
|
423
|
+
import { html, css } from 'lit';
|
|
424
|
+
import { customElement } from 'lit/decorators.js';
|
|
425
|
+
import type { MillerNode } from './types.js';
|
|
426
|
+
|
|
427
|
+
@customElement('ixfx-miller-custom')
|
|
428
|
+
export class MillerCustomElement extends MillerBaseElement {
|
|
429
|
+
protected override renderColumnContent(nodes: MillerNode[], depth: number) {
|
|
430
|
+
return html`
|
|
431
|
+
<div class="custom-column">
|
|
432
|
+
${nodes.map(node => html`
|
|
433
|
+
<div
|
|
434
|
+
class="custom-item ${this._isSelected(node, depth) ? 'selected' : ''}
|
|
435
|
+
${this._isFocused(node, depth) ? 'focused' : ''}"
|
|
436
|
+
@click=${() => this._handleItemClick(node, depth)}
|
|
437
|
+
>
|
|
438
|
+
${node.item.label}
|
|
439
|
+
</div>
|
|
440
|
+
`)}
|
|
441
|
+
</div>
|
|
442
|
+
`;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
static override styles = [
|
|
446
|
+
...MillerBaseElement.styles,
|
|
447
|
+
css`
|
|
448
|
+
.custom-item.selected { background: var(--item-selected-bg); }
|
|
449
|
+
.custom-item.focused { outline: 2px solid var(--accent); }
|
|
450
|
+
`,
|
|
451
|
+
];
|
|
452
|
+
}
|
|
453
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# `ixfx-narrowed-text`
|
|
2
|
+
|
|
3
|
+
A filtered text selection component with inline search. Shows slotted items filtered by a query string as the user types.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<ixfx-narrowed-text selection-mode="single" value="beta">
|
|
7
|
+
<span data-value="alpha">Alpha</span>
|
|
8
|
+
<span data-value="beta">Beta</span>
|
|
9
|
+
<span data-value="gamma">Gamma</span>
|
|
10
|
+
</ixfx-narrowed-text>
|
|
11
|
+
|
|
12
|
+
<ixfx-narrowed-text selection-mode="multiple">
|
|
13
|
+
<span data-value="opt1" data-label="Option One">Option One</span>
|
|
14
|
+
<span data-value="opt2" data-label="Option Two">Option Two</span>
|
|
15
|
+
</ixfx-narrowed-text>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Properties / attributes
|
|
19
|
+
|
|
20
|
+
| Attribute / Property | Type | Default | Description |
|
|
21
|
+
|-------------------------|-----------------|-------------|------------------------------------------------|
|
|
22
|
+
| `selectionMode` | `'single' \| 'multiple'` | `'single'` | Number of items that can be selected |
|
|
23
|
+
| `requiredSelection` | `boolean` | `false` | Prevent deselecting last item in single mode |
|
|
24
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction |
|
|
25
|
+
| `nowrap` | `boolean` | `false` | Prevent items from wrapping |
|
|
26
|
+
| `separator` | `string` | `' '` | Separator for multi-value string |
|
|
27
|
+
| `width` | `string \| undefined` | `undefined` | Container width (e.g. `'200px'`) |
|
|
28
|
+
| `height` | `string \| undefined` | `undefined` | Container height |
|
|
29
|
+
| `preserveFilter` | `boolean` | `false` | Keep filter query after selection |
|
|
30
|
+
| `value` | `string` | `''` | Selected value(s) as string |
|
|
31
|
+
|
|
32
|
+
### CSS parts
|
|
33
|
+
|
|
34
|
+
| Part | Description |
|
|
35
|
+
|------------|---------------------|
|
|
36
|
+
| `container` | Main container |
|
|
37
|
+
| `items` | Items container |
|
|
38
|
+
| `item` | Individual item |
|
|
39
|
+
| `label` | Item label text |
|
|
40
|
+
|
|
41
|
+
### Events
|
|
42
|
+
|
|
43
|
+
| Event | Detail | Description |
|
|
44
|
+
|----------|-------------------------------------------------|----------------------------|
|
|
45
|
+
| `change` | `{ item, query, selected }` | Fired when selection changes |
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
const nt = document.querySelector('ixfx-narrowed-text');
|
|
49
|
+
nt.addEventListener('change', e => {
|
|
50
|
+
console.log(e.detail.selected); // array of selected items
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Properties of slotted items
|
|
55
|
+
|
|
56
|
+
Items should have `data-value` attribute. Optional attributes:
|
|
57
|
+
- `data-label`: Display label (defaults to textContent)
|
|
58
|
+
- `data-description`: Optional description text
|
|
59
|
+
- `data-icon-name`: Optional icon name
|
|
60
|
+
|
|
61
|
+
### CSS custom properties
|
|
62
|
+
|
|
63
|
+
| Property | Default | Description |
|
|
64
|
+
|----------------------------------------------|---------|--------------------------------------|
|
|
65
|
+
| `--item-padding` | `var(--space-xs) var(--space-m)` | Item padding |
|
|
66
|
+
| `--item-border-radius` | `var(--radius-s)` | Item border radius |
|
|
67
|
+
| `--item-icon-spacing` | `var(--space-xs)` | Gap between icon and label |
|
|
68
|
+
| `--item-height` | `28px` | Minimum item container height |
|
|
69
|
+
| `--item-label-font-style-default` | `normal` | Default font style |
|
|
70
|
+
| `--item-label-text-decoration-tickled` | `underline dotted` | Tickled item decoration |
|
|
71
|
+
| `--highlight-background` | `var(--accent)` | Query match highlight bg |
|
|
72
|
+
| `--highlight-color` | `var(--accent-text)` | Query match highlight color |
|