@ixfx/components 0.16.2 → 0.16.4
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 +61 -7
- package/bundle/index.js +151 -143
- 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/ac-token.js +1 -1
- package/dist/{button-D1sPDUCM.d.ts → button-N92DKoWb.d.ts} +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/colour-picker.d.ts +1 -1
- package/dist/{crumb-navigation-nnr881n-.d.ts → crumb-navigation-BbqfmkYs.d.ts} +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/{data-grid-CaPLWt5E.js → data-grid-8TsiF2ES.js} +7 -6
- package/dist/{data-grid-BAEpfTkb.d.ts → data-grid-CslzF16R.d.ts} +4 -4
- package/dist/data-grid.d.ts +1 -1
- package/dist/data-grid.js +1 -1
- package/dist/{detail-list-DK49TsJd.d.ts → detail-list-n1MbNbhF.d.ts} +4 -4
- package/dist/{detail-list-C3XKTRmE.js → detail-list-w3GXKO2k.js} +4 -3
- package/dist/detail-list.d.ts +2 -2
- package/dist/detail-list.js +1 -1
- package/dist/{flat-list-Bv4-qfjp.d.ts → flat-list-D_4y3wgn.d.ts} +4 -4
- package/dist/flat-list.d.ts +1 -1
- package/dist/flat-list.js +2 -2
- package/dist/{grid-list-DaeSHv9P.d.ts → grid-list-CqYS28NY.d.ts} +4 -2
- package/dist/{grid-list-51XsR2X2.js → grid-list-Cz2tKcNZ.js} +4 -3
- package/dist/grid-list.d.ts +2 -2
- package/dist/grid-list.js +1 -1
- package/dist/{grouped-item-lister-DZIjo9nO.d.ts → grouped-item-lister-CS4yInuw.d.ts} +2 -2
- 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-2YaxUUA9.js → incr-search-Dc6gYpJG.js} +24 -4
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +1 -1
- package/dist/{index-BjRoPrwj.d.ts → index-Bm9a9S3_.d.ts} +3 -2
- 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-CJ5fshD3.d.ts → index-UvnoZUAo.d.ts} +1 -1
- package/dist/{index-vdbbGE7N.d.ts → index-VAs9akt4.d.ts} +18 -2
- package/dist/{index-DXDz60XH.d.ts → index-YCDugjdu.d.ts} +4 -4
- package/dist/{index-DM9OLgTH.d.ts → index-ZaA90u09.d.ts} +2 -2
- package/dist/index.d.ts +37 -26
- package/dist/index.js +88 -13
- package/dist/item-appearance-BjcMyauw.js +29 -0
- 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-selection-controller-CJDdjH7E.js → list-selection-controller-D4iIrvJH.js} +12 -8
- package/dist/{list-selection-types-hj9Njei3.d.ts → list-selection-types-Dnamh2Lu.d.ts} +29 -3
- package/dist/{menu-item-B0zqw2xp.d.ts → menu-item-bF6narIz.d.ts} +1 -1
- package/dist/menu.d.ts +2 -2
- package/dist/{miller-BfrTcujx.js → miller-BrmBywNZ.js} +20 -9
- package/dist/miller.d.ts +1 -1
- package/dist/miller.js +1 -1
- package/dist/narrowed-text.js +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/{item-appearance-Bl6rMIID.js → partial-keys-Ck6CiprB.js} +1 -29
- package/dist/{split-layout-ChZ0Glrs.d.ts → split-layout-CophkNeU.d.ts} +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/sunburst-tree.d.ts +1 -1
- package/dist/sunburst-tree.js +1 -1
- package/dist/{sunbursttree-list-BAS0KrDG.d.ts → sunbursttree-list-Bk20giTF.d.ts} +1 -1
- package/dist/{sunbursttree-list-906G33ME.js → sunbursttree-list-DQF6DcsS.js} +1 -1
- package/dist/{tab-list-wC0xNz1b.d.ts → tab-list-Df371rqw.d.ts} +1 -1
- package/dist/tabs.d.ts +1 -1
- package/dist/timeline.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-C_H1COTf.js → tree-BaXYyt5U.js} +185 -69
- package/dist/tree.d.ts +1 -1
- package/dist/tree.js +1 -1
- package/dist/{vertical-list-od4050Bo.d.ts → vertical-list-DtHX26JO.d.ts} +2 -2
- package/dist/{vertical-list-qMRIxZIT.js → vertical-list-o5hfqH87.js} +4 -3
- package/dist/vertical-list.d.ts +2 -2
- package/dist/vertical-list.js +1 -1
- package/docs-user/README.md +6 -0
- package/docs-user/bottom-drawer.md +2 -2
- package/docs-user/crumbs.md +1 -1
- package/docs-user/data-grid.md +3 -3
- package/docs-user/detail-list.md +3 -3
- package/docs-user/dock.md +3 -3
- package/docs-user/flat-list.md +7 -7
- package/docs-user/grid-list.md +4 -4
- package/docs-user/hyperbolic-tree.md +6 -6
- package/docs-user/icicle-tree.md +2 -2
- package/docs-user/incr-search.md +1 -1
- package/docs-user/index.json +26 -2
- package/docs-user/infra-handle.md +122 -0
- package/docs-user/infra-incremental.md +154 -0
- package/docs-user/infra-theming.md +510 -0
- package/docs-user/infra-view-state.md +210 -0
- package/docs-user/list-state.md +656 -0
- package/docs-user/llms.txt +6 -0
- package/docs-user/miller.md +5 -1
- package/docs-user/multicol-list.md +6 -0
- package/docs-user/panel.md +3 -3
- package/docs-user/select-horiz.md +1 -1
- package/docs-user/split-layout.md +2 -2
- package/docs-user/sunburst-tree.md +3 -3
- package/docs-user/titlebar.md +2 -2
- package/docs-user/tree.md +8 -2
- package/docs-user/treemap.md +2 -2
- package/docs-user/twosplit-layout.md +3 -3
- package/docs-user/user-selection-intents.md +86 -0
- package/docs-user/user-theming.md +1 -1
- package/docs-user/vertical-list.md +3 -3
- package/llms.txt +6 -0
- package/package.json +1 -1
- /package/dist/{index-DV3iaUBW.d.ts → index-DV3iaUBW2.d.ts} +0 -0
package/docs-user/grid-list.md
CHANGED
|
@@ -194,7 +194,7 @@ grid.addEventListener('list-select', ({ detail }) => {
|
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
Caller-owned selection: a cancelable `list-select-intent` fires before every gesture, and `partialKeys` marks partially selected cells (`data-partial`, `part="item partial"`).
|
|
197
|
-
See [Selection intents, partial rows, unloaded keys](
|
|
197
|
+
See [Selection intents, partial rows, unloaded keys](./user-selection-intents.md): `list-select-intent`, `partialKeys`, and keys that arrive later.
|
|
198
198
|
|
|
199
199
|
Restore across a reload:
|
|
200
200
|
|
|
@@ -329,7 +329,7 @@ items directly, independent of search; the data-mode equivalent is
|
|
|
329
329
|
`itemFilterPredicate` (`(item, index) => boolean`). Filtering removes items from
|
|
330
330
|
layout and navigation without materialising anything in data mode. Limit modes
|
|
331
331
|
with `search-modes="navigate filter"`. Full model:
|
|
332
|
-
[`docs/infra-incremental.md`](
|
|
332
|
+
[`docs/infra-incremental.md`](./infra-incremental.md).
|
|
333
333
|
|
|
334
334
|
---
|
|
335
335
|
|
|
@@ -428,7 +428,7 @@ operations.
|
|
|
428
428
|
|
|
429
429
|
## Grouping
|
|
430
430
|
|
|
431
|
-
Group items under collapsible headers, following `ixfx-data-grid` (see its [Grouping](
|
|
431
|
+
Group items under collapsible headers, following `ixfx-data-grid` (see its [Grouping](./data-grid.md#grouping) section).
|
|
432
432
|
|
|
433
433
|
```ts
|
|
434
434
|
grid.groupBy = 'kind'; // data mode: item property · element mode: data-kind attribute
|
|
@@ -463,7 +463,7 @@ Differences from data-grid: group state is reset on every `groupBy` change, the
|
|
|
463
463
|
|
|
464
464
|
Snapshot and restore how the grid is showing its data, separately from the
|
|
465
465
|
data. The contract is shared with the other list components; see
|
|
466
|
-
[`docs/infra-view-state.md`](
|
|
466
|
+
[`docs/infra-view-state.md`](./infra-view-state.md).
|
|
467
467
|
|
|
468
468
|
```ts
|
|
469
469
|
import { bindViewState } from '@ixfx/components';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`<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-graph distance. Clicking a node re-centers the disk on it with an animated Möbius transform — the underlying layout never changes, only the projection.
|
|
4
4
|
|
|
5
|
-
It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](
|
|
5
|
+
It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](./tree.md)), so `TreeDataModel`, `IncrSearchTreeController`, and any `TreeComponent`-typed code work unmodified.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -36,7 +36,7 @@ It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](.
|
|
|
36
36
|
|
|
37
37
|
## Data model
|
|
38
38
|
|
|
39
|
-
Same `TreeNode` / `TreeItem` shapes as the rest of the tree family (see [`src/tree/README.md`](
|
|
39
|
+
Same `TreeNode` / `TreeItem` shapes as the rest of the tree family (see [`src/tree/README.md`](./tree.md#data-model)), including `root`, `model` (`TreeDataModel`), and `loadChildren` for lazy loading.
|
|
40
40
|
|
|
41
41
|
Keys only need to be unique **among siblings**: internally each node is identified by its key *path* from the root (`HyperbolicLayoutNode.id`), so data like a file system — where `readme.md` recurs in many folders — lays out and focuses correctly. Key-based lookups that can't know the path (`focusedKey` when given a bare key, `revealKey`, `::part(node-<key>)`) match the first node, depth-first, carrying that key.
|
|
42
42
|
|
|
@@ -58,7 +58,7 @@ el.root = {
|
|
|
58
58
|
| `model` | `TreeDataModel \| undefined` | `undefined` | Shared data model; takes precedence over `loadChildren` |
|
|
59
59
|
| `loadChildren` | `LoadChildrenCallback \| undefined` | `undefined` | Callback for lazy child loading |
|
|
60
60
|
| `restoreLoads` | `boolean` | `true` | Whether restoring a saved `center` may load its unloaded ancestors. See [View state](#view-state) |
|
|
61
|
-
| `interactionMode` (`interaction-mode`) | `'implicit' \| 'standard' \| 'manual'` | `'standard'` | Gesture mapping — see [`src/tree/README.md`](
|
|
61
|
+
| `interactionMode` (`interaction-mode`) | `'implicit' \| 'standard' \| 'manual'` | `'standard'` | Gesture mapping — see [`src/tree/README.md`](./tree.md#interactionmode-ixfx-tree-list--ixfx-miller-list) (`'checked'` is unsupported here) |
|
|
62
62
|
| `selectionFilter` (`selection-filter`) | `'none' \| 'leaf' \| 'branch' \| 'all'` | `'all'` | Which node kinds can be selected |
|
|
63
63
|
| `exclusivity` | `TreeExclusivity` | `'none'` | Accepted for interface compatibility, ignored |
|
|
64
64
|
| `filterPredicate` | `TreeFilterPredicate \| undefined` | `undefined` | Non-matching nodes (and branches with no matching descendant) are dimmed, not hidden |
|
|
@@ -80,7 +80,7 @@ el.root = {
|
|
|
80
80
|
| `focusAnimationDuration` (`focus-animation-duration`) | `number` | `400` | Duration (ms) of the animated re-center on focus change; `0` snaps instantly. Respects `prefers-reduced-motion` |
|
|
81
81
|
| `edgeRenderer` | `HyperbolicEdgeRenderer \| undefined` | `undefined` | Custom renderer for edges, in place of the default geodesic-arc path |
|
|
82
82
|
| `disableKeyboardNavigation` (`disable-keyboard-navigation`) | `boolean` | `false` | Disables the keyboard handlers below |
|
|
83
|
-
| `searchModes` (`search-modes`) | `string \| undefined` | `undefined` | Space-separated subset of `navigate select filter` — see [`src/tree/README.md`](
|
|
83
|
+
| `searchModes` (`search-modes`) | `string \| undefined` | `undefined` | Space-separated subset of `navigate select filter` — see [`src/tree/README.md`](./tree.md#incremental-search) |
|
|
84
84
|
| `selectedNode` | `TreeNode \| undefined` | `undefined` | Primary (most recent) selected node |
|
|
85
85
|
| `selectedNodes` | `ReadonlySet<TreeNode>` | `{}` | Full selection set (read-only) |
|
|
86
86
|
| `focusedNode` | `TreeNode \| undefined` (read-only) | true root | The node currently centered on the disk. Pure view-state — `root` is never mutated by focus changes |
|
|
@@ -256,7 +256,7 @@ const nodes = el.getNodes(); // ReadonlyArray<{ node: TreeNode; depth: number }>
|
|
|
256
256
|
|
|
257
257
|
## View state
|
|
258
258
|
|
|
259
|
-
Implements the shared view-state API (`getViewState()` / `setViewState()` / `clearPendingViewState()` / `view-state-change`) — see [`docs/infra-view-state.md`](
|
|
259
|
+
Implements the shared view-state API (`getViewState()` / `setViewState()` / `clearPendingViewState()` / `view-state-change`) — see [`docs/infra-view-state.md`](./infra-view-state.md). `kind` is `hyperbolictree-list`.
|
|
260
260
|
|
|
261
261
|
```typescript
|
|
262
262
|
type HyperbolicTreeListViewState = ViewStateBase & {
|
|
@@ -386,4 +386,4 @@ type HyperbolicLayoutEdge = {
|
|
|
386
386
|
};
|
|
387
387
|
```
|
|
388
388
|
|
|
389
|
-
See [`src/tree/README.md`](
|
|
389
|
+
See [`src/tree/README.md`](./tree.md) for `TreeNode`, `TreeItem` (including `weight`), `TreeDataModel`, `TreeComponent`, and incremental search — all shared with the rest of the tree family.
|
package/docs-user/icicle-tree.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](
|
|
5
|
+
It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](./tree.md)). The [sunburst](./sunburst-tree.md) is its polar counterpart.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## View state
|
|
10
10
|
|
|
11
|
-
Implements the shared view-state API (`getViewState()` / `setViewState()` / `clearPendingViewState()` / `view-state-change`) — see [`docs/infra-view-state.md`](
|
|
11
|
+
Implements the shared view-state API (`getViewState()` / `setViewState()` / `clearPendingViewState()` / `view-state-change`) — see [`docs/infra-view-state.md`](./infra-view-state.md). `kind` is `icicletree-list`.
|
|
12
12
|
|
|
13
13
|
| Field | Type | Meaning |
|
|
14
14
|
|---|---|---|
|
package/docs-user/incr-search.md
CHANGED
|
@@ -6,7 +6,7 @@ Utilities for incremental (type-ahead) search over data collections or DOM-backe
|
|
|
6
6
|
popover in `ixfx-grid-list` / `ixfx-vertical-list` / `ixfx-detail-list` /
|
|
7
7
|
`ixfx-data-grid` / `ixfx-tree-list` / `ixfx-miller-list`. Three modes
|
|
8
8
|
(navigate / select / filter), driven by `list.search.*` commands. See
|
|
9
|
-
[`docs/infra-incremental.md`](
|
|
9
|
+
[`docs/infra-incremental.md`](./infra-incremental.md) — start there
|
|
10
10
|
for anything component-facing.
|
|
11
11
|
- **`createIncrSearch`** — the low-level fuzzy engine (fzf) both the controller
|
|
12
12
|
and the helpers below are built on.
|
package/docs-user/index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ixfx/components",
|
|
3
|
-
"version": "0.16.
|
|
4
|
-
"generated": "2026-11-
|
|
3
|
+
"version": "0.16.4",
|
|
4
|
+
"generated": "2026-11-02T21:02:52.405Z",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"name": "ac-text",
|
|
@@ -305,14 +305,38 @@
|
|
|
305
305
|
}
|
|
306
306
|
],
|
|
307
307
|
"overviews": [
|
|
308
|
+
{
|
|
309
|
+
"name": "infra-handle.md",
|
|
310
|
+
"path": "docs-user/infra-handle.md"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"name": "infra-incremental.md",
|
|
314
|
+
"path": "docs-user/infra-incremental.md"
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"name": "infra-theming.md",
|
|
318
|
+
"path": "docs-user/infra-theming.md"
|
|
319
|
+
},
|
|
308
320
|
{
|
|
309
321
|
"name": "infra-tickled.md",
|
|
310
322
|
"path": "docs-user/infra-tickled.md"
|
|
311
323
|
},
|
|
324
|
+
{
|
|
325
|
+
"name": "infra-view-state.md",
|
|
326
|
+
"path": "docs-user/infra-view-state.md"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"name": "list-state.md",
|
|
330
|
+
"path": "docs-user/list-state.md"
|
|
331
|
+
},
|
|
312
332
|
{
|
|
313
333
|
"name": "user-catalog.md",
|
|
314
334
|
"path": "docs-user/user-catalog.md"
|
|
315
335
|
},
|
|
336
|
+
{
|
|
337
|
+
"name": "user-selection-intents.md",
|
|
338
|
+
"path": "docs-user/user-selection-intents.md"
|
|
339
|
+
},
|
|
316
340
|
{
|
|
317
341
|
"name": "user-theming.md",
|
|
318
342
|
"path": "docs-user/user-theming.md"
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Drag / resize handles
|
|
2
|
+
|
|
3
|
+
A **handle** is the grab affordance for moving or resizing an object — a
|
|
4
|
+
splitter grip, a resize edge, a reorder handle. `handleStyles`
|
|
5
|
+
(`src/styles/handle.js`) is the one shared look, so every draggable component
|
|
6
|
+
reads the same and themes through the same variables.
|
|
7
|
+
|
|
8
|
+
Before this module the library had six unrelated handle idioms (two of them
|
|
9
|
+
near-duplicate 3-dot implementations, two hard-coded Unicode glyphs). Don't add a
|
|
10
|
+
seventh.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { themeFallbacks } from '../styles/fallbacks.js';
|
|
18
|
+
import { handleStyles } from '../styles/handle.js';
|
|
19
|
+
|
|
20
|
+
class MyComponent extends LitElement {
|
|
21
|
+
static styles = [themeFallbacks, handleStyles, css`
|
|
22
|
+
.hit-zone { /* your resize / drag target */ }
|
|
23
|
+
`];
|
|
24
|
+
|
|
25
|
+
render() {
|
|
26
|
+
return html`
|
|
27
|
+
<div class="hit-zone" @pointerdown=${this.#onDown}>
|
|
28
|
+
<span class="ixfx-handle" data-handle="flat" data-shape="horizontal" part="handle"></span>
|
|
29
|
+
</div>`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Bind pointer handlers to the **hit-zone**, not the handle — the handle is
|
|
35
|
+
`pointer-events: none`.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Two independent axes
|
|
40
|
+
|
|
41
|
+
| Attribute | Values | Meaning |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `data-handle` | `flat` (default) · `dotted` · `dimpled` · `grated` | **texture** — how the mark looks |
|
|
44
|
+
| `data-shape` | `horizontal` (default) · `vertical` · `spot` | **topology** — a wide bar, a tall bar, or a small centred point handle |
|
|
45
|
+
|
|
46
|
+
Any texture combines with any shape.
|
|
47
|
+
|
|
48
|
+
- **flat** — a solid rounded capsule (a bar) or dot (`spot`), filled with
|
|
49
|
+
`--ixfx-handle-color`. The `ixfx-bottom-drawer` resize handle.
|
|
50
|
+
- **dotted** — marks in the [Lucide *grip*](https://lucide.dev/icons/grip)
|
|
51
|
+
arrangement: **three in a row** (`horizontal`), **three in a column**
|
|
52
|
+
(`vertical`), or a **3×3 block** (`spot`). Drawn as one `::before` mark plus
|
|
53
|
+
`box-shadow` copies, so each mark's corner rounding is `--ixfx-handle-radius`
|
|
54
|
+
(→ the theme `--radius-l`): **round** normally, **square** under a flat theme
|
|
55
|
+
(`--radius-l: 0`, e.g. `theme-cyberpunk`). Tinted by `--ixfx-handle-color`. The
|
|
56
|
+
`ixfx-data-grid` column reorder handle, `ixfx-timeline-track-tab` reorder
|
|
57
|
+
handle, `ixfx-dock-host` junction handles.
|
|
58
|
+
- **dimpled** — that same layout, but each **bar** mark is pressed in: a lighter
|
|
59
|
+
copy peeking out above-left and a darker copy below-right (`spot` stays plain —
|
|
60
|
+
the 3D is imperceptible at that size). Splitter grips (`ixfx-split-layout`,
|
|
61
|
+
`ixfx-twosplit-layout`).
|
|
62
|
+
- **grated** — fine 45° knurling: parallel ridges, each a **lit edge, the base
|
|
63
|
+
colour, then a shaded edge**. `spot` is the same hatch cropped square.
|
|
64
|
+
|
|
65
|
+
`dotted`, `dimpled` and `grated` are texture only — no plate, box or border; the
|
|
66
|
+
gaps are transparent, so the surface behind shows through. The base is
|
|
67
|
+
`--ixfx-handle-color` (`currentColor`); `--ixfx-handle-highlight` /
|
|
68
|
+
`--ixfx-handle-shadow` default to that colour lightened / darkened, so `dimpled`
|
|
69
|
+
stays as prominent as `dotted` and re-tones with the theme.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Active state
|
|
74
|
+
|
|
75
|
+
The handle goes to `--ixfx-handle-opacity-active` (and, for `flat` / `dotted`,
|
|
76
|
+
`--ixfx-handle-color-active`) when:
|
|
77
|
+
|
|
78
|
+
- its **immediate parent** (the hit-zone) is `:hover` or `:focus-visible`, or
|
|
79
|
+
- the hit-zone or the handle carries `[data-active]`.
|
|
80
|
+
|
|
81
|
+
While a drag is live the pointer may leave the hit-zone, so set `[data-active]`
|
|
82
|
+
on the hit-zone yourself for the duration and the handle stays lit.
|
|
83
|
+
|
|
84
|
+
To suppress the reveal entirely, set `--ixfx-handle-opacity-active` equal to
|
|
85
|
+
`--ixfx-handle-opacity`.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## CSS variables
|
|
90
|
+
|
|
91
|
+
Defaults resolve through `themeFallbacks`. Override on the handle element or any
|
|
92
|
+
ancestor (`:host`, the hit-zone, …).
|
|
93
|
+
|
|
94
|
+
| Variable | Default | Purpose |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `--ixfx-handle-color` | `var(--surface-muted-text)` | `flat` / `dotted` fill |
|
|
97
|
+
| `--ixfx-handle-color-active` | `var(--accent)` | `flat` / `dotted` hover / focus / `[data-active]` |
|
|
98
|
+
| `--ixfx-handle-thickness` | `6px` | cross-axis size of a bar |
|
|
99
|
+
| `--ixfx-handle-length` | `44px` | long-axis size of a bar (`100%` fills) |
|
|
100
|
+
| `--ixfx-handle-spot-size` | `14px` | width = height of a `spot` |
|
|
101
|
+
| `--ixfx-handle-radius` | `var(--radius-l)` | `flat` capsule + `dotted` / `dimpled` mark rounding (0 ⇒ squares) |
|
|
102
|
+
| `--ixfx-handle-opacity` | `0.55` | rest opacity |
|
|
103
|
+
| `--ixfx-handle-opacity-active` | `1` | active opacity |
|
|
104
|
+
| `--ixfx-handle-dot-size` | `4px` | mark size (dotted / dimpled) |
|
|
105
|
+
| `--ixfx-handle-dot-gap` | `length / 6` (bar) · `spot / 3` | mark centre-to-centre step |
|
|
106
|
+
| `--ixfx-handle-tile` | `5px` | knurl period (grated) |
|
|
107
|
+
| `--ixfx-handle-highlight` | `color-mix(currentColor 35%, #fff)` | lit deboss edge (dimpled / grated) |
|
|
108
|
+
| `--ixfx-handle-shadow` | `color-mix(currentColor 60%, #000)` | shaded deboss edge (dimpled / grated) |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Notes & limits
|
|
113
|
+
|
|
114
|
+
- **Host-as-handle** (`:host(.ixfx-handle)`) is not supported yet — always wrap
|
|
115
|
+
the class on a child element inside the hit-zone.
|
|
116
|
+
- The handle exposes `part="handle"` (rename per component where a more specific
|
|
117
|
+
part name already exists, e.g. `part="grip"` / `part="reorder-handle"`).
|
|
118
|
+
- No motion beyond the colour / opacity transition; nothing to gate for reduced
|
|
119
|
+
motion (the module still drops the transition under
|
|
120
|
+
`prefers-reduced-motion: reduce`).
|
|
121
|
+
|
|
122
|
+
See it live: `demo/handle.html` (4 textures × 3 shapes + variable knobs).
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Incremental search
|
|
2
|
+
|
|
3
|
+
Type-ahead search shared by the list and tree components. One popover, one
|
|
4
|
+
`ReactiveController`, three behaviours ("modes"), triggered by
|
|
5
|
+
`@clinth/ui-commands` commands. Built on the fuzzy engine in
|
|
6
|
+
`src/incr-search/incr-search.ts` (fzf).
|
|
7
|
+
|
|
8
|
+
Supported today: `ixfx-grid-list`, `ixfx-vertical-list`, `ixfx-detail-list`,
|
|
9
|
+
`ixfx-data-grid`, `ixfx-tree-list`, `ixfx-miller-list`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## The three modes
|
|
14
|
+
|
|
15
|
+
| Mode | As you type… | Enter | On close |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| **navigate** | the cursor moves to the best match; ↑/↓ cycle between matches | activates the current match, closes | cursor stays |
|
|
18
|
+
| **select** | the selection becomes the set of matching items, live | closes | selection stays |
|
|
19
|
+
| **filter** | items that don't match are hidden | closes (keeps the filter with `stickyFilter`) | filter is removed |
|
|
20
|
+
|
|
21
|
+
All modes highlight the matched characters (CSS Custom Highlights) where the
|
|
22
|
+
component supports it.
|
|
23
|
+
|
|
24
|
+
### Conversions
|
|
25
|
+
|
|
26
|
+
The popover is interactive. While it's open you can, without retyping:
|
|
27
|
+
|
|
28
|
+
- **switch mode** — buttons for each other enabled mode, or **Tab** /
|
|
29
|
+
**Shift+Tab** in the input to cycle forward / back (`cycleMode(±1)`). The old
|
|
30
|
+
mode's effect is undone first (e.g. leaving *filter* un-hides), then the new
|
|
31
|
+
mode is applied to the current query. Example: start in *filter*, hit
|
|
32
|
+
**navigate** to stop hiding and just move the cursor.
|
|
33
|
+
- **re-open** — running a search command while the popover is open keeps the
|
|
34
|
+
query: it switches to that command's mode and refocuses the input with the
|
|
35
|
+
text selected. `focus()` refocuses without changing mode.
|
|
36
|
+
- **Select matches** — a one-shot button (shown in *navigate* / *filter* when
|
|
37
|
+
*select* is enabled) that turns the current match set into the selection while
|
|
38
|
+
staying in the current mode.
|
|
39
|
+
|
|
40
|
+
### Sticky filter
|
|
41
|
+
|
|
42
|
+
By default a filter lasts only while the popover is open. Set
|
|
43
|
+
`list.search.stickyFilter = true` (or the `stickyFilter` controller option) so
|
|
44
|
+
that Enter in *filter* mode closes the popover but keeps the filter. A
|
|
45
|
+
"Filtered: …" chip (part `search-kept`) stays in the popover's place, and the
|
|
46
|
+
filter re-applies as items change. Clear it with the chip's ×, Escape on the
|
|
47
|
+
list, or `search.clearFilter()`. Clicking the chip, or running any search
|
|
48
|
+
command, reopens the popover with the kept query. `search.keptFilter` is the
|
|
49
|
+
kept query (or `undefined`).
|
|
50
|
+
|
|
51
|
+
### Which modes are offered
|
|
52
|
+
|
|
53
|
+
The effective set is **component capability ∩ the `search-modes` attribute**:
|
|
54
|
+
|
|
55
|
+
- **capability** — most components offer all three. A component with no
|
|
56
|
+
selection concept would omit `select`, etc.
|
|
57
|
+
- **`search-modes`** attribute — a space-separated subset of
|
|
58
|
+
`navigate select filter`. Unset ⇒ the component default. Empty (`search-modes=""`)
|
|
59
|
+
⇒ search disabled: no commands registered, no popover.
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<ixfx-grid-list search-modes="navigate filter"></ixfx-grid-list>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
Each component owns a `@clinth/ui-commands` `CommandRegistry` (exposed as
|
|
70
|
+
`el.registry`) and registers one command per enabled mode:
|
|
71
|
+
|
|
72
|
+
| id | opens |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `list.search.navigate` | popover in navigate mode |
|
|
75
|
+
| `list.search.select` | popover in select mode |
|
|
76
|
+
| `list.search.filter` | popover in filter mode |
|
|
77
|
+
|
|
78
|
+
There is **no default keybinding** — a consumer binds a key or button:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
el.registry.invoke('list.search.filter');
|
|
82
|
+
// or via @clinth/ui-commands' InputManager / a data-command button
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`ixfx-grouped-item-lister.dispatch('list.search.filter')` fans the command out to
|
|
86
|
+
every sub-list.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Adding search to a new component
|
|
91
|
+
|
|
92
|
+
1. **Adapter.** Implement `ListSearchAdapter<TItem>` (`src/incr-search/list-search-controller.ts`):
|
|
93
|
+
`getItems` / `getLabel` / `getKey`, then `focusItem` (navigate),
|
|
94
|
+
`selectItems` (select), `applyFilter` (filter; `undefined` clears).
|
|
95
|
+
Optional: `activateItem`, the `highlight*` hooks, `container` (an element
|
|
96
|
+
whose child-list mutations should re-`refresh()` the item set).
|
|
97
|
+
- Element-based lists: `createElementListSearchAdapter(this, { itemSelector })`.
|
|
98
|
+
- Tree-based: `createTreeListSearchAdapter(this, { labelSelector })`.
|
|
99
|
+
|
|
100
|
+
2. **Controller.** One field:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
readonly search = new ListSearchController<TItem>(this, {
|
|
104
|
+
adapter: /* from step 1 */,
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
3. **Styles + field.** Add `listSearchStyles` to `static styles` and
|
|
109
|
+
`${this.search.renderField()}` to `render()`, inside a `position: relative`
|
|
110
|
+
ancestor that is **not** the scroll container (so the popover doesn't scroll
|
|
111
|
+
away — put `overflow:auto` on an inner element, `position:relative` on `:host`).
|
|
112
|
+
|
|
113
|
+
4. **Modes + commands.** Keep a `search-modes` reflected property; on connect and
|
|
114
|
+
whenever it changes:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
this.search.setModes(/* capability ∩ search-modes */);
|
|
118
|
+
this.#unregister?.();
|
|
119
|
+
this.#unregister = registerListSearchCommands(this.registry, this.search);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Own the registry (`createCommandRegistry()`), expose it as `registry`,
|
|
123
|
+
unregister on disconnect. Route any `dispatch(commandId, args)` to
|
|
124
|
+
`registry.invoke(commandId, args)`.
|
|
125
|
+
|
|
126
|
+
5. **Keyboard.** `if (this.search.isOpen) return;` at the top of the host's
|
|
127
|
+
keydown handler so list navigation doesn't fight the field.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Notes
|
|
132
|
+
|
|
133
|
+
- **Virtualised lists** (`ixfx-grid-list`, `ixfx-data-grid`) — filter genuinely
|
|
134
|
+
drops items from layout/virtualisation. Character highlights are best-effort:
|
|
135
|
+
applied to whatever is in the rendered window (data-grid does no highlighting —
|
|
136
|
+
a match position indexes into the whole-row text, which spans cells).
|
|
137
|
+
- **Trees** snapshot the whole node set when the popover opens (`getNodes()`
|
|
138
|
+
returns every node regardless of expansion or an active filter), so refining a
|
|
139
|
+
query can always re-surface a node. `navigate` expands the match's ancestors;
|
|
140
|
+
`filter` prunes via `filterPredicate` but does **not** auto-expand collapsed
|
|
141
|
+
ancestors of a deep match.
|
|
142
|
+
- **Highlights** painted inside a host's shadow root are styled by
|
|
143
|
+
`listSearchStyles` (keys `incr-search-highlight`, `grid-list-search`,
|
|
144
|
+
`data-grid-search`; tokens `--search-highlight-bg` / `--search-highlight-color`).
|
|
145
|
+
A host using another key must add its own rule. Text in the light DOM (the
|
|
146
|
+
element lists paint onto the item elements) needs a page-level
|
|
147
|
+
`::highlight(<key>)` rule:
|
|
148
|
+
`::highlight(vertical-list-search) { background: var(--accent); color: var(--accent-text); }`.
|
|
149
|
+
- Everything is inert when the effective mode set is empty.
|
|
150
|
+
|
|
151
|
+
## Deprecated
|
|
152
|
+
|
|
153
|
+
`createElementIncrSearch` and `IncrSearchTreeController` (the external-`<input>`
|
|
154
|
+
filter-only controllers) still work but are superseded by the built-in popover.
|