@ixfx/components 0.16.3 → 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 +2 -2
- package/bundle/index.js +5 -5
- package/dist/{index-DyaHA9Kk.d.ts → index-YCDugjdu.d.ts} +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{miller-Be7x4ufN.js → miller-BrmBywNZ.js} +17 -7
- package/dist/miller.d.ts +1 -1
- package/dist/miller.js +1 -1
- package/docs-user/README.md +5 -0
- package/docs-user/bottom-drawer.md +2 -2
- package/docs-user/crumbs.md +1 -1
- package/docs-user/data-grid.md +2 -2
- 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 +3 -3
- 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 +22 -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 +5 -0
- package/docs-user/miller.md +2 -2
- package/docs-user/panel.md +3 -3
- 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 +2 -2
- package/docs-user/treemap.md +2 -2
- package/docs-user/twosplit-layout.md +3 -3
- package/docs-user/user-selection-intents.md +3 -3
- package/docs-user/user-theming.md +1 -1
- package/docs-user/vertical-list.md +2 -2
- package/llms.txt +5 -0
- package/package.json +1 -1
|
@@ -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.
|