@ixfx/components 0.4.4 → 0.5.1
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 +136 -8
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +569 -23
- package/bundle/index.js.map +1 -1
- package/bundle/style.css +1 -1
- package/dist/ac-text.d.ts +1 -1
- package/dist/{button-DxYee1ZH.js → button-CTE8crYn.js} +3 -1
- package/dist/button-CTE8crYn.js.map +1 -0
- package/dist/button.d.ts +1 -1
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/{colour-picker-BacNRabo.js → colour-picker-C3Ag4uh-.js} +2 -2
- package/dist/{colour-picker-BacNRabo.js.map → colour-picker-C3Ag4uh-.js.map} +1 -1
- package/dist/colour-picker.d.ts +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/{defaults-C3b9OWJD.js → defaults-DcE8RQJc.js} +19 -1
- package/dist/defaults-DcE8RQJc.js.map +1 -0
- package/dist/{icon-DpA9x2Ve.d.ts → icon-BUwP1CZ1.d.ts} +2 -2
- package/dist/icon-BUwP1CZ1.d.ts.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +1 -1
- package/dist/{index-DfEkTGvT.d.ts → index-B1UKcA7l.d.ts} +3 -3
- package/dist/{index-DfEkTGvT.d.ts.map → index-B1UKcA7l.d.ts.map} +1 -1
- package/dist/{index-D_ftz_IT.d.ts → index-BKAlRgbq.d.ts} +2 -2
- package/dist/{index-D_ftz_IT.d.ts.map → index-BKAlRgbq.d.ts.map} +1 -1
- package/dist/{index-CoqtMSd9.d.ts → index-BUB5SICW.d.ts} +2 -2
- package/dist/{index-CoqtMSd9.d.ts.map → index-BUB5SICW.d.ts.map} +1 -1
- package/dist/{index-BtGFYfaK.d.ts → index-RqMh1i-O.d.ts} +2 -2
- package/dist/{index-BtGFYfaK.d.ts.map → index-RqMh1i-O.d.ts.map} +1 -1
- package/dist/index.d.ts +140 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +548 -27
- package/dist/index.js.map +1 -1
- package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -2
- package/dist/{labelled-input-base-DiTme1JO.d.ts.map → labelled-input-base-Dp_9KP0G.d.ts.map} +1 -1
- package/dist/labelled-radial-input.d.ts +3 -3
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/{menu-item-DSyf9f_L.d.ts → menu-item-CQ4Qg731.d.ts} +2 -2
- package/dist/{menu-item-DSyf9f_L.d.ts.map → menu-item-CQ4Qg731.d.ts.map} +1 -1
- package/dist/menu.d.ts +2 -2
- package/dist/panel.d.ts +1 -1
- package/dist/polar-pad.d.ts +1 -1
- package/dist/{radial-input-DXMN55Nz.d.ts → radial-input-BhZeyyP-.d.ts} +2 -2
- package/dist/{radial-input-DXMN55Nz.d.ts.map → radial-input-BhZeyyP-.d.ts.map} +1 -1
- package/dist/radial-input.d.ts +1 -1
- package/dist/range-input.d.ts +1 -1
- package/dist/range.d.ts +1 -1
- package/dist/{slider-input-XppH3vcy.d.ts → slider-input-TidGhvXt.d.ts} +2 -2
- package/dist/{slider-input-XppH3vcy.d.ts.map → slider-input-TidGhvXt.d.ts.map} +1 -1
- package/dist/slider-input.d.ts +1 -1
- package/dist/style.css +1 -1
- package/dist/swipe.d.ts +1 -1
- package/dist/{tab-list-DzknpdSo.js → tab-list-DhkVhRQc.js} +7 -2
- package/dist/{tab-list-DzknpdSo.js.map → tab-list-DhkVhRQc.js.map} +1 -1
- package/dist/{tab-list-BohmbvqF.d.ts → tab-list-VtWT-_FG.d.ts} +2 -2
- package/dist/{tab-list-BohmbvqF.d.ts.map → tab-list-VtWT-_FG.d.ts.map} +1 -1
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +1 -1
- package/dist/wheel-nudge-BlSCe_hD.js.map +1 -1
- package/dist/{wheel-nudge-DGrNzWuE.d.ts → wheel-nudge-DgMTRYlh.d.ts} +3 -3
- package/dist/wheel-nudge-DgMTRYlh.d.ts.map +1 -0
- package/dist/xy-pad.d.ts +1 -1
- package/docs-user/README.md +2 -0
- package/docs-user/button.md +50 -0
- package/docs-user/narrowed-text.md +33 -1
- package/docs-user/notifier.md +131 -0
- package/docs-user/plots.md +61 -1
- package/docs-user/slider-input.md +673 -0
- package/docs-user/tabs.md +52 -14
- package/docs-user/user-catalog.md +69 -4
- package/docs-user/vertical-list.md +102 -7
- package/package.json +3 -1
- package/dist/button-DxYee1ZH.js.map +0 -1
- package/dist/defaults-C3b9OWJD.js.map +0 -1
- package/dist/icon-DpA9x2Ve.d.ts.map +0 -1
- package/dist/wheel-nudge-DGrNzWuE.d.ts.map +0 -1
package/docs-user/tabs.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
A complete tabbed interface system using several coordinated components:
|
|
4
4
|
|
|
5
|
-
- **`ixfx-tab-layout`** — Layout wrapper (minimal)
|
|
6
|
-
- **`ixfx-tab-layout-item`** — Individual tab item within layout
|
|
7
5
|
- **`ixfx-tab-list`** — Tab header bar with tab buttons
|
|
8
6
|
- **`ixfx-tab-list-item`** — Individual tab button
|
|
9
7
|
- **`ixfx-tab-panel`** — Content panel for a single tab
|
|
@@ -12,17 +10,15 @@ A complete tabbed interface system using several coordinated components:
|
|
|
12
10
|
## Basic usage
|
|
13
11
|
|
|
14
12
|
```html
|
|
15
|
-
<ixfx-tab-
|
|
16
|
-
<ixfx-tab-list>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<ixfx-tab-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</ixfx-tab-panels>
|
|
25
|
-
</ixfx-tab-layout>
|
|
13
|
+
<ixfx-tab-list>
|
|
14
|
+
<ixfx-tab-list-item id="tab-home" for="panel-home">Home</ixfx-tab-list-item>
|
|
15
|
+
<ixfx-tab-list-item id="tab-profile" for="panel-profile">Profile</ixfx-tab-list-item>
|
|
16
|
+
</ixfx-tab-list>
|
|
17
|
+
|
|
18
|
+
<ixfx-tab-panels>
|
|
19
|
+
<ixfx-tab-panel id="panel-home">Home content</ixfx-tab-panel>
|
|
20
|
+
<ixfx-tab-panel id="panel-profile">Profile content</ixfx-tab-panel>
|
|
21
|
+
</ixfx-tab-panels>
|
|
26
22
|
```
|
|
27
23
|
|
|
28
24
|
Wire them together:
|
|
@@ -47,13 +43,15 @@ Manages tab selection and keyboard navigation.
|
|
|
47
43
|
|---------------------|-----------------------------------|----------------|----------------------------------|
|
|
48
44
|
| `orientation` | `'horizontal' \| 'vertical-stack' \| 'vertical-side'` | `'horizontal'` | Layout direction |
|
|
49
45
|
| `flip` | `boolean` | `false` | Flip indicator to opposite side |
|
|
50
|
-
| `scrollable` | `boolean` | `false` | Enable scroll overflow
|
|
46
|
+
| `scrollable` | `boolean` | `false` | Enable scroll overflow. When set, vertical mousewheel on horizontal lists scrolls horizontally (and vice-versa for vertical lists). |
|
|
47
|
+
| `drag-mode` | `'off' \| 'reorder'` | `'off'` | Pointer-driven reordering within the list (sets `grab` cursor on items) |
|
|
51
48
|
|
|
52
49
|
### Events
|
|
53
50
|
|
|
54
51
|
| Event | Detail | Description |
|
|
55
52
|
|----------|------------------------------------------------------|---------------------------|
|
|
56
53
|
| `change` | `{ previous, selected, for }` | Tab selection changed |
|
|
54
|
+
| `reorder` | `{ id, fromIndex, toIndex }` | Tab reordered (only fired when `drag-mode="reorder"`). Fires after the DOM has been updated. Press <kbd>Esc</kbd> or receive a `pointercancel` while dragging to abort — no `reorder` event fires in that case. |
|
|
57
55
|
|
|
58
56
|
### Methods
|
|
59
57
|
|
|
@@ -169,6 +167,46 @@ A recommended `.tab-toolbar-btn` style for the page stylesheet:
|
|
|
169
167
|
|----------|--------|----------------------------|
|
|
170
168
|
| `close` | — | Fired when close button clicked; item removes itself from DOM |
|
|
171
169
|
|
|
170
|
+
### Command activation
|
|
171
|
+
|
|
172
|
+
Tabs can be programmatically activated via the `CommandRegistry` system. This is useful for keyboard shortcuts (e.g. `Alt+1` to switch to tab 1).
|
|
173
|
+
|
|
174
|
+
1. Create a `CommandRegistry` and register commands for each tab:
|
|
175
|
+
```js
|
|
176
|
+
import { CommandRegistry } from '@clinth/ui-commands';
|
|
177
|
+
|
|
178
|
+
const commands = new CommandRegistry();
|
|
179
|
+
commands.register({ id: 'tab-home', label: 'Home', description: 'Switch to Home tab', execute: () => {} });
|
|
180
|
+
commands.register({ id: 'tab-profile', label: 'Profile', description: 'Switch to Profile tab', execute: () => {} });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
2. Bind keyboard shortcuts to the commands using `KeyboardManager`:
|
|
184
|
+
```js
|
|
185
|
+
import { KeyboardManager } from '@clinth/ui-commands';
|
|
186
|
+
|
|
187
|
+
const keyboard = new KeyboardManager();
|
|
188
|
+
keyboard.bind({ key: '1', modifiers: new Set(['alt']), commandId: 'tab-home' });
|
|
189
|
+
keyboard.bind({ key: '2', modifiers: new Set(['alt']), commandId: 'tab-profile' });
|
|
190
|
+
keyboard.onKeyEvent((commandId) => commands.invoke(commandId));
|
|
191
|
+
keyboard.attach(document.body);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
3. Set the `command-active` attribute and assign the registry to each tab item:
|
|
195
|
+
```html
|
|
196
|
+
<ixfx-tab-list>
|
|
197
|
+
<ixfx-tab-list-item command-active="tab-home">Home</ixfx-tab-list-item>
|
|
198
|
+
<ixfx-tab-list-item command-active="tab-profile">Profile</ixfx-tab-list-item>
|
|
199
|
+
</ixfx-tab-list>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
for (const item of document.querySelectorAll('ixfx-tab-list-item[command-active]')) {
|
|
204
|
+
item.registry = commands;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
When a command is invoked (via keyboard shortcut or `commands.invoke('tab-home')`), the associated tab will be activated as if clicked.
|
|
209
|
+
|
|
172
210
|
### CSS custom properties
|
|
173
211
|
|
|
174
212
|
| Property | Default | Description |
|
|
@@ -30,12 +30,33 @@ A circular input control for selecting values in a rotational manner. Ideal for
|
|
|
30
30
|
### ixfx-ac-text
|
|
31
31
|
An autocomplete text input that suggests options as the user type. Perfect for search boxes, tag entry, or any field where you want to assist users with predefined options.
|
|
32
32
|
|
|
33
|
+
### ixfx-ac-token
|
|
34
|
+
A token/tag input with per-token autocomplete, where committed tokens appear as inline chips and a suggestion popup follows the typing cursor. Ideal for email recipient fields, tag pickers, or any multi-value entry with per-value suggestions.
|
|
35
|
+
|
|
33
36
|
### ixfx-narrowed-text
|
|
34
37
|
A specialized text input that constrains input length and provides visual feedback about remaining capacity. Use for fields with strict character limits like tweet composition or SMS messages.
|
|
35
38
|
|
|
36
39
|
### ixfx-editable-label
|
|
37
40
|
A label that becomes an inline editor when clicked or focused. Useful for in-place editing of list items, card titles, or any display text that users might need to modify.
|
|
38
41
|
|
|
42
|
+
### ixfx-editable-number
|
|
43
|
+
A numeric label that combines inline text editing with pointer-drag-to-adjust (Ableton-style), optional min/max clamping, step snapping, and a fill-bar visualisation. Use for mixing-console-style numeric controls where both typing and dragging are appropriate.
|
|
44
|
+
|
|
45
|
+
### ixfx-range-input
|
|
46
|
+
A label-free, fully styled range slider wrapping the native `<input type="range">`. Supports horizontal and vertical orientations, custom thumb shapes, gradient fills, and an optional value tooltip. Use as a drop-in replacement for native range inputs when you need consistent theming.
|
|
47
|
+
|
|
48
|
+
### ixfx-slider-input
|
|
49
|
+
A linear SVG-based slider with horizontal/vertical orientations, single/dual/triple value modes, and triangle/needle/dot markers. Use for audio-mixer style multi-handle sliders, threshold selectors, or any interface requiring precise linear control.
|
|
50
|
+
|
|
51
|
+
### ixfx-radial-input
|
|
52
|
+
A rotary dial input rendered as a flattened arc SVG, inspired by music production interfaces. Responds to drag and keyboard input. Use as a building block for custom rotary controls or combine with `ixfx-editable-label` via `ixfx-labelled-radial-input`.
|
|
53
|
+
|
|
54
|
+
### ixfx-swipe
|
|
55
|
+
A touch-friendly swipe/nudge component that produces a value in [-1, 1] based on tap-and-drag distance, emitted only on pointer release. Use as a compact gesture control for audio scrubbing, video seeking, or any value where a spring-back gesture is appropriate.
|
|
56
|
+
|
|
57
|
+
### ixfx-vertical-list
|
|
58
|
+
A scrollable vertical list of items with selection, keyboard navigation, and incremental search. Supports plain `<li>` items in HTML or rich custom elements added programmatically. Use wherever you need a list with first-class keyboard and selection support.
|
|
59
|
+
|
|
39
60
|
### ixfx-color-picker
|
|
40
61
|
A comprehensive color selection tool with multiple modes (hue/saturation/lightness, red/green/blue, etc.) and support for saving custom colors. Use whenever users need to select or adjust colors.
|
|
41
62
|
|
|
@@ -53,9 +74,21 @@ A tabbed interface for organizing content into separate views. Use when you have
|
|
|
53
74
|
### ixfx-tab-panel
|
|
54
75
|
Individual panels that work with the ixfx-tabs component to create tabbed interfaces.
|
|
55
76
|
|
|
77
|
+
### ixfx-tab-panels
|
|
78
|
+
A container that manages a collection of `ixfx-tab-panel` children and exposes a `selectPanel()` method to switch between them. Pair with `ixfx-tab-list` for a complete tabbed interface.
|
|
79
|
+
|
|
80
|
+
### ixfx-tab-list
|
|
81
|
+
The header bar of a tabbed interface, holding `ixfx-tab-list-item` buttons and emitting a `change` event when the active tab changes. Pair with `ixfx-tab-panels` for a complete tabbed interface.
|
|
82
|
+
|
|
83
|
+
### ixfx-tab-list-item
|
|
84
|
+
Individual tab buttons used inside `ixfx-tab-list`. Use the `for` attribute to associate each button with the id of its corresponding `ixfx-tab-panel`.
|
|
85
|
+
|
|
56
86
|
### ixfx-data-display
|
|
57
87
|
A flexible component for presenting various types of data with consistent formatting. Use for showing metrics, status information, or any structured data that benefits from standardized presentation.
|
|
58
88
|
|
|
89
|
+
### ixfx-transitory-label
|
|
90
|
+
An inline text label that animates between values instead of changing instantly, with fade, slide-up, and other transition effects. Use for status readouts, mode indicators, or any text that benefits from smooth value transitions rather than abrupt updates.
|
|
91
|
+
|
|
59
92
|
## Navigation Components
|
|
60
93
|
|
|
61
94
|
### ixfx-menu
|
|
@@ -76,6 +109,15 @@ A hierarchical tree view component for displaying nested data structures. Perfec
|
|
|
76
109
|
### ixfx-crumb-navigation
|
|
77
110
|
Similar to breadcrumb navigation but with a more compact representation focused on the immediate path context.
|
|
78
111
|
|
|
112
|
+
### ixfx-crumb-path
|
|
113
|
+
A path-style breadcrumb (e.g. `/users/jane/projects`) with editable path segments and autocomplete dropdowns. Supports lazy-loaded sibling paths. Use for file paths, URL navigation, or any address-bar-style interface where segments can be edited inline.
|
|
114
|
+
|
|
115
|
+
### ixfx-miller
|
|
116
|
+
A Miller column component that 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. Use for navigating deeply nested data with persistent visibility of the path.
|
|
117
|
+
|
|
118
|
+
### ixfx-grouped-item-lister
|
|
119
|
+
A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (ungrouped mode) or split into labelled groups, each with its own sub-component (grouped mode). Use for categorised lists, filtered views, or any list that needs both grouping and rich item rendering.
|
|
120
|
+
|
|
79
121
|
## Layout Components
|
|
80
122
|
|
|
81
123
|
### ixfx-split-layout
|
|
@@ -110,6 +152,15 @@ A component for representing and manipulating numerical ranges with visual handl
|
|
|
110
152
|
### ixfx-range-multi
|
|
111
153
|
An extension of the range component that allows multiple non-overlapping ranges to be selected simultaneously. Ideal for selecting multiple time segments or value ranges.
|
|
112
154
|
|
|
155
|
+
### ixfx-plot-multi-axis
|
|
156
|
+
A multi-series plot that renders several named 1D data streams simultaneously with per-series colours. Use for comparing multiple sensor readings, multi-track audio visualisation, or any scenario where several signals need to share the same axes.
|
|
157
|
+
|
|
158
|
+
### ixfx-plot-histogram
|
|
159
|
+
A histogram plot that bins buffered values into buckets over the observed (or fixed) data range, with no time or sequence axis. Use for value distributions, density estimation, or any scenario where the shape of a dataset matters more than its order.
|
|
160
|
+
|
|
161
|
+
### ixfx-hex-editor
|
|
162
|
+
A hex editor for inspecting and editing binary data, with configurable columns, ASCII/Hex/Offset columns, byte-level selection, drag-to-edit values, undo/redo, and colour-scheme options. Use for binary file inspection, debuggers, or any tool that needs to view or modify raw bytes.
|
|
163
|
+
|
|
113
164
|
## Utility Components
|
|
114
165
|
|
|
115
166
|
### ixfx-icon
|
|
@@ -124,6 +175,9 @@ A simple indicator light component that can show on/off states or various colors
|
|
|
124
175
|
### ixfx-horizontal-selector
|
|
125
176
|
A compact horizontal control for selecting from a set of options. Use when you need a more space-efficient alternative to radio groups or dropdowns.
|
|
126
177
|
|
|
178
|
+
### ixfx-incr-search
|
|
179
|
+
Incremental (type-ahead) search utilities for filtering data collections or DOM-backed lists in real time. Provides controllers for plain lists, tree components, and Miller columns, with built-in highlighting and keyboard navigation. Use to add fast type-ahead filtering to any list, tree, or column-based interface.
|
|
180
|
+
|
|
127
181
|
## Component Selection Guidelines
|
|
128
182
|
|
|
129
183
|
When choosing components for your application, consider these patterns:
|
|
@@ -131,22 +185,30 @@ When choosing components for your application, consider these patterns:
|
|
|
131
185
|
### For Forms and Settings
|
|
132
186
|
- Use `ixfx-checkbox` for binary options
|
|
133
187
|
- Use `ixfx-radio-group` for mutually exclusive choices
|
|
134
|
-
- Use `ixfx-labelled-range-input` for numerical parameters
|
|
188
|
+
- Use `ixfx-labelled-range-input` for numerical parameters with labels
|
|
135
189
|
- Use `ixfx-toggle-switch` for settings with clear on/off states
|
|
190
|
+
- Use `ixfx-range-input` or `ixfx-slider-input` for plain slider-style numeric entry
|
|
191
|
+
- Use `ixfx-editable-number` for values that benefit from both typing and dragging
|
|
136
192
|
- Use `ixfx-panel` or `ixfx-panel-group` to organize related settings
|
|
137
193
|
|
|
138
194
|
### For Data Display
|
|
139
195
|
- Use `ixfx-data-display` for consistent metric presentation
|
|
140
196
|
- Use `ixfx-panel` as containers for grouped information
|
|
141
197
|
- Use `ixfx-tabs` to separate different views of related data
|
|
142
|
-
- Use visualization components (`ixfx-xy-pad`, `ixfx-single-axis`, etc.) for graphical data representation
|
|
198
|
+
- Use visualization components (`ixfx-xy-pad`, `ixfx-single-axis`, `ixfx-plot-multi-axis`, `ixfx-plot-histogram`, etc.) for graphical data representation
|
|
199
|
+
- Use `ixfx-transitory-label` for readouts that animate between values
|
|
143
200
|
|
|
144
201
|
### For Navigation and Selection
|
|
145
202
|
- Use `ixfx-menu` for contextual actions
|
|
146
203
|
- Use `ixfx-menu-bar` for primary application navigation
|
|
147
204
|
- Use `ixfx-tree` for hierarchical data exploration
|
|
148
205
|
- Use `ixfx-breadcrumb-navigation` to show location in hierarchies
|
|
149
|
-
- Use `ixfx-
|
|
206
|
+
- Use `ixfx-crumb-path` for path-style navigation with editable segments
|
|
207
|
+
- Use `ixfx-miller` for deeply nested data with persistent column visibility
|
|
208
|
+
- Use `ixfx-vertical-list` for selectable, keyboard-navigable lists
|
|
209
|
+
- Use `ixfx-grouped-item-lister` for typed, optionally grouped item lists
|
|
210
|
+
- Use `ixfx-ac-text` for assisted single-value entry with predefined options
|
|
211
|
+
- Use `ixfx-ac-token` for multi-value tag/recipient entry with per-token suggestions
|
|
150
212
|
|
|
151
213
|
### For Layout and Structure
|
|
152
214
|
- Use `ixfx-split-layout` for adjustable panes
|
|
@@ -156,9 +218,12 @@ When choosing components for your application, consider these patterns:
|
|
|
156
218
|
|
|
157
219
|
### For Interactive Controls
|
|
158
220
|
- Use `ixfx-xy-pad` for 2D parameter control
|
|
159
|
-
- Use `ixfx-labelled-radial-input` for
|
|
221
|
+
- Use `ixfx-labelled-radial-input` for labelled rotary parameters
|
|
222
|
+
- Use `ixfx-radial-input` for bare rotary dial controls
|
|
160
223
|
- Use `ixfx-range` or `ixfx-range-multi` for value ranges
|
|
224
|
+
- Use `ixfx-swipe` for spring-back gesture controls
|
|
161
225
|
- Use `ixfx-color-picker` for comprehensive color selection
|
|
226
|
+
- Use `ixfx-hex-editor` for inspecting or editing raw binary data
|
|
162
227
|
|
|
163
228
|
This catalog represents the core components available in the ixfx library. Each component is designed to work together through a shared theming system and consistent interaction patterns, allowing you to build cohesive interfaces with minimal effort.
|
|
164
229
|
|
|
@@ -9,12 +9,13 @@ A scrollable vertical list of items with selection, keyboard navigation, and inc
|
|
|
9
9
|
1. [Quick start](#quick-start)
|
|
10
10
|
2. [Item types](#item-types)
|
|
11
11
|
3. [Properties](#properties)
|
|
12
|
-
4. [
|
|
13
|
-
5. [
|
|
14
|
-
6. [
|
|
15
|
-
7. [
|
|
16
|
-
8. [
|
|
17
|
-
9. [CSS
|
|
12
|
+
4. [Custom checkbox](#custom-checkbox)
|
|
13
|
+
5. [Selection](#selection)
|
|
14
|
+
6. [Events](#events)
|
|
15
|
+
7. [Keyboard navigation](#keyboard-navigation)
|
|
16
|
+
8. [Incremental search](#incremental-search)
|
|
17
|
+
9. [CSS variables](#css-variables)
|
|
18
|
+
10. [CSS parts](#css-parts)
|
|
18
19
|
|
|
19
20
|
---
|
|
20
21
|
|
|
@@ -115,6 +116,7 @@ To reflect selection state inside the custom element's shadow DOM, target the `:
|
|
|
115
116
|
| `selectionMode` / `selection-mode` | `'none' \| 'single' \| 'multiple'` | `'single'` | Which items can be selected simultaneously |
|
|
116
117
|
| `interactionMode` / `interaction-mode` | `ListInteractionMode` | `'standard'` | How gestures map to selection changes (see table below) |
|
|
117
118
|
| `filterPredicate` | `((el: Element) => boolean) \| undefined` | `undefined` | External filter; only items returning `true` are shown |
|
|
119
|
+
| `checkboxRenderer` | `((checked: boolean) => unknown) \| CustomCheckboxOptions \| undefined` | `undefined` | Custom content for the checkbox in `checked` mode. See [Custom checkbox](#custom-checkbox) |
|
|
118
120
|
|
|
119
121
|
Switching `interactionMode` always clears the current selection.
|
|
120
122
|
|
|
@@ -125,11 +127,90 @@ Switching `interactionMode` always clears the current selection.
|
|
|
125
127
|
| `'implicit'` | Click always replaces selection. No modifier support. |
|
|
126
128
|
| `'standard'` | Plain click replaces selection; Ctrl/Cmd+click toggles; Shift+click range-selects (multiple mode only); Cmd+A selects all |
|
|
127
129
|
| `'vscode'` | Like `standard` for clicks; Shift+Arrow extends the range from the keyboard anchor |
|
|
128
|
-
| `'checked'` |
|
|
130
|
+
| `'checked'` | Checkbox column on the left of every row; only that column is clickable and it spans the full row height so a tall card can still be toggled with a single click on the left; Space toggles the focused row. See [Custom checkbox](#custom-checkbox) for styling |
|
|
129
131
|
| `'manual'` | No automatic selection; drive state entirely via `select()` / `deselect()` / `clearSelection()` |
|
|
130
132
|
|
|
131
133
|
---
|
|
132
134
|
|
|
135
|
+
## Custom checkbox
|
|
136
|
+
|
|
137
|
+
In `checked` mode every row gets a checkbox column on the left. The column is always a **full-height click target** (the wrapper uses `align-self: stretch`), so a single click anywhere on the left side of a tall card toggles it — you never have to precisely hit a small checkbox. Clicks on the row body (the text or custom card content) do **not** toggle, and the cursor is `default` there so the affordance is clear.
|
|
138
|
+
|
|
139
|
+
By default the column contains a native `<input type="checkbox">`. The visible square is small, but the entire column (~20–24px wide, full row height) is clickable.
|
|
140
|
+
|
|
141
|
+
### Subtle style — `checkboxRenderer`
|
|
142
|
+
|
|
143
|
+
Set `checkboxRenderer` to a function that returns a Lit template, or pass an options object for additional control. The function receives the row's current `checked` state. This is what enables the *subtle* style: render an empty template when unchecked and a translucent mark when checked.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { html } from 'lit';
|
|
147
|
+
import type { VerticalListElement } from '@ixfx/components';
|
|
148
|
+
|
|
149
|
+
const list = document.querySelector<VerticalListElement>('#my-list')!;
|
|
150
|
+
list.interactionMode = 'checked';
|
|
151
|
+
|
|
152
|
+
// Nothing when unchecked; a translucent tick when checked
|
|
153
|
+
list.checkboxRenderer = (checked) => checked
|
|
154
|
+
? html`<svg viewBox="0 0 16 16" width="14" height="14"
|
|
155
|
+
fill="none" stroke="currentColor" stroke-width="2"
|
|
156
|
+
stroke-linecap="round" stroke-linejoin="round"
|
|
157
|
+
style="opacity:0.5">
|
|
158
|
+
<polyline points="3,8 7,12 13,4" />
|
|
159
|
+
</svg>`
|
|
160
|
+
: html``;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Using `CustomCheckboxOptions`
|
|
164
|
+
|
|
165
|
+
Pass an options object to also control the checkbox column's `margin` and `width`:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import type { CustomCheckboxOptions } from '@ixfx/components';
|
|
169
|
+
|
|
170
|
+
list.checkboxRenderer = {
|
|
171
|
+
renderer: (checked) => checked
|
|
172
|
+
? html`<svg viewBox="0 0 16 16" width="14" height="14"
|
|
173
|
+
fill="none" stroke="currentColor" stroke-width="2"
|
|
174
|
+
stroke-linecap="round" stroke-linejoin="round"
|
|
175
|
+
style="opacity:0.5">
|
|
176
|
+
<polyline points="3,8 7,12 13,4" />
|
|
177
|
+
</svg>`
|
|
178
|
+
: html``,
|
|
179
|
+
margin: `0 6px 0 0`, // left margin for the checkbox column (default: `0`)
|
|
180
|
+
width: `24px`, // width of the checkbox column (default: `24px`)
|
|
181
|
+
} satisfies CustomCheckboxOptions;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
| Option | Type | Default | Description |
|
|
185
|
+
|---|---|---|---|
|
|
186
|
+
| `renderer` | `(checked: boolean) => unknown` | — | Required. Returns the Lit template to render inside the checkbox cell |
|
|
187
|
+
| `margin` | `string` | `0` | CSS margin for the checkbox column (e.g. `"0 6px 0 0"`) |
|
|
188
|
+
| `width` | `string` | `24px` | Width of the checkbox column |
|
|
189
|
+
|
|
190
|
+
Because the wrapper still spans the full row height and owns the click handler, the empty unchecked area is just as clickable as the visible tick — even though there is no border, background, or other visual indicator of where the column is.
|
|
191
|
+
|
|
192
|
+
### How it works under the hood
|
|
193
|
+
|
|
194
|
+
In `checked` mode each row has a `<span class="ixfx-list-checkbox" role="checkbox" part="checkbox">` prepended as the first child:
|
|
195
|
+
|
|
196
|
+
- When `checkboxRenderer` is set, your template is rendered into the span (via lit's `render()`) and re-rendered on every state change.
|
|
197
|
+
- When `checkboxRenderer` is **not** set, the span contains a real `<input type="checkbox">` with `pointer-events: none` — the span owns the click so there is exactly one toggle per click.
|
|
198
|
+
- `aria-checked` and `data-checked` on the span reflect the current state.
|
|
199
|
+
- `align-self: stretch` makes the span fill the row's cross-axis, giving you the full-height click target.
|
|
200
|
+
|
|
201
|
+
The element is also exposed as the `checkbox` CSS part, so you can tune the wrapper from outside:
|
|
202
|
+
|
|
203
|
+
```css
|
|
204
|
+
ixfx-vertical-list::part(checkbox) {
|
|
205
|
+
width: 32px; /* a wider column */
|
|
206
|
+
color: var(--accent); /* recolour your rendered content */
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
You can change `checkboxRenderer` at any time (e.g., swap the subtle SVG for an animated check); the wrapper element swaps in place and the renderer is re-applied.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
133
214
|
## Selection
|
|
134
215
|
|
|
135
216
|
### Programmatic API
|
|
@@ -328,6 +409,19 @@ list.filterPredicate = undefined;
|
|
|
328
409
|
| `--item-bg-selected-tickled` | `rgba(21,101,192,1)` | Selected + cursor row background |
|
|
329
410
|
| `--accent` | — | Accent colour (focus ring, search highlight, checkbox) |
|
|
330
411
|
| `--transition` | `0.1s ease-out` | Transition timing for hover/selection changes |
|
|
412
|
+
| `--list-scrollbar-width` | `auto` | Maps to the `scrollbar-width` CSS property on the scroll container (`auto`, `thin`, `none`) |
|
|
413
|
+
| `--list-scrollbar-gutter` | `auto` | Maps to the `scrollbar-gutter` CSS property on the scroll container (`auto`, `stable`, `stable both-edges`) |
|
|
414
|
+
| `--list-scrollbar-color` | `auto` | Maps to the `scrollbar-color` CSS property on the scroll container (`auto` or a two-colour `thumb track` pair) |
|
|
415
|
+
|
|
416
|
+
The three `--list-scrollbar-*` variables are consumed by the shadow-DOM `<ul>` scroll container, so they can be set on any ancestor:
|
|
417
|
+
|
|
418
|
+
```css
|
|
419
|
+
ixfx-vertical-list {
|
|
420
|
+
--list-scrollbar-width: thin;
|
|
421
|
+
--list-scrollbar-gutter: stable;
|
|
422
|
+
--list-scrollbar-color: var(--accent) var(--surface-2);
|
|
423
|
+
}
|
|
424
|
+
```
|
|
331
425
|
|
|
332
426
|
---
|
|
333
427
|
|
|
@@ -337,6 +431,7 @@ list.filterPredicate = undefined;
|
|
|
337
431
|
|---|---|
|
|
338
432
|
| `list` | The `<ul>` scroll container |
|
|
339
433
|
| `search-overlay` | The floating search input overlay |
|
|
434
|
+
| `checkbox` | The per-row checkbox column wrapper in `checked` mode (always present; hosts the native `<input>` or the user-rendered content) |
|
|
340
435
|
|
|
341
436
|
Use `::part()` to style these from outside the component's shadow DOM:
|
|
342
437
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ixfx/components",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.1",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "ISC",
|
|
@@ -33,8 +33,10 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"typecheck": "tsc --noEmit",
|
|
35
35
|
"start": "vite serve",
|
|
36
|
+
"dev": "vite serve",
|
|
36
37
|
"lint": "eslint",
|
|
37
38
|
"lint:fix": "eslint --fix",
|
|
39
|
+
"build:md": "node ./scripts/collate-docs.ts",
|
|
38
40
|
"build": "tsdown",
|
|
39
41
|
"rebuild": "rm -rf dist bundle && pnpm run build",
|
|
40
42
|
"publish-prepare": "pnpm run rebuild && pnpm run typecheck && pnpm run lint",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"button-DxYee1ZH.js","names":["customElement"],"sources":["../src/button/button-styles.ts","../src/button/button.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const buttonStyles = css`\n :host {\n display: inline-flex;\n max-width: 100%;\n vertical-align: middle;\n user-select: none;\n -webkit-user-select: none;\n }\n\n button {\n cursor: pointer;\n appearance: none;\n background: var(--surface-4, #ccc);\n color: var(--surface-3-text, #000);\n border: var(--border, 1px solid #999);\n border-radius: var(--radius-xl, 8px);\n corner-shape: superellipse(1);\n padding: var(--space-m, 8px) var(--space-l, 12px);\n font-family: var(--font-family, inherit);\n font-size: var(--text-m, 14px);\n margin: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5em;\n width: 100%;\n overflow: hidden;\n white-space: nowrap;\n box-shadow: \n inset 1px 1px 1px 1px color-mix(in oklch, var(--surface-4) 30%, transparent), inset -1px -1px 1px 1px color-mix(in oklch, var(--surface-2) 20%, transparent),\n 2px 2px 1px 0px color-mix(in oklch, var(--surface-2) 30%, transparent);\n transition: background var(--transition, 0.2s), border-color var(--transition, 0.2s);\n }\n\n button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n button:hover:not(:disabled) {\n background: color-mix(in oklch, var(--surface-4) 80%, transparent);\n border-color: var(--accent, #666);\n box-shadow: \n inset 1px 1px 1px 1px color-mix(in oklch, var(--surface-5) 70%, transparent), \n inset -1px -1px 1px 1px color-mix(in oklch, var(--surface-2) 60%, transparent),\n 2px 2px 1px 0px color-mix(in oklch, var(--surface-2) 90%, transparent);\n }\n\n button:active:not(:disabled) {\n background: var(--surface-2, #aaa);\n transform: translateY(1px);\n box-shadow: inset 1px 1px 0px 0.5px color-mix(in oklch,var(--surface-1),transparent);\n }\n\n .button-icon {\n display: var(--ixfx-button-hide-icon, inline-flex);\n align-items: center;\n flex-shrink: 0;\n }\n\n .button-label {\n white-space: nowrap;\n display: var(--ixfx-button-hide-label, inline);\n }\n\n :host([hide-label]) .button-label {\n display: none;\n }\n\n :host([hide-icon]) .button-icon {\n display: none;\n }\n`;\n","import { html, LitElement, nothing } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { unsafeHTML } from \"lit/directives/unsafe-html.js\";\nimport { getIcon } from \"../icons/index.js\";\nimport { prominenceStyles } from \"../styles/prominence.js\";\nimport { safeCustomElement as customElement } from \"../util/custom-elements.js\";\nimport { buttonStyles } from \"./button-styles.js\";\n\n@customElement(`ixfx-button`)\nexport class ButtonElement extends LitElement {\n @property({ type: Boolean })\n disabled = false;\n\n @property({ type: String })\n icon?: string;\n\n @property({ type: Boolean, attribute: `hide-icon`, reflect: true })\n hideIcon = false;\n\n @property({ type: Boolean, attribute: `hide-label`, reflect: true })\n hideLabel = false;\n\n protected _renderIcon() {\n if (this.hideIcon || !this.icon)\n return nothing;\n\n if (this.icon.startsWith(`<svg`)) {\n return html`<span class=\"button-icon\">${unsafeHTML(this.icon)}</span>`;\n }\n\n const svg = getIcon(this.icon);\n\n if (!svg)\n return nothing;\n\n return html`<ixfx-icon class=\"button-icon\" name=\"${this.icon}\"></ixfx-icon>`;\n }\n\n protected render() {\n const label = this.hideLabel\n ? nothing\n : html`<span class=\"button-label\"><slot></slot></span>`;\n return html`<button ?disabled=${this.disabled}>${this._renderIcon()}${label}</button>`;\n }\n\n static styles = [\n prominenceStyles,\n buttonStyles,\n ];\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ixfx-button\": ButtonElement;\n }\n}\n"],"mappings":";;;;;;;;AAEA,MAAa,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOxB,IAAM,gBAAN,MAAM,sBAAsB,WAAW;;;EAEjC,KAAA,WAAA;EAMA,KAAA,WAAA;EAGC,KAAA,YAAA;;CAEZ,cAAwB;EACtB,IAAI,KAAK,YAAY,CAAC,KAAK,MACzB,OAAO;EAET,IAAI,KAAK,KAAK,WAAW,MAAM,GAC7B,OAAO,IAAI,6BAA6B,WAAW,KAAK,IAAI,EAAE;EAKhE,IAAI,CAFQ,QAAQ,KAAK,IAElB,GACL,OAAO;EAET,OAAO,IAAI,wCAAwC,KAAK,KAAK;CAC/D;CAEA,SAAmB;EACjB,MAAM,QAAQ,KAAK,YACf,UACA,IAAI;EACR,OAAO,IAAI,qBAAqB,KAAK,SAAS,GAAG,KAAK,YAAY,IAAI,MAAM;CAC9E;;EAEgB,KAAA,SAAA,CACd,kBACA,YACF;;AACF;AAvCG,WAAA,CAAA,SAAS,EAAE,MAAM,QAAQ,CAAC,CAAA,GAAA,cAAA,WAAA,YAAA,KAAA,CAAA;AAG1B,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,cAAA,WAAA,QAAA,KAAA,CAAA;AAGzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,WAAW;CAAa,SAAS;AAAK,CAAC,CAAA,GAAA,cAAA,WAAA,YAAA,KAAA,CAAA;AAGjE,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,WAAW;CAAc,SAAS;AAAK,CAAC,CAAA,GAAA,cAAA,WAAA,aAAA,KAAA,CAAA;AAXpEA,gBAAAA,WAAAA,CAAAA,kBAAc,aAAa,CAAA,GAAA,aAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"defaults-C3b9OWJD.js","names":[],"sources":["../src/icons/defaults.ts"],"sourcesContent":["import { registerIcon } from './registry.js';\n\n/**\n * Chevron pointing down. Used for split-button dropdown toggle.\n * fill=\"currentColor\" so it inherits CSS color.\n */\nexport const ICON_CHEVRON_DOWN = `<svg viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"/></svg>`;\n\n/**\n * Caret pointing right. Used for tree-basic expand indicator as a CSS mask.\n * fill=\"black\" is correct for CSS mask-image use (shape only, color comes from background-color).\n */\nexport const ICON_CARET_RIGHT = `<svg viewBox=\"0 0 24 24\" fill=\"black\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10 7l6 5-6 5V7z\"/></svg>`;\n\n/**\n * Checkmark. Useful for menu-check-item or confirmation states.\n * fill=\"currentColor\" so it inherits CSS color.\n */\nexport const ICON_CHECK = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z\"/></svg>`;\n\n/**\n * Close / X icon. Useful for dialogs, notifications, dismissible elements.\n * fill=\"currentColor\" so it inherits CSS color.\n */\nexport const ICON_CLOSE = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z\"/></svg>`;\n\n/**\n * Chevron pointing right. Used for split-button switcher toggle.\n * fill=\"currentColor\" so it inherits CSS color.\n */\nexport const ICON_CHEVRON_RIGHT = `<svg viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z\"/></svg>`;\n\nexport function registerDefaults(): void {\n registerIcon(`chevron-down`, ICON_CHEVRON_DOWN);\n registerIcon(`chevron-right`, ICON_CHEVRON_RIGHT);\n registerIcon(`caret-right`, ICON_CARET_RIGHT);\n registerIcon(`check`, ICON_CHECK);\n registerIcon(`close`, ICON_CLOSE);\n}\n"],"mappings":";;;;;;AAMA,MAAa,oBAAoB;;;;;AAMjC,MAAa,mBAAmB;;;;;AAMhC,MAAa,aAAa;;;;;AAM1B,MAAa,aAAa;;;;;AAM1B,MAAa,qBAAqB;AAElC,SAAgB,mBAAyB;CACvC,aAAa,gBAAgB,iBAAiB;CAC9C,aAAa,iBAAiB,kBAAkB;CAChD,aAAa,eAAe,gBAAgB;CAC5C,aAAa,SAAS,UAAU;CAChC,aAAa,SAAS,UAAU;AAClC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"icon-DpA9x2Ve.d.ts","names":[],"sources":["../src/icons/icon.ts"],"mappings":";;;;;;;;;;;;;cAoBa,wBAAwB;;EACvB;EACgB;EAChB;EAC0C;EACU;EAQvD;EAKA;YAKU,iBAAM,wBAAA;YAgBN,QAAQ,SAAS;SAWpB,sBAAM;;QAsChB;YACI;IACR,aAAa"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wheel-nudge-DGrNzWuE.d.ts","names":[],"sources":["../src/mixins/wheel-nudge.ts"],"mappings":";;;;;;KASY;;;;;;;;;;;UAYK;EACf,mBAAmB,WAAW,gBAAgB,OAAO;EACrD;;;;;;KAOU,uBAAuB,SAAS,QAAQ"}
|