@ixfx/components 0.7.4 → 0.8.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.
Files changed (101) hide show
  1. package/bundle/index.d.ts +787 -190
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +2770 -592
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-token.js +1 -1
  6. package/dist/crumbs.d.ts +5 -1
  7. package/dist/crumbs.d.ts.map +1 -1
  8. package/dist/crumbs.js +10 -0
  9. package/dist/crumbs.js.map +1 -1
  10. package/dist/{data-grid-BddUjAti.d.ts → data-grid-D-z7bx5-.d.ts} +26 -1
  11. package/dist/data-grid-D-z7bx5-.d.ts.map +1 -0
  12. package/dist/data-grid.d.ts +1 -1
  13. package/dist/data-grid.js +88 -7
  14. package/dist/data-grid.js.map +1 -1
  15. package/dist/{detail-list-Br0WAnry.d.ts → detail-list-_ZJHYEBr.d.ts} +20 -12
  16. package/dist/detail-list-_ZJHYEBr.d.ts.map +1 -0
  17. package/dist/detail-list.d.ts +2 -2
  18. package/dist/detail-list.js +51 -135
  19. package/dist/detail-list.js.map +1 -1
  20. package/dist/grid-list-nRUQOcEL.js +1537 -0
  21. package/dist/grid-list-nRUQOcEL.js.map +1 -0
  22. package/dist/{grid-list-B_orPsot.d.ts → grid-list-u33vmKzb.d.ts} +128 -10
  23. package/dist/grid-list-u33vmKzb.d.ts.map +1 -0
  24. package/dist/grid-list.d.ts +1 -1
  25. package/dist/grid-list.js +1 -932
  26. package/dist/incr-search-C05WjbZh.js +999 -0
  27. package/dist/incr-search-C05WjbZh.js.map +1 -0
  28. package/dist/incr-search.d.ts +2 -2
  29. package/dist/incr-search.js +2 -3
  30. package/dist/index-h6gxtfHH.d.ts +397 -0
  31. package/dist/index-h6gxtfHH.d.ts.map +1 -0
  32. package/dist/index.d.ts +62 -13
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +343 -17
  35. package/dist/index.js.map +1 -1
  36. package/dist/led.d.ts +33 -1
  37. package/dist/led.d.ts.map +1 -1
  38. package/dist/led.js +157 -13
  39. package/dist/led.js.map +1 -1
  40. package/dist/{list-selection-controller-CUo-Hhcm.js → list-selection-controller-DTFI40li.js} +2 -76
  41. package/dist/list-selection-controller-DTFI40li.js.map +1 -0
  42. package/dist/miller.d.ts +81 -3
  43. package/dist/miller.d.ts.map +1 -1
  44. package/dist/miller.js +327 -68
  45. package/dist/miller.js.map +1 -1
  46. package/dist/narrowed-text.js +1 -2
  47. package/dist/narrowed-text.js.map +1 -1
  48. package/dist/panel.d.ts +3 -1
  49. package/dist/panel.d.ts.map +1 -1
  50. package/dist/panel.js +31 -1
  51. package/dist/panel.js.map +1 -1
  52. package/dist/plots.d.ts +2 -2
  53. package/dist/plots.js +1 -1
  54. package/dist/{titlebar-Bj5pfdw_.d.ts → titlebar-YQ-jVQoS.d.ts} +2 -2
  55. package/dist/{titlebar-Bj5pfdw_.d.ts.map → titlebar-YQ-jVQoS.d.ts.map} +1 -1
  56. package/dist/titlebar.d.ts +1 -1
  57. package/dist/titlebar.js +83 -1
  58. package/dist/titlebar.js.map +1 -1
  59. package/dist/{tree-B74zdI60.js → tree-Chptss7I.js} +77 -15
  60. package/dist/tree-Chptss7I.js.map +1 -0
  61. package/dist/{tree-component-BvV4muxx.d.ts → tree-component-F6qZ0Gdv.d.ts} +9 -1
  62. package/dist/{tree-component-BvV4muxx.d.ts.map → tree-component-F6qZ0Gdv.d.ts.map} +1 -1
  63. package/dist/tree.d.ts +22 -1
  64. package/dist/tree.d.ts.map +1 -1
  65. package/dist/tree.js +1 -1
  66. package/dist/{vertical-list-CQSEVeOT.d.ts → vertical-list-CFuFDygi.d.ts} +17 -14
  67. package/dist/vertical-list-CFuFDygi.d.ts.map +1 -0
  68. package/dist/vertical-list.d.ts +1 -1
  69. package/dist/vertical-list.js +59 -127
  70. package/dist/vertical-list.js.map +1 -1
  71. package/dist/{xy-axis-5KhVTNoX.d.ts → xy-axis-BfP9hKLj.d.ts} +25 -2
  72. package/dist/{xy-axis-5KhVTNoX.d.ts.map → xy-axis-BfP9hKLj.d.ts.map} +1 -1
  73. package/dist/{xy-axis-VrX8dmvS.js → xy-axis-DzM1brBZ.js} +141 -8
  74. package/dist/xy-axis-DzM1brBZ.js.map +1 -0
  75. package/docs-user/data-grid.md +19 -0
  76. package/docs-user/detail-list.md +1 -1
  77. package/docs-user/grid-list.md +120 -11
  78. package/docs-user/incr-search.md +12 -2
  79. package/docs-user/index.json +3 -3
  80. package/docs-user/led.md +37 -7
  81. package/docs-user/llms.txt +1 -1
  82. package/docs-user/panel.md +68 -13
  83. package/docs-user/titlebar.md +3 -4
  84. package/docs-user/tree.md +22 -1
  85. package/docs-user/vertical-list.md +7 -1
  86. package/llms.txt +1 -1
  87. package/package.json +1 -1
  88. package/dist/data-grid-BddUjAti.d.ts.map +0 -1
  89. package/dist/detail-list-Br0WAnry.d.ts.map +0 -1
  90. package/dist/element-search-Y8SRfPGM.js +0 -185
  91. package/dist/element-search-Y8SRfPGM.js.map +0 -1
  92. package/dist/grid-list-B_orPsot.d.ts.map +0 -1
  93. package/dist/grid-list.js.map +0 -1
  94. package/dist/incr-search-vCzuao-2.js +0 -137
  95. package/dist/incr-search-vCzuao-2.js.map +0 -1
  96. package/dist/index-Cu5i8zZY.d.ts +0 -160
  97. package/dist/index-Cu5i8zZY.d.ts.map +0 -1
  98. package/dist/list-selection-controller-CUo-Hhcm.js.map +0 -1
  99. package/dist/tree-B74zdI60.js.map +0 -1
  100. package/dist/vertical-list-CQSEVeOT.d.ts.map +0 -1
  101. package/dist/xy-axis-VrX8dmvS.js.map +0 -1
@@ -313,7 +313,7 @@ With wrapping:
313
313
 
314
314
  ## Incremental search
315
315
 
316
- Press Ctrl+F (or Cmd+F on Mac) to open the search overlay. Type to filter items in real-time using fuzzy matching.
316
+ A built-in type-ahead popover with **navigate** / **select** / **filter** modes — see [`docs/infra-incremental.md`](../../docs/infra-incremental.md). No default shortcut; open it with `list.registry.invoke('list.search.filter')` (or `.navigate` / `.select`). A filter change re-runs the column geometry automatically.
317
317
 
318
318
  Search matches against `data-search-label` (or `textContent` as fallback). Use `data-search-label` to match against a different string than what's displayed:
319
319
 
@@ -192,11 +192,13 @@ All bubble and are `composed`.
192
192
 
193
193
  | Event | Detail | When |
194
194
  |---|---|---|
195
- | `list-select` | `{ selected, previous, selectedKeys }` | Selection changes by any means |
196
- | `list-activate` | `{ item }` | Double-click or `Enter` on an item |
197
- | `list-item-click` | `{ item }` | Single click / `Enter` |
198
- | `list-tickle` | `{ item }` | Cursor moves onto an item |
199
- | `grid-list-viewport-change` | `{ visibleKeys }` | Scroll, resize, or content change — the rendered window moved |
195
+ | `list-select` | `{ selectedKeys, previousKeys, selected, previous }` | Selection changes by any means |
196
+ | `list-activate` | `{ key, index, item, element }` | Double-click or `Enter` on an item |
197
+ | `list-item-click` | `{ key, index, item, element }` | Single click / `Enter` |
198
+ | `list-tickle` | `{ key, index, item, element }` | Cursor moves onto an item |
199
+ | `grid-list-viewport-change` | `{ renderedKeys, visibleKeys }` | Scroll, resize, filter, content change, or `refreshLayout()` — the rendered window moved |
200
+
201
+ `key` / `index` are the stable contract in both modes. `item` is backward-compatible: the light-DOM `Element` in element mode, the data value in data mode. `element` is the DOM node when there is one (`undefined` in data mode). `selected` / `previous` (Element sets) are populated in element mode only; use `selectedKeys` / `previousKeys` everywhere.
200
202
 
201
203
  ---
202
204
 
@@ -215,17 +217,101 @@ All bubble and are `composed`.
215
217
 
216
218
  ---
217
219
 
220
+ ## Incremental search
221
+
222
+ A built-in type-ahead popover with **navigate** / **select** / **filter** modes.
223
+ No default shortcut — open it via a command on `grid.registry`:
224
+
225
+ ```typescript
226
+ grid.registry.invoke('list.search.filter'); // or .navigate / .select
227
+ ```
228
+
229
+ Search matches `data-label` (or `[data-grid-list-caption]` text, then
230
+ `data-search-label`, then `textContent`). Filter mode genuinely removes items
231
+ from the virtualised layout. Add a page-level highlight rule to see matched
232
+ characters:
233
+
234
+ ```css
235
+ ::highlight(grid-list-search) { background: var(--accent); color: var(--accent-text); }
236
+ ```
237
+
238
+ `filterPredicate` (an `(el: Element) => boolean` property) filters element-mode
239
+ items directly, independent of search; the data-mode equivalent is
240
+ `itemFilterPredicate` (`(item, index) => boolean`). Filtering removes items from
241
+ layout and navigation without materialising anything in data mode. Limit modes
242
+ with `search-modes="navigate filter"`. Full model:
243
+ [`docs/infra-incremental.md`](../../docs/infra-incremental.md).
244
+
245
+ ---
246
+
247
+ ## Data-backed mode
248
+
249
+ For very large collections (a directory of 100k files), give the grid an
250
+ **ordered array plus an adapter** instead of one light-DOM element per item.
251
+ Only the visible window plus overscan is ever rendered — item content DOM is
252
+ created and torn down as the window moves.
253
+
254
+ ```typescript
255
+ import type { GridListElement, GridListDataAdapter } from '@ixfx/components/grid-list';
256
+
257
+ const grid = document.querySelector<GridListElement<FileRecord>>('#grid')!;
258
+
259
+ grid.itemAdapter = {
260
+ key: f => f.path, // stable identity (required)
261
+ label: f => f.name, // caption text
262
+ render: f => html`<img src=${f.thumb} loading="lazy" decoding="async">`,
263
+ disabled: f => f.locked, // optional — not selectable / tickle-able
264
+ searchableText: f => `${f.name} ${f.ext}`, // optional — search haystack
265
+ };
266
+ grid.items = files; // readonly FileRecord[]
267
+ ```
268
+
269
+ `render` returns anything Lit can render — a `TemplateResult`, a DOM `Node`, or a
270
+ string. It runs only for items in the rendered window.
271
+
272
+ | Method | Effect |
273
+ |---|---|
274
+ | `setItems(items)` | Replace the collection. Selection / cursor keep any key still present. |
275
+ | `appendItems(items)` | Add to the end. Scroll, selection and cursor untouched; no reflow animation. |
276
+ | `updateItem(key, item)` | Replace one item. A visible cell re-renders in place; an offscreen one updates when scrolled in. |
277
+ | `removeItems(keys)` | Remove items. Removed keys drop out of selection / cursor. |
278
+ | `clearItems()` | Drop everything. Resets selection / cursor and emits one empty `list-select`. |
279
+
280
+ Selection, cursor, keyboard nav, search, filtering, `count`, `renderedCount`,
281
+ `renderedKeys()` and `visibleKeys()` behave identically to element mode — all
282
+ key-based. Data-mode events carry the data value as `detail.item` and
283
+ `detail.element` is `undefined`; read `detail.selectedKeys` for selection.
284
+ `selectedItems` (an Element set) is element-mode only.
285
+
286
+ Duplicate keys are warned about once; the first occurrence wins for key-based
287
+ operations.
288
+
289
+ ---
290
+
218
291
  ## Virtualisation
219
292
 
220
- Only the cells in (or near) the viewport are in the DOM.
293
+ Only the cells in (or near) the viewport are in the DOM — in both element and
294
+ data mode.
221
295
 
222
296
  ```typescript
223
- grid.count; // total items
297
+ grid.count; // total items (after filtering)
224
298
  grid.renderedCount; // cells currently in the shadow DOM
225
- grid.visibleKeys(); // data-key of items in the current window
299
+ grid.renderedKeys(); // keys of the rendered window viewport + overscan (prefetch set)
300
+ grid.visibleKeys(); // keys strictly intersecting the viewport (no overscan)
226
301
  ```
227
302
 
228
- `grid-list` handles tens of thousands of children in every orientation.
303
+ `grid-list` handles hundreds of thousands of items in every orientation. Keys of
304
+ keyless light-DOM items (auto-assigned internally) are omitted from
305
+ `renderedKeys()` / `visibleKeys()` / `selectedKeys`.
306
+
307
+ ### Hidden containers
308
+
309
+ `refreshLayout()` re-reads the viewport after an ancestor's visibility or size
310
+ changed — e.g. a dock tab being re-activated. It is safe to call while hidden
311
+ (a zero-sized measurement never replaces the last usable geometry) and forces a
312
+ repaint of the virtual window even when the size is unchanged. Scroll position,
313
+ zoom anchoring and selection survive a hide/show cycle. `remeasure()` is an
314
+ alias.
229
315
 
230
316
  ---
231
317
 
@@ -251,8 +337,9 @@ it's suppressed under `prefers-reduced-motion: reduce`. Duration:
251
337
  | Part | Element |
252
338
  |---|---|
253
339
  | `viewport` | The scroll container |
254
- | `item` | A cell |
255
- | `thumb` | The square holding the slotted content |
340
+ | `item` | A cell. Also carries the state tokens `selected` / `tickled`, e.g. `::part(item selected)` |
341
+ | `content` | The rendered content wrapper (data mode) |
342
+ | `thumb` | The square holding the slotted content (element mode) |
256
343
  | `caption` | The caption element (absent for `caption-style="hidden"`) |
257
344
 
258
345
  Host CSS custom properties: `--grid-list-item-bg`, `--grid-list-radius`,
@@ -260,6 +347,28 @@ Host CSS custom properties: `--grid-list-item-bg`, `--grid-list-radius`,
260
347
  default `200ms`), `--scroll-fade-width` (edge fade reach, default `30px`), plus
261
348
  the shared `--item-bg-*` / `--accent` theme tokens.
262
349
 
350
+ ### Scrollbar
351
+
352
+ The scroll container maps three variables straight onto the standard scrollbar
353
+ properties (both orientations):
354
+
355
+ | Variable | Default | Maps to |
356
+ |---|---|---|
357
+ | `--grid-list-scrollbar-width` | `auto` | `scrollbar-width` — `auto` \| `thin` \| `none` (hide it) |
358
+ | `--grid-list-scrollbar-gutter` | `auto` | `scrollbar-gutter` — `auto` \| `stable` \| `stable both-edges` |
359
+ | `--grid-list-scrollbar-color` | `auto` | `scrollbar-color` — `auto` or a `thumb track` colour pair |
360
+
361
+ ```css
362
+ ixfx-grid-list {
363
+ --grid-list-scrollbar-width: none; /* suppress entirely */
364
+ /* or: --grid-list-scrollbar-width: thin;
365
+ --grid-list-scrollbar-color: var(--accent) transparent; */
366
+ }
367
+ ```
368
+
369
+ For finer control, style `::part(viewport)` directly (including
370
+ `::part(viewport)::-webkit-scrollbar` in Blink/WebKit).
371
+
263
372
  ---
264
373
 
265
374
  ## Choosing a list component
@@ -1,8 +1,18 @@
1
1
  # Incremental Search
2
2
 
3
3
  Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
4
- You can use the low-level `createIncrSearch` for data, `createElementIncrSearch` for
5
- DOM elements, or `IncrSearchTreeController` for tree and Miller list components.
4
+
5
+ - **`ListSearchController`** the shared brain behind the built-in search
6
+ popover in `ixfx-grid-list` / `ixfx-vertical-list` / `ixfx-detail-list` /
7
+ `ixfx-data-grid` / `ixfx-tree-list` / `ixfx-miller-list`. Three modes
8
+ (navigate / select / filter), driven by `list.search.*` commands. See
9
+ [`docs/infra-incremental.md`](../../docs/infra-incremental.md) — start there
10
+ for anything component-facing.
11
+ - **`createIncrSearch`** — the low-level fuzzy engine (fzf) both the controller
12
+ and the helpers below are built on.
13
+ - **`createElementIncrSearch`** / **`IncrSearchTreeController`** — *deprecated*
14
+ filter-only controllers driven by an external `<input>`. Kept for standalone
15
+ use; components now use `ListSearchController`.
6
16
 
7
17
  ## Plain UL list
8
18
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
- "version": "0.7.4",
4
- "generated": "2026-09-08T16:59:30.569Z",
3
+ "version": "0.8.0",
4
+ "generated": "2026-09-08T21:01:02.526Z",
5
5
  "components": [
6
6
  {
7
7
  "name": "ac-text",
@@ -100,7 +100,7 @@
100
100
  },
101
101
  {
102
102
  "name": "led",
103
- "summary": "An LED indicator component with color mixing and optional blinking animation.",
103
+ "summary": "An LED indicator component with color mixing and brightness, blink, and pulse modes.",
104
104
  "path": "docs-user/led.md"
105
105
  },
106
106
  {
package/docs-user/led.md CHANGED
@@ -1,29 +1,50 @@
1
1
  # `ixfx-led`
2
2
 
3
- An LED indicator component with color mixing and optional blinking animation.
3
+ An LED indicator component with color mixing and brightness, blink, and pulse modes.
4
4
 
5
5
  ```html
6
6
  <ixfx-led></ixfx-led>
7
7
  <ixfx-led lit color="oklch(60% 0.2 120)"></ixfx-led>
8
- <ixfx-led blinking rate="300"></ixfx-led>
8
+ <ixfx-led glow lit color="oklch(60% 0.2 120)"></ixfx-led>
9
+ <ixfx-led mode="blink" rate="300"></ixfx-led>
10
+ <ixfx-led mode="pulse" value="1" attack-time="40" release-time="400"></ixfx-led>
9
11
  ```
10
12
 
11
13
  ### Properties / attributes
12
14
 
13
15
  | Attribute / Property | Type | Default | Description |
14
16
  |----------------------|----------|--------------------------|----------------------------------------|
15
- | `lit` | `boolean`| `false` | Whether the LED is illuminated |
16
- | `value` | `number` | `0` | Value 0-1 used for brightness calculation |
17
+ | `mode` | `'brightness' \| 'blink' \| 'pulse'` | `'brightness'` | Operating mode (see below) |
18
+ | `lit` | `boolean`| `false` | Whether the LED is illuminated (shortcut for `value` 0/1) |
19
+ | `value` | `number` | `0` | Value 0-1. In `brightness` mode sets brightness directly; in `pulse` mode it is the envelope peak |
17
20
  | `color` | `string` | `'oklch(50% 0.15 25)'` | LED color (oklch recommended) |
18
- | `blinking` | `boolean`| `false` | Enable blinking animation |
19
- | `rate` | `number` | `500` | Blinking interval in milliseconds |
21
+ | `glow` | `boolean`| `false` | Render a domed LED with a specular highlight and a coloured bloom that grows with `value` |
22
+ | `blinking` | `boolean`| `false` | Legacy alias for `mode="blink"` |
23
+ | `rate` | `number` | `500` | `blink` mode interval in milliseconds |
24
+ | `attackTime` / `attack-time` | `number` | `40` | `pulse` mode rise time in milliseconds (0 → instant) |
25
+ | `releaseTime` / `release-time` | `number` | `400` | `pulse` mode decay time in milliseconds (0 → instant) |
26
+ | `pulseRetrigger` / `pulse-retrigger` | `boolean` | `false` | When set, a re-`pulse()` restarts the envelope from 0; when unset, the attack ramps from the envelope's current level |
27
+
28
+ ### Modes
29
+
30
+ - **`brightness`** (default) — `value` sets the brightness.
31
+ - **`blink`** — the LED toggles on/off every `rate` ms. Setting the legacy `blinking` attribute selects this mode.
32
+ - **`pulse`** — the LED sits unlit until `pulse()` is called. Each call runs a linear attack/release envelope up to `value` (over `attack-time`) and back down to 0 (over `release-time`). Calling `pulse()` again restarts the envelope: from 0 if `pulse-retrigger` is set, otherwise the attack ramps up from wherever the envelope currently sits (a seamless retrigger mid-decay).
33
+
34
+ ### Methods
35
+
36
+ | Method | Description |
37
+ |-----------|-------------|
38
+ | `pulse()` | Trigger the `pulse`-mode envelope. No-op unless `mode` is `pulse`. |
20
39
 
21
40
  ### CSS custom properties
22
41
 
23
42
  | Property | Default | Description |
24
43
  |----------|---------|-------------------|
25
44
  | `--size` | `1ex` | LED diameter |
26
- | `--colour` | `oklch(50% 0.15 25)` | LED colour (overrides `color` attribute) |
45
+ | `--colour` | `oklch(50% 0.15 25)` | LED colour (overrides the `color` attribute) |
46
+
47
+ Colour resolves as `--colour` → `color` attribute → default. The `glow` bloom uses the same resolved colour.
27
48
 
28
49
  ### Usage notes
29
50
 
@@ -34,7 +55,16 @@ For blinking, setting `rate` below 300ms adjusts the transition duration automat
34
55
  ### TypeScript types
35
56
 
36
57
  ```ts
58
+ import type { LedMode } from './led.js';
59
+
37
60
  // LED color is a CSS color string
38
61
  led.color = 'oklch(70% 0.15 200)';
39
62
  led.value = 0.75; // 75% lit
63
+
64
+ // Pulse mode
65
+ led.mode = 'pulse';
66
+ led.value = 1;
67
+ led.attackTime = 20;
68
+ led.releaseTime = 600;
69
+ led.pulse(); // fire the envelope
40
70
  ```
@@ -30,7 +30,7 @@ import '@ixfx/components/data-grid';
30
30
  - [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
31
31
  - [labelled-range-input](docs-user/labelled-range-input.md): A composite range input that pairs an editable label (`ixfx-editable-number`) with a slider (`ixfx-range-input`). The two controls are always in sync — dragging the slider updates the number, and edit
32
32
  - [labelled-slider-input](docs-user/labelled-slider-input.md): A composite slider input that pairs an editable label (`ixfx-editable-number`) with a slider (`ixfx-slider-input`). The two controls are always in sync — dragging the slider updates the number, and ed
33
- - [led](docs-user/led.md): An LED indicator component with color mixing and optional blinking animation.
33
+ - [led](docs-user/led.md): An LED indicator component with color mixing and brightness, blink, and pulse modes.
34
34
  - [menu](docs-user/menu.md): A custom menu system built on native HTML popovers with keyboard navigation, tickled focus management, and command routing.
35
35
  - [miller](docs-user/miller.md): 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.
36
36
  - [narrowed-text](docs-user/narrowed-text.md): A filtered text selection component with inline search. Shows slotted items filtered by a query string as the user types.
@@ -24,8 +24,9 @@ The header chrome is an `ixfx-titlebar` (see [`ixfx-titlebar`](../titlebar/READM
24
24
  | `title` | string | `''` | Panel title text |
25
25
  | `icon-name` | string | `''` | Icon name from the registry |
26
26
  | `closable` | boolean | `false` | Shows a close button that removes the panel |
27
+ | `keep-toolbar` | boolean | `false` | Keep the toolbar visible even when collapsed |
27
28
  | `toggle-interaction` | `'implicit'` \| `'explicit'` \| `'disabled'` | `'implicit'` | How users can toggle the panel (see below) |
28
- | `variant` | `'titlebar'` \| `'midline'` | `'titlebar'` | Visual style of the panel |
29
+ | `variant` | `'titlebar'` \| `'midline'` \| `'tree'` | `'titlebar'` | Visual style of the panel |
29
30
 
30
31
  ### Toggle interaction modes
31
32
 
@@ -49,10 +50,10 @@ Set `collapsible` to `false` (via property binding or JS — `collapsible` defau
49
50
 
50
51
  ### Slots
51
52
 
52
- | Slot | Description |
53
- |-------------|--------------------------------------------------|
54
- | default | Panel content (shown when expanded) |
55
- | `toolbar` | Buttons or other elements in the title bar toolbar |
53
+ | Slot | Description |
54
+ |-------------|-----------------------------------------------------------------|
55
+ | default | Panel content (shown when expanded) |
56
+ | `toolbar` | Buttons or other elements in the title bar toolbar (hidden when collapsed by default; set `keep-toolbar` to always show) |
56
57
 
57
58
  ### Events
58
59
 
@@ -113,6 +114,20 @@ A lightweight, minimal style with a horizontal line running through the title te
113
114
  - The line is visible between the title text and toolbar buttons
114
115
  - Useful for section headers or metadata display
115
116
 
117
+ ### `tree` variant
118
+
119
+ A VS Code explorer / Outline style with a minimal inline title bar:
120
+
121
+ ```html
122
+ <ixfx-panel title="Explorer" variant="tree" icon-name="folder">
123
+ <p>Content here</p>
124
+ </ixfx-panel>
125
+ ```
126
+
127
+ - Caret is shown first, followed by the icon and title inline (no debossed line)
128
+ - Transparent background and border; minimal padding
129
+ - Toolbar is hidden when the panel is collapsed — set `keep-toolbar` to always show it
130
+
116
131
  ---
117
132
 
118
133
  ## Vertical orientation
@@ -189,18 +204,19 @@ Use the `collapsed` attribute to start the panel in collapsed state:
189
204
 
190
205
  ## `ixfx-panel-group`
191
206
 
192
- A container component that manages multiple panels with exclusive open behavior (accordion style).
207
+ A container component that manages multiple panels with flexible open behavior.
193
208
 
194
209
  ### Properties / attributes
195
210
 
196
211
  | Attribute / Property | Type | Default | Description |
197
212
  |------------------------|---------------------------------------------------|----------------|---------------------------------------------------------------|
213
+ | `accordion` | boolean | `false` | Restrict to one open panel at a time |
214
+ | `allow-reorder` | boolean | `false` | Enable drag-and-drop reordering of panels via their titlebars |
215
+ | `collapsible` | boolean | `true` | Collapse behaviour propagated to child panels |
216
+ | `gap` | string | (unset) | Explicit gap between panels; sets `--group-gap` when provided |
198
217
  | `orientation` | `'horizontal'` \| `'vertical'` | `'horizontal'` | Layout direction of the group |
199
- | `variant` | `'titlebar'` \| `'midline'` | `'titlebar'` | Visual style applied to all child panels |
200
218
  | `toggle-interaction` | `'implicit'` \| `'explicit'` \| `'disabled'` | `'implicit'` | Toggle behavior applied to all child panels |
201
- | `allow-multiple` | boolean | `false` | Allow multiple panels to be open simultaneously |
202
- | `collapsible` | boolean | `true` | Collapse behaviour propagated to child panels |
203
- | `gap` | string | `'var(--space-m)'` | CSS gap between panels |
219
+ | `variant` | `'titlebar'` \| `'midline'` \| `'tree'` | `'titlebar'` | Visual style applied to all child panels |
204
220
 
205
221
  ### Group orientation vs child panel orientation
206
222
 
@@ -231,14 +247,53 @@ Explicit attributes on child panels take precedence over group-propagated values
231
247
  </ixfx-panel-group>
232
248
  ```
233
249
 
234
- By default, only one panel can be open at a time. Set `allow-multiple` to allow multiple panels to be open:
250
+ By default, multiple panels can be open at the same time. Add `accordion` to restrict it to one open panel at a time:
235
251
 
236
252
  ```html
237
- <ixfx-panel-group orientation="horizontal" allow-multiple>
253
+ <ixfx-panel-group orientation="horizontal" accordion>
238
254
  <!-- panels -->
239
255
  </ixfx-panel-group>
240
256
  ```
241
257
 
258
+ ### Reordering
259
+
260
+ Set `allow-reorder` to let users rearrange panels by dragging their titlebars (pointer-only in v1; keyboard reorder is not included):
261
+
262
+ ```html
263
+ <ixfx-panel-group orientation="vertical" allow-reorder>
264
+ <ixfx-panel title="Panel 1">...</ixfx-panel>
265
+ <ixfx-panel title="Panel 2">...</ixfx-panel>
266
+ </ixfx-panel-group>
267
+ ```
268
+
269
+ Dragging starts anywhere on the titlebar except interactive chrome (buttons, toolbar slot, caret/close) — those controls always win over dragging. A drop-caret marks the insertion point and a floating ghost styled like the titlebar follows the pointer. Text selection is suppressed for the duration of the drag. Press <kbd>Esc</kbd> to abort a drag. The trailing click after a real reorder is swallowed so the panel doesn't also collapse/expand.
270
+
271
+ #### Events
272
+
273
+ | Event | Detail | Description |
274
+ |-----------|-------------------------------------------------|--------------------------------------|
275
+ | `reorder` | `{ fromIndex, toIndex, panel }` | Fired after panels are reordered |
276
+
277
+ `fromIndex` / `toIndex` are positions in the group's real light-DOM `children` (which may include non-panel elements), not positions in the filtered panel list.
278
+
279
+ ```js
280
+ group.addEventListener('reorder', e => {
281
+ console.log('Moved from', e.detail.fromIndex, 'to', e.detail.toIndex);
282
+ });
283
+ ```
284
+
285
+ ### Group gap
286
+
287
+ The space between panels is controlled by the `--group-gap` CSS custom property, falling back to `var(--space-l)` when unset:
288
+
289
+ ```css
290
+ ixfx-panel-group {
291
+ --group-gap: var(--space-xl);
292
+ }
293
+ ```
294
+
295
+ Alternatively set the `gap` property/attribute for the same effect (it mirrors its value into `--group-gap`).
296
+
242
297
  ### Group-propagated properties
243
298
 
244
299
  When you set `variant`, `toggle-interaction` or `collapsible` on the group, those values are automatically applied to all child panels that don't have an explicit value set. This simplifies declaration when all panels in a group share the same styling:
@@ -256,7 +311,7 @@ When you set `variant`, `toggle-interaction` or `collapsible` on the group, thos
256
311
  ## TypeScript types
257
312
 
258
313
  ```ts
259
- import type { PanelOrientation, PanelVariant, PanelToggleInteraction, PanelToggleDetail, PanelGroupOrientation } from 'ixfxfun-components';
314
+ import type { PanelOrientation, PanelVariant, PanelToggleInteraction, PanelToggleDetail, PanelGroupOrientation, PanelGroupReorderDetail } from 'ixfxfun-components';
260
315
  // Canonical titlebar types (Panel* are aliases):
261
316
  import type { TitlebarOrientation, TitlebarVariant, TitlebarToggleInteraction, TitlebarToggleDetail } from 'ixfxfun-components';
262
317
  ```
@@ -23,7 +23,7 @@ Standalone titlebar chrome extracted from `ixfx-panel`: title text, icon, toolba
23
23
  | `title` | string | `''` | Title text |
24
24
  | `icon-name` | string | `''` | Icon name from the registry |
25
25
  | `orientation` | `'horizontal'` \| `'vertical'` | `'horizontal'` | Layout direction of the bar |
26
- | `variant` | `'titlebar'` \| `'midline'` | `'titlebar'` | Visual style (same variants as `ixfx-panel`) |
26
+ | `variant` | `'titlebar'` \| `'midline'` \| `'tree'` | `'titlebar'` | Visual style (same variants as `ixfx-panel`) |
27
27
  | `closable` | boolean | `false` | Shows a close button that fires `close` |
28
28
  | `collapsible` | boolean | `false` | Shows the caret and enables toggle behaviour (opt-in) |
29
29
  | `collapsed` | boolean | `false` | Collapsed state (controls caret rotation / label) |
@@ -79,11 +79,10 @@ Built-in icon names: `check`, `chevron-down`, `chevron-right`, `close`.
79
79
 
80
80
  ## Visual variants
81
81
 
82
- Same `titlebar` (default) and `midline` styles as `ixfx-panel`, including vertical orientation support:
82
+ Same `titlebar` (default) and `midline` styles as `ixfx-panel`, including vertical orientation support. The `tree` variant renders caret, icon and title in a compact inline layout with transparent chrome, matching VS Code explorer / Outline style:
83
83
 
84
84
  ```html
85
- <ixfx-titlebar title="Settings" variant="midline" icon-name="settings" collapsible></ixfx-titlebar>
86
- <ixfx-titlebar title="Device" orientation="vertical" icon-name="server" collapsible></ixfx-titlebar>
85
+ <ixfx-titlebar title="Explorer" variant="tree" icon-name="folder" collapsible></ixfx-titlebar>
87
86
  ```
88
87
 
89
88
  ---
package/docs-user/tree.md CHANGED
@@ -367,7 +367,28 @@ Keyboard navigation follows the crumb path. Arrow keys move between breadcrumb s
367
367
 
368
368
  ## Incremental search
369
369
 
370
- Wire up a text input to filter and highlight matching nodes using `IncrSearchTreeController`:
370
+ `ixfx-tree-list` and `ixfx-miller-list` have a **built-in type-ahead popover**
371
+ with three modes:
372
+
373
+ - **navigate** — expands the match's ancestors and moves the cursor to it; ↑/↓
374
+ cycle between matches; Enter activates and closes.
375
+ - **select** — the selection becomes the set of matching nodes.
376
+ - **filter** — non-matching nodes (and branches with no matching descendant) are
377
+ hidden; branches with a match auto-expand so the match is visible.
378
+
379
+ No default shortcut — open it via a command on `el.registry`:
380
+
381
+ ```typescript
382
+ el.registry.invoke('list.search.navigate'); // or .select / .filter
383
+ ```
384
+
385
+ Limit the offered modes with the `search-modes` attribute
386
+ (`search-modes="navigate filter"`). Full model: [`docs/infra-incremental.md`](../../docs/infra-incremental.md).
387
+
388
+ ### Deprecated: `IncrSearchTreeController`
389
+
390
+ The external-`<input>` filter-only controller still works but is superseded by
391
+ the built-in popover.
371
392
 
372
393
  ```typescript
373
394
  import { IncrSearchTreeController } from '@ixfx/components';
@@ -504,7 +504,13 @@ The element must be focused (click it or tab to it) before keyboard navigation w
504
504
 
505
505
  ## Incremental search
506
506
 
507
- Press **Ctrl+F** (or **Cmd+F**) to open the search overlay. Typing filters items in real time using fuzzy matching. Matched text in plain `<li>` items is highlighted via the [CSS Highlight API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API).
507
+ A built-in type-ahead popover with three modes — **navigate** (cursor jumps to the best match, ↑/↓ cycle), **select** (matches become the selection), **filter** (non-matches hidden). See [`docs/infra-incremental.md`](../../docs/infra-incremental.md) for the full model, conversions between modes, and the `search-modes` attribute.
508
+
509
+ There is **no default shortcut**. Open it by invoking one of the commands on `list.registry`:
510
+
511
+ ```typescript
512
+ list.registry.invoke('list.search.filter'); // or .navigate / .select
513
+ ```
508
514
 
509
515
  ### Adding the page-level highlight rule
510
516
 
package/llms.txt CHANGED
@@ -30,7 +30,7 @@ import '@ixfx/components/data-grid';
30
30
  - [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
31
31
  - [labelled-range-input](docs-user/labelled-range-input.md): A composite range input that pairs an editable label (`ixfx-editable-number`) with a slider (`ixfx-range-input`). The two controls are always in sync — dragging the slider updates the number, and edit
32
32
  - [labelled-slider-input](docs-user/labelled-slider-input.md): A composite slider input that pairs an editable label (`ixfx-editable-number`) with a slider (`ixfx-slider-input`). The two controls are always in sync — dragging the slider updates the number, and ed
33
- - [led](docs-user/led.md): An LED indicator component with color mixing and optional blinking animation.
33
+ - [led](docs-user/led.md): An LED indicator component with color mixing and brightness, blink, and pulse modes.
34
34
  - [menu](docs-user/menu.md): A custom menu system built on native HTML popovers with keyboard navigation, tickled focus management, and command routing.
35
35
  - [miller](docs-user/miller.md): 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.
36
36
  - [narrowed-text](docs-user/narrowed-text.md): A filtered text selection component with inline search. Shows slotted items filtered by a query string as the user types.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
3
  "type": "module",
4
- "version": "0.7.4",
4
+ "version": "0.8.0",
5
5
  "packageManager": "pnpm@10.0.0",
6
6
  "description": "Web components for ixfx — Lit-based UI toolkit",
7
7
  "author": "Clint Heyer",
@@ -1 +0,0 @@
1
- {"version":3,"file":"data-grid-BddUjAti.d.ts","names":[],"sources":["../src/data-grid/types.ts","../src/data-grid/data-grid.ts"],"mappings":";;;KAAY;KACA;KACA;KACA;WAA+B;WAAsB,WAAW;;KAChE;KAEA,sBAAsB;WACvB,KAAK;WACL,QAAQ,eAAe;WACvB;WACA;WACA;EACT,SAAS;EACT;;KAEU,sBAAsB,gBAAgB,KAAK,sBAAsB;KAEjE,eAAe;EACzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU,GAAG,GAAG,GAAG;EACnB;EACA;EACA;EACA,YAAY,KAAK,GAAG,KAAK,eAAe,IAAI;EAC5C;EACA;EACA;EACA,SAAS,qBAAqB,sBAAsB;EACpD,gBAAgB;IAAyB;IAAe;;EACxD,aAAa,gBAAgB,KAAK,GAAG,KAAK,eAAe;;KAG/C,mBAAmB,gBAAgB,oBAAoB;KAEvD;KAEA;KACA;EAA4B;;KAE5B,kBAAkB,MAAM,KAAK,GAAG;KAChC,gBAAgB,gBAAgB,KAAK;KACrC,sBAAsB,kBAAkB;;KAGxC;;;KCrCA;WAAgC,MAAM;WAA4B,UAAU;;KAC5E,oBAAoB,YAAY;KAChC;WAAkC,cAAc;WAA8B,UAAU;;KACxF,sBAAsB,YAAY;KAClC;WAAyC;WAAuB;WAAqB,kBAAkB;;KACvG,6BAA6B,YAAY;KACzC;WAAwC;WAAsB;;KAC9D,4BAA4B,YAAY;KACxC;WAA4C;WAAsB;;KAClE,gCAAgC,YAAY;KAC5C;WAAqC;WAAyB;WAAyB;WAA2B;;KAClH,yBAAyB,YAAY;KACrC;WAAoC;WAAyB;WAAyB;WAA4B;;KAClH,wBAAwB,YAAY;KAEpC;WAAuC,SAAS;;KAChD,2BAA2B,YAAY;UAClC;EACf,iBAAiB;EACjB,mBAAmB;EACnB,2BAA2B;EAC3B,0BAA0B;EAC1B,8BAA8B;EAC9B,uBAAuB;EACvB,sBAAsB;EACtB,yBAAyB;;cAId,wBAAwB;;SACnB,sBAAM;EAoGK,SAAS;EACT;EAC8B,eAAe;EACb,iBAAiB;EAC5C,WAAW;EACU;EACrB,WAAW;EACX,oBAAoB;EACpB,sBAAsB,cAAc,KAAK;EAC9B,aAAa;EAExB,SAAS;EACiB;EAC1B,YAAY;;EAEZ;EACgD;;;;;;EAM1B,YAAY;EAClC,kBAAkB;UAC1C;UAES;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UAEW;UACK;UAEzB;UAKA;UACA;UACA;UAMA;UACA;UACA;UACA;UAGA;EAEC;EAQA;UAWD;YAgBW;YAaA,QAAQ,SAAS;YAmBjB,WAAW,SAAS;UA4D/B;UAQA;UAiCA;UAOA;UAoBA;UAYA;UAcA;UA0BA;UAKA;UAMA;UAsFA;UAYA;UAEA;UAcA;;MAQJ,gBAAgB;;MAKhB,aAAa,MAAM;;;;;;;EAUvB,WAAW,MAAM;;MA0Bb;;EAKJ,UAAU;;;;;;;;EAcV,WAAW;UA8CH;UAiBA;UAsBA;UAmCA;UAsKA;UAUA;UAKA;UAQA;;EAeR,UAAU,gBAAgB;;EAK1B,aAAa;EAWb;EAIA,mBAAmB,mBAAmB,aAAa,8BAA8B,6BAA6B,UAAU;UAkBhH;UAWA;UAYA;UAyBA;UACA;UA2BA;UAYA;UAkCA;EAoBR;IAAqB;IAAkB;IAAgB;IAAkB;;UASjE;UAaA;YAQW,wBAAM;;QAyMnB;YACI;IAAwB,kBAAkB;;;UAErC;EACf,oBAAoB,gBAAgB,yBAAyB,MAAM,GAAG,WAAW,MAAM,iBAAiB,OAAO,wBAAwB,aAAa,oBAAoB,sCAAsC,cAAc,UAAU,oCAAoC,oBAAoB;EAC9R,uBAAuB,gBAAgB,yBAAyB,MAAM,GAAG,WAAW,MAAM,iBAAiB,OAAO,wBAAwB,aAAa,oBAAoB,mCAAmC,cAAc,UAAU,oCAAoC,oBAAoB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"detail-list-Br0WAnry.d.ts","names":[],"sources":["../src/detail-list/types.ts","../src/detail-list/detail-list.ts"],"mappings":";;;;;;;;KACY;;KAGA;WACD,SAAS;WACT;WACA;;;KAIC,wBAAwB,GAAG,gBAAgB,GAAG;;KAG9C,mBAAmB,eAAe,2BAA2B;;;;;;;;;;;;;;;;;;;;cCqD5D,0BAA0B;;EAErC,eAAe;EAGf,iBAAiB;EAGjB,MAAM;EAGN;EAGA,gBAAgB;EAGhB;EAGA,UAAU;EAGV;EAGA,WAAW;EAGX;EAGA;EAGA,mBAAmB,IAAI;;;;;;EAQvB;;EAIA,oBAAoB,gCAAgC;UAEnC;UACA;UAGT;UAGA;mBAgBS;UAeT;EAcC;EA0DA;EAgBA;EAmBA,QAAQ,SAAS;;MAkCtB,gBAAgB;;MAKhB,iBAAiB,YAAY;;;;;;EASjC;EAIA,QAAQ,MAAM,kBAAkB,UAAU;;;;;EAuB1C,WAAW,MAAM,UAAU;;EAW3B,YAAY,OAAO,SAAS,WAAW;EAIvC;;;;;EAYA,SAAS,gBAAgB;;;;;EAgBzB,YAAY,MAAM;;EAKlB,SAAS,YAAY,WAAW,QAAQ;EAExC,OAAO,MAAM;EAIb,WAAW,OAAO,SAAS;EAI3B,SAAS,MAAM;EAIf;EAIA;;MAQI,gBAAgB;;MAKhB;;EAMJ,WAAW,MAAM;;EAKjB,gBAAgB,MAAM;;EAKtB,mBAAmB,MAAM;;EAKzB,SAAS;EAgBP,eAAe,0CAA0C,UAAU;UAoa7D;;;;;;;;;EAkBF,kBAAkB;UAwBhB;UAMA;UAgCA;UAiBA;;;;;;;UA2DA;;UAoBA;UAcA;UAYA;UAQA;UAKA;UASA;UAMA;UAIA;UAuFA;UASA;UAMA;UAKA;UAQA;UAMA;SA0CQ,sBAAM;EAuKb,wBAAM;;QAiCT;YACI;IACR,oBAAoB;;YAGZ,4BAA4B"}