@ixfx/components 0.14.6 → 0.15.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 (70) hide show
  1. package/bundle/index.d.ts +114 -2
  2. package/bundle/index.js +575 -247
  3. package/bundle/style.css +2 -82
  4. package/dist/ac-text.d.ts +1 -1
  5. package/dist/{ac-token--_vCHXWw.d.ts → ac-token-Cq7lsYf-.d.ts} +16 -2
  6. package/dist/ac-token.d.ts +2 -2
  7. package/dist/ac-token.js +8 -0
  8. package/dist/{button-BYuf72aR.d.ts → button-D1sPDUCM.d.ts} +8 -1
  9. package/dist/{button-B7mgqtxR.js → button-ikByJJPN.js} +29 -8
  10. package/dist/button.d.ts +1 -1
  11. package/dist/button.js +1 -1
  12. package/dist/{colour-picker-BCMU7W7R.js → colour-picker-DwAa6RDh.js} +73 -9
  13. package/dist/colour-picker.d.ts +1 -1
  14. package/dist/colour-picker.js +1 -1
  15. package/dist/crumbs.d.ts +1 -1
  16. package/dist/{data-grid-DmkId8bu.d.ts → data-grid-_BmlhuUu.d.ts} +3 -3
  17. package/dist/data-grid.d.ts +1 -1
  18. package/dist/{detail-list-CmQqeXvp.d.ts → detail-list-DXMnTrYm.d.ts} +3 -3
  19. package/dist/detail-list.d.ts +2 -2
  20. package/dist/{flat-list-BmQBB3ZC.d.ts → flat-list-JfozgW7d.d.ts} +2 -2
  21. package/dist/flat-list.d.ts +1 -1
  22. package/dist/form-BGZK3jZd.d.ts +54 -0
  23. package/dist/form-CY0nibZ5.js +418 -0
  24. package/dist/form.d.ts +1 -13
  25. package/dist/form.js +1 -1
  26. package/dist/{grid-list-BVsUEvgd.d.ts → grid-list-tQwXs895.d.ts} +1 -1
  27. package/dist/grid-list.d.ts +1 -1
  28. package/dist/{grouped-item-lister-DETte0Dh.d.ts → grouped-item-lister-Bhyo-dkH.d.ts} +1 -1
  29. package/dist/grouped-item-lister.d.ts +1 -1
  30. package/dist/{icon-CDGprSdE.d.ts → icon-DR_3rSyZ.d.ts} +1 -1
  31. package/dist/icons.d.ts +2 -2
  32. package/dist/incr-search.d.ts +1 -1
  33. package/dist/{index-CoqtMSd9.d.ts → index-BUB5SICW.d.ts} +1 -1
  34. package/dist/{index-tq6f8007.d.ts → index-Bz3loIAf.d.ts} +4 -1
  35. package/dist/{index-UvnoZUAo.d.ts → index-CJ5fshD3.d.ts} +1 -1
  36. package/dist/{index-BvoXLnBf.d.ts → index-ChdhB7RK.d.ts} +1 -1
  37. package/dist/{index-DxyyVqzA.d.ts → index-cnaRPQPp.d.ts} +2 -2
  38. package/dist/{index-CYndvTWH.d.ts → index-garxxqgV.d.ts} +1 -1
  39. package/dist/{index-DuhzWWTr.d.ts → index-wUYiXH8c.d.ts} +1 -1
  40. package/dist/index.d.ts +69 -22
  41. package/dist/index.js +138 -8
  42. package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +1 -1
  43. package/dist/labelled-radial-input.d.ts +2 -2
  44. package/dist/labelled-range-input.d.ts +1 -1
  45. package/dist/labelled-slider-input.d.ts +1 -1
  46. package/dist/list-group-header.d.ts +1 -1
  47. package/dist/{menu-item-bF6narIz.d.ts → menu-item-B0zqw2xp.d.ts} +1 -1
  48. package/dist/menu.d.ts +2 -2
  49. package/dist/miller.d.ts +1 -1
  50. package/dist/panel.d.ts +1 -1
  51. package/dist/{split-layout-JK3xWVX2.d.ts → split-layout-C5SYbDrq.d.ts} +1 -1
  52. package/dist/split-layout.d.ts +1 -1
  53. package/dist/style.css +2 -82
  54. package/dist/tab-list-R_bGbDvO.js +766 -0
  55. package/dist/{tab-list-CexFFg6O.d.ts → tab-list-wC0xNz1b.d.ts} +2 -1
  56. package/dist/tabs.d.ts +1 -1
  57. package/dist/tabs.js +1 -673
  58. package/dist/{titlebar-Tqu-VvMU.d.ts → titlebar-B5zSTZnF.d.ts} +1 -1
  59. package/dist/titlebar.d.ts +1 -1
  60. package/dist/tree.d.ts +1 -1
  61. package/dist/{vertical-list-ztrdFvyT.d.ts → vertical-list-BYyb2n4x.d.ts} +1 -1
  62. package/dist/vertical-list.d.ts +1 -1
  63. package/docs-user/ac-token.md +13 -0
  64. package/docs-user/button.md +1 -0
  65. package/docs-user/colour-picker.md +19 -0
  66. package/docs-user/form.md +97 -4
  67. package/docs-user/index.json +2 -2
  68. package/docs-user/tabs.md +30 -0
  69. package/package.json +2 -1
  70. package/dist/form-CsXJX15p.js +0 -62
package/dist/tree.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { A as TreeTickleEvent, D as TreeSelectDetail, M as TreeDataModelOptions, O as TreeSelectEvent, T as TreeRevealMode, _ as TreeDropTarget, b as TreeItemClickDetail, c as TreeDragContext, d as TreeDragStartEvent, g as TreeDropPosition, h as TreeDropEvent, i as TreeCollapseEvent, j as TreeDataModel, k as TreeTickleDetail, l as TreeDragEndEvent, m as TreeDropDetail, n as TreeActivateEvent, o as TreeComponentEventMap, p as TreeDropContext, r as TreeCollapseDetail, s as TreeDndMode, t as TreeActivateDetail, u as TreeDragOverEvent, v as TreeExpandDetail, w as TreeNavigateOptions, x as TreeItemClickEvent, y as TreeExpandEvent } from "./tree-component-CDsYTBDi.js";
2
2
  import { a as TreeItem, n as TreeExclusivity, o as TreeNode, r as TreeFilterPredicate, s as TreeSelectionFilter, t as LoadChildrenCallback } from "./types-Jky6oNAp.js";
3
- import { a as TreeDropVisibleRow, c as TreeListElement, d as TreeBaseElementEventMap, f as TreeVirtualize, i as TreeDropRowInfo, l as TreeCheckboxState, n as TreeDropControllerConfig, o as TreeController, p as TreeConfig, r as TreeDropResolvedDrag, s as TreeControllerOptions, t as TreeDropController, u as TreeBaseElement } from "./index-DuhzWWTr.js";
3
+ import { a as TreeDropVisibleRow, c as TreeListElement, d as TreeBaseElementEventMap, f as TreeVirtualize, i as TreeDropRowInfo, l as TreeCheckboxState, n as TreeDropControllerConfig, o as TreeController, p as TreeConfig, r as TreeDropResolvedDrag, s as TreeControllerOptions, t as TreeDropController, u as TreeBaseElement } from "./index-wUYiXH8c.js";
4
4
  export { type LoadChildrenCallback, type TreeActivateDetail, type TreeActivateEvent, TreeBaseElement, TreeBaseElementEventMap, type TreeCheckboxState, type TreeCollapseDetail, type TreeCollapseEvent, type TreeComponentEventMap, TreeConfig, TreeController, TreeControllerOptions, TreeDataModel, type TreeDataModelOptions, type TreeDndMode, type TreeDragContext, type TreeDragEndEvent, type TreeDragOverEvent, type TreeDragStartEvent, type TreeDropContext, TreeDropController, TreeDropControllerConfig, type TreeDropDetail, type TreeDropEvent, type TreeDropPosition, TreeDropResolvedDrag, TreeDropRowInfo, type TreeDropTarget, TreeDropVisibleRow, type TreeExclusivity, type TreeExpandDetail, type TreeExpandEvent, type TreeFilterPredicate, type TreeItem, type TreeItemClickDetail, type TreeItemClickEvent, TreeListElement, type TreeNavigateOptions, type TreeNode, type TreeRevealMode, type TreeSelectDetail, type TreeSelectEvent, type TreeSelectionFilter, type TreeTickleDetail, type TreeTickleEvent, TreeVirtualize };
@@ -1,6 +1,6 @@
1
1
  import { B as ListNativeDragStartEvent, I as ListDragPreviewRenderer, L as ListDragPreviewSourceResolver } from "./tree-component-CDsYTBDi.js";
2
2
  import { n as ListViewportChangeEvent } from "./list-viewport-types-BRZsLcvp.js";
3
- import { S as ListSearchController } from "./index-BvoXLnBf.js";
3
+ import { S as ListSearchController } from "./index-ChdhB7RK.js";
4
4
  import { i as ListInteractionMode, l as ListSelectionMode, r as ListElementEventMap } from "./list-selection-types-PjKdhF-a.js";
5
5
  import "./editable-label-rzXkjf1H.js";
6
6
  import { LitElement, PropertyValues } from "lit";
@@ -1,5 +1,5 @@
1
1
  import { B as ListNativeDragStartEvent, F as ListDragPreviewContext, I as ListDragPreviewRenderer, N as ListDragEntry, P as ListDragPreview, R as ListDragSource, z as ListNativeDragStartDetail } from "./tree-component-CDsYTBDi.js";
2
2
  import { n as ListViewportChangeEvent, t as ListViewportChangeDetail } from "./list-viewport-types-BRZsLcvp.js";
3
3
  import { a as ListItemClickDetail, c as ListSelectEvent, d as ListTickleEvent, i as ListInteractionMode, l as ListSelectionMode, n as ListActivateEvent, o as ListItemClickEvent, r as ListElementEventMap, s as ListSelectDetail, t as ListActivateDetail, u as ListTickleDetail } from "./list-selection-types-PjKdhF-a.js";
4
- import { a as ListContextMenuDetail, c as ListDragLeaveEvent, d as ListDropEvent, f as ListEditCancelDetail, h as ListEditCommitEvent, i as VerticalListElementEventMap, l as ListDragOverEvent, m as ListEditCommitDetail, n as CustomCheckboxOptions, o as ListContextMenuEvent, p as ListEditCancelEvent, r as VerticalListElement, s as ListDragLeaveDetail, t as AddItemOptions, u as ListDropDetail } from "./vertical-list-ztrdFvyT.js";
4
+ import { a as ListContextMenuDetail, c as ListDragLeaveEvent, d as ListDropEvent, f as ListEditCancelDetail, h as ListEditCommitEvent, i as VerticalListElementEventMap, l as ListDragOverEvent, m as ListEditCommitDetail, n as CustomCheckboxOptions, o as ListContextMenuEvent, p as ListEditCancelEvent, r as VerticalListElement, s as ListDragLeaveDetail, t as AddItemOptions, u as ListDropDetail } from "./vertical-list-BYyb2n4x.js";
5
5
  export { AddItemOptions, CustomCheckboxOptions, type ListActivateDetail, type ListActivateEvent, type ListContextMenuDetail, type ListContextMenuEvent, type ListDragEntry, type ListDragLeaveDetail, type ListDragLeaveEvent, type ListDragOverEvent, type ListDragPreview, type ListDragPreviewContext, type ListDragPreviewRenderer, type ListDragSource, type ListDropDetail, type ListDropEvent, type ListEditCancelDetail, type ListEditCancelEvent, type ListEditCommitDetail, type ListEditCommitEvent, type ListElementEventMap, type ListInteractionMode, type ListItemClickDetail, type ListItemClickEvent, type ListNativeDragStartDetail, type ListNativeDragStartEvent, type ListSelectDetail, type ListSelectEvent, type ListSelectionMode, type ListTickleDetail, type ListTickleEvent, type ListViewportChangeDetail, type ListViewportChangeEvent, VerticalListElement, VerticalListElementEventMap };
@@ -36,6 +36,7 @@ el.addEventListener('ac-token-change', e => {
36
36
  |-----------------------|-----------------------------------|-------------|---------------------------------------------------------------------|
37
37
  | `completionProvider` | `AcTokenCompletionProvider \| undefined` | `undefined` | Async or sync function returning suggestions for the current draft token. Takes precedence over `data-tokens`. |
38
38
  | `tokenizer` | `AcTokenizerFn \| undefined` | `undefined` | Custom function to split text into tokens. Default splits on space, comma, semicolon. |
39
+ | `tokenStyle` | `AcTokenStyleFn \| undefined` | `undefined` | Called per committed chip with the `AcTokenItem`; return `{ background?, color?, borderColor? }` (any CSS colour) or undefined for the default look. Not applied while the chip is selected. |
39
40
  | `data-tokens` | `string` (attribute only) | `''` | Space/comma/semicolon-separated list of static suggestion labels. Used when no `completionProvider` is set. |
40
41
  | `placeholder` | `string` | `''` | Placeholder shown when no tokens or draft text are present. |
41
42
  | `strict` | `boolean` | `false` | When true, only suggestions from the popup can be committed. Free-form typed text is ignored. |
@@ -138,6 +139,18 @@ The tokenizer is also used when pasting text to bulk-commit multiple tokens at o
138
139
 
139
140
  ---
140
141
 
142
+ ### Per-token styling
143
+
144
+ ```js
145
+ el.tokenStyle = item => item.label.startsWith('#')
146
+ ? { background: 'teal', color: 'white' }
147
+ : undefined;
148
+ ```
149
+
150
+ Re-assign `tokenStyle` to re-evaluate chips if the rules change.
151
+
152
+ ---
153
+
141
154
  ### `AcTokenItem` shape
142
155
 
143
156
  ```ts
@@ -27,6 +27,7 @@ Three related button components:
27
27
  | `icon` | string | — | Registered icon name **or** raw `<svg>` string |
28
28
  | `hide-icon` | boolean | `false` | Hides the icon even when `icon` is set |
29
29
  | `hide-label` | boolean | `false` | Hides the text label (useful for icon-only buttons)|
30
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | Inside a `<form>` (including `ixfx-form`), `submit` submits it (after validation) and `reset` resets it. The button is form-associated, so this works even though its inner `<button>` is in a shadow root |
30
31
  | `subtle` | boolean | `false` | Renders the background at 40% opacity, raised to 70% on hover/focus |
31
32
 
32
33
  ### Slots
@@ -200,6 +200,25 @@ Picker sizes (width × height):
200
200
 
201
201
  Set the committed colour from any ixfx-compatible format. The swatch updates immediately. The inner picker reflects the new colour the next time the popover opens.
202
202
 
203
+ #### `close()`
204
+
205
+ Close the popover. In confirm mode, uncommitted changes are reverted.
206
+
207
+ ### Slots
208
+
209
+ | Slot | Description |
210
+ |------|-------------|
211
+ | `action` | Optional button(s) at the bottom-left of the popup, inline with the confirm button. Wire up its click handler yourself and call `close()` to dismiss. |
212
+
213
+ ```html
214
+ <ixfx-colour-picker-popup mode="confirm" id="bg">
215
+ <button slot="action" id="none">No colour</button>
216
+ </ixfx-colour-picker-popup>
217
+ <script>
218
+ none.addEventListener('click', () => { /* clear your value */ bg.close(); });
219
+ </script>
220
+ ```
221
+
203
222
  ### Limitations
204
223
 
205
224
  The popover is positioned relative to the swatch using CSS anchor positioning (`anchor-name`/`position-anchor`). Chrome does not resolve anchor positioning for elements inside an SVG `<foreignObject>` — if you embed this component there, the popover will render unanchored at the top-left of the viewport instead of next to the swatch. Workaround: keep the swatch as a plain SVG element and render this component (or just the picker) in a fixed-positioned HTML overlay positioned via `getBoundingClientRect()` on your own swatch element.
package/docs-user/form.md CHANGED
@@ -6,25 +6,74 @@ Primitives for typical form layout with responsive label positioning and data bi
6
6
 
7
7
  ## `ixfx-form`
8
8
 
9
- Main container for form sections. Provides `getValues()` and `setValues()` methods for data binding.
9
+ Main container for form sections. It wraps its content in a native `<form>`, so submit, Enter-to-submit, validation, `FormData` and `form.elements` work as they do for any form. It also provides `getValues()` and `setValues()` for data binding.
10
10
 
11
11
  ```html
12
12
  <ixfx-form>
13
13
  <ixfx-form-group label="Section A">
14
14
  <p>Preamble text for section A...</p>
15
15
  <ixfx-form-part label="Some text">
16
- <input type="text" name="input1" />
16
+ <input type="text" name="input1" required />
17
17
  </ixfx-form-part>
18
18
  <ixfx-form-part label="Some other text">
19
19
  <input type="text" name="input2" />
20
20
  </ixfx-form-part>
21
21
  </ixfx-form-group>
22
+ <ixfx-button type="submit">Save</ixfx-button>
22
23
  </ixfx-form>
23
24
  ```
24
25
 
26
+ ```js
27
+ form.addEventListener('submit', (e) => {
28
+ console.log(form.getValues({ typed: true }));
29
+ });
30
+ ```
31
+
25
32
  ### Properties / attributes
26
33
 
27
- `ixfx-form` has no attributes — it is a pure layout and data-binding container.
34
+ | Attribute / Property | Type | Default | Description |
35
+ |---------------------|------|---------|-------------|
36
+ | `label-align` / `labelAlign` | `'left' \| 'right'` | `'right'` | Label justification in the wide layout. Applies to all groups unless a group overrides it |
37
+ | `novalidate` / `noValidate` | boolean | `false` | Skip validation on submit, like `<form novalidate>`. `reportValidity()` still validates |
38
+
39
+ ### Methods and properties
40
+
41
+ | Member | Returns | Description |
42
+ |--------|---------|-------------|
43
+ | `getValues(options?)` | `Record<string, unknown>` | Collects values from all named controls. Checkboxes give booleans; all other values are strings, including `type=number`. With `{ typed: true }`, `number` and `range` inputs give numbers (`null` when empty) |
44
+ | `setValues(data)` | `void` | Applies values to named inputs. Keys absent from `data` are left untouched. |
45
+ | `requestSubmit()` | `void` | Validates (unless `novalidate`), then fires `submit` |
46
+ | `checkValidity()` | `boolean` | True if every native control is valid |
47
+ | `reportValidity()` | `boolean` | As `checkValidity()`, and shows the browser's message on the first invalid control |
48
+ | `reset()` | `void` | Resets native controls |
49
+ | `form` | `HTMLFormElement` | The native form holding the content |
50
+ | `elements` | `HTMLFormControlsCollection` | The native form's controls |
51
+
52
+ ### Submitting
53
+
54
+ - `submit` is the standard event: it bubbles, and carries no navigation (`ixfx-form` always cancels the default action, because there is no `action`).
55
+ - Submit with a native `<button type="submit">`, `<ixfx-button type="submit">`, `requestSubmit()`, or Enter in a text field.
56
+ - Validation uses the browser's: `required`, `pattern`, `min`, `max`, `type=email` and so on.
57
+
58
+ ### What is and isn't a native control
59
+
60
+ Native `<input>`, `<select>` and `<textarea>` take part in validation, `FormData`, `elements` and `reset()`. Components with their own shadow root (ranges, colour pickers and so on) do not: the browser can't see inside them. `getValues()` and `setValues()` still handle them through their `.value`, but validation and `reset()` skip them.
61
+
62
+ Don't nest an `ixfx-form` inside another `<form>`: nested forms are invalid HTML.
63
+
64
+ ### Styling inside a shadow root
65
+
66
+ The form components are light DOM, so a global stylesheet can't reach them inside another component's shadow root. They add their own layout styles to the document, or to the shadow root they are in, when they connect. Consumers need no workaround. The styles are placed first, so your own CSS wins ties. The package also exports `@ixfx/components/style.css` for the global theme variables.
67
+
68
+ ### Value collection rules
69
+
70
+ - Native `<input type=radio>` sharing a `name` → value of the checked one
71
+ - Native/checked-bearing checkbox → boolean
72
+ - `<select multiple>` → array of selected values
73
+ - Everything else with a `.value` property → used as-is
74
+
75
+ ---------------------|------|---------|-------------|
76
+ | `label-align` / `labelAlign` | `'left' \| 'right'` | `'right'` | Label justification in the wide layout. Applies to all groups unless a group overrides it |
28
77
 
29
78
  ### Methods
30
79
 
@@ -62,6 +111,7 @@ A section within the form, backed by a `<fieldset>`/`<legend>` for proper access
62
111
  | Attribute / Property | Type | Default | Description |
63
112
  |---------------------|--------|---------|-------------|
64
113
  | `label` | string | `''` | Section heading text |
114
+ | `label-align` / `labelAlign` | `'left' \| 'right'` | inherited | Overrides the form's label justification for this group |
65
115
 
66
116
  ---
67
117
 
@@ -89,16 +139,59 @@ An `id` is generated for the control if it doesn't already have one.
89
139
 
90
140
  ---
91
141
 
142
+ ## `ixfx-form-row`
143
+
144
+ Lets two or more `ixfx-form-part` elements share a row. Must be a direct child of `ixfx-form-group`.
145
+
146
+ ```html
147
+ <ixfx-form-group label="Address">
148
+ <ixfx-form-part label="Street"><input name="street" /></ixfx-form-part>
149
+ <ixfx-form-row>
150
+ <ixfx-form-part label="City"><input name="city" /></ixfx-form-part>
151
+ <ixfx-form-part label="Postcode"><input name="postcode" /></ixfx-form-part>
152
+ </ixfx-form-row>
153
+ </ixfx-form-group>
154
+ ```
155
+
156
+ The group counts the pairs in its widest row (`--ixfx-form-pairs`) and builds a grid of `label | input` columns that many times. Full-width parts span their input across the remaining columns, and all rows (including separate shared-row blocks) line up via subgrid.
157
+
158
+ - **Wide:** `label1 input1 label2 input2`
159
+ - **Narrow:** labels on one line, inputs below, one column per pair
160
+
161
+ ---
162
+
163
+ ## `ixfx-form-options`
164
+
165
+ Groups option parts (typically radio buttons sharing a `name`) under one label. Must be a direct child of `ixfx-form-group`.
166
+
167
+ ```html
168
+ <ixfx-form-options label="Contact method">
169
+ <ixfx-form-part label="Email"><input type="radio" name="contact" value="email" /></ixfx-form-part>
170
+ <ixfx-form-part label="Phone"><input type="radio" name="contact" value="phone" /></ixfx-form-part>
171
+ </ixfx-form-options>
172
+ ```
173
+
174
+ | Attribute / Property | Type | Default | Description |
175
+ |---------------------|------|---------|-------------|
176
+ | `label` | string | `''` | Group label, shown in the label column (wide) or above the options (narrow) |
177
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Horizontal: each option is the control with its label centred beneath, flowing and wrapping as space runs out. Vertical: control then label, one option per line |
178
+
179
+ Gap between horizontal options: `--ixfx-form-option-gap` (default `var(--space-l)`). The element has `role="group"` labelled by its `label`.
180
+
181
+ ---
182
+
92
183
  ## Responsive behaviour
93
184
 
94
185
  The layout switches between two modes based on available width (via CSS container queries):
95
186
 
96
187
  **Narrow (< 30rem):** Label appears above the input, single-column stacked layout.
97
188
 
98
- **Wide (≥ 30rem):** Label appears to the left of the input, two-column grid. All labels in a group share the width of the widest label (right-justified).
189
+ **Wide (≥ 30rem):** Label appears to the left of the input, two-column grid. All labels in a group share the width of the widest label (right-justified by default; set `label-align="left"` on the form or a group to left-justify).
99
190
 
100
191
  Resize the container to see the transition.
101
192
 
193
+ In the wide layout the gap between a label and its input is `--ixfx-form-label-gap` (default `var(--space-s)`), and the gap between pairs in a shared row is `--ixfx-form-pair-gap` (default `var(--space-l)`).
194
+
102
195
  ---
103
196
 
104
197
  ## TypeScript types
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
- "version": "0.14.6",
4
- "generated": "2026-10-16T18:49:43.481Z",
3
+ "version": "0.15.0",
4
+ "generated": "2026-10-18T17:51:43.273Z",
5
5
  "components": [
6
6
  {
7
7
  "name": "ac-text",
package/docs-user/tabs.md CHANGED
@@ -298,6 +298,36 @@ Example — thick text underline instead of a border indicator:
298
298
  </ixfx-tab-list>
299
299
  ```
300
300
 
301
+ ### Sliding indicator and view transitions
302
+
303
+ Each selected item draws an indicator bar (the item's `::after`). It sits on the bottom edge (top with `flip`; right/left for vertical orientations). It and its animation are **off by default** and are controlled with CSS variables, set on `ixfx-tab-list` (or any ancestor):
304
+
305
+ | Property | Default | Description |
306
+ |----------|---------|-------------|
307
+ | `--ixfx-tab-indicator-thickness` | `0px` | Indicator thickness. `0px` turns the indicator off |
308
+ | `--ixfx-tab-indicator-colour` | `var(--accent)` | Indicator colour |
309
+ | `--ixfx-tab-indicator-radius` | `0px` | Corner radius |
310
+ | `--ixfx-tab-indicator-inset` | `0px` | Inset from both ends of the tab edge |
311
+ | `--ixfx-tab-indicator-offset` | `0px` | Distance from the tab edge (negative values move it outside) |
312
+ | `--ixfx-tab-transition-duration` | *(unset)* | Animate selection changes with the View Transitions API. Unset or `0ms` turns animation off |
313
+ | `--ixfx-tab-transition-easing` | `cubic-bezier(0.3333, 0, 0.6667, 1)` | Easing for the indicator's movement. The default is a smoothstep curve (3t² − 2t³) |
314
+
315
+ ```html
316
+ <ixfx-tab-list style="
317
+ --ixfx-selected-tab-border-width: 0;
318
+ --ixfx-tab-indicator-thickness: 3px;
319
+ --ixfx-tab-transition-duration: 250ms;
320
+ ">
321
+ ```
322
+
323
+ Notes:
324
+
325
+ - The indicator is drawn in addition to the existing border indicator; set `--ixfx-selected-tab-border-width: 0` to use only the sliding one.
326
+ - Animation applies to user-initiated changes (click, arrow keys). The click selection and the `change` event run inside the same transition, so a panel swapped in a `change` listener animates with the header (a page-wide cross-fade). Programmatic `selectElement()` stays synchronous and instant.
327
+ - Under a view transition the browser applies the `selected` change on the next frame rather than synchronously, so code reading `getSelectedElement()` immediately after a click may see the previous tab; listen for `change` instead.
328
+ - Browsers without `document.startViewTransition`, and users with `prefers-reduced-motion: reduce`, get an instant change.
329
+ - While a transition runs the page is rendered from snapshots, so it is not interactive for the duration. Keep it short.
330
+
301
331
  ### CSS parts
302
332
 
303
333
  | Part | Description |
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
3
  "type": "module",
4
- "version": "0.14.6",
4
+ "version": "0.15.0",
5
5
  "packageManager": "pnpm@10.0.0",
6
6
  "description": "Web components for ixfx — Lit-based UI toolkit",
7
7
  "author": "Clint Heyer",
8
8
  "license": "MIT",
9
9
  "keywords": ["web-components", "lit", "ixfx", "ui"],
10
10
  "exports": {
11
+ "./style.css": "./dist/style.css",
11
12
  ".": {
12
13
  "types": "./dist/index.d.ts",
13
14
  "import": "./dist/index.js"
@@ -1,62 +0,0 @@
1
- import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
2
- import { LitElement } from "lit";
3
- //#region src/form/form-values.ts
4
- function collectFormValues(root) {
5
- const values = {};
6
- root.querySelectorAll(`[name]`).forEach((el) => {
7
- const name = el.getAttribute(`name`);
8
- if (!name) return;
9
- if (el instanceof HTMLInputElement) {
10
- if (el.type === `radio`) {
11
- if (el.checked) values[name] = el.value;
12
- } else if (el.type === `checkbox`) values[name] = el.checked;
13
- else values[name] = el.value;
14
- } else if (el instanceof HTMLSelectElement) {
15
- if (el.multiple) values[name] = Array.from(el.selectedOptions).map((opt) => opt.value);
16
- else values[name] = el.value;
17
- } else if (el instanceof HTMLTextAreaElement) values[name] = el.value;
18
- else if (`value` in el) values[name] = el.value;
19
- });
20
- return values;
21
- }
22
- function applyFormValues(root, data) {
23
- root.querySelectorAll(`[name]`).forEach((el) => {
24
- const name = el.getAttribute(`name`);
25
- if (!name || !(name in data)) return;
26
- const value = data[name];
27
- if (el instanceof HTMLInputElement) {
28
- if (el.type === `radio`) el.checked = el.value === value;
29
- else if (el.type === `checkbox`) el.checked = Boolean(value);
30
- else el.value = String(value ?? ``);
31
- el.dispatchEvent(new Event(`input`, { bubbles: true }));
32
- el.dispatchEvent(new Event(`change`, { bubbles: true }));
33
- } else if (el instanceof HTMLSelectElement) {
34
- if (el.multiple && Array.isArray(value)) Array.from(el.options).forEach((opt) => {
35
- opt.selected = value.includes(opt.value);
36
- });
37
- else el.value = String(value ?? ``);
38
- el.dispatchEvent(new Event(`input`, { bubbles: true }));
39
- el.dispatchEvent(new Event(`change`, { bubbles: true }));
40
- } else if (el instanceof HTMLTextAreaElement) {
41
- el.value = String(value ?? ``);
42
- el.dispatchEvent(new Event(`input`, { bubbles: true }));
43
- el.dispatchEvent(new Event(`change`, { bubbles: true }));
44
- } else if (`value` in el) el.value = value;
45
- });
46
- }
47
- //#endregion
48
- //#region src/form/form.ts
49
- let FormElement = class FormElement extends LitElement {
50
- createRenderRoot() {
51
- return this;
52
- }
53
- getValues() {
54
- return collectFormValues(this);
55
- }
56
- setValues(data) {
57
- applyFormValues(this, data);
58
- }
59
- };
60
- FormElement = __decorate([safeCustomElement(`ixfx-form`)], FormElement);
61
- //#endregion
62
- export { FormElement as t };