forty-cdk 0.8.0 → 0.10.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 (57) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +104 -283
  3. package/combobox/README.md +88 -14
  4. package/context-menu/README.md +20 -19
  5. package/dropdown-menu/README.md +20 -19
  6. package/fesm2022/forty-cdk-calendar.mjs +11 -1
  7. package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
  8. package/fesm2022/forty-cdk-combobox.mjs +176 -8
  9. package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
  10. package/fesm2022/forty-cdk-context-menu.mjs +11 -2
  11. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
  12. package/fesm2022/forty-cdk-core.mjs +197 -259
  13. package/fesm2022/forty-cdk-core.mjs.map +1 -1
  14. package/fesm2022/forty-cdk-drag-drop.mjs +6 -1
  15. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
  16. package/fesm2022/forty-cdk-drawer.mjs +14 -3
  17. package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
  18. package/fesm2022/forty-cdk-dropdown-menu.mjs +11 -2
  19. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
  20. package/fesm2022/forty-cdk-file-upload.mjs +7 -1
  21. package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
  22. package/fesm2022/forty-cdk-internationalized-date.mjs +22 -2
  23. package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
  24. package/fesm2022/forty-cdk-menu.mjs +17 -2
  25. package/fesm2022/forty-cdk-menu.mjs.map +1 -1
  26. package/fesm2022/forty-cdk-menubar.mjs +7 -0
  27. package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
  28. package/fesm2022/forty-cdk-pane-resizer.mjs +64 -21
  29. package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -1
  30. package/fesm2022/forty-cdk-popover.mjs +37 -7
  31. package/fesm2022/forty-cdk-popover.mjs.map +1 -1
  32. package/fesm2022/forty-cdk-progress.mjs +3 -2
  33. package/fesm2022/forty-cdk-progress.mjs.map +1 -1
  34. package/fesm2022/forty-cdk-table.mjs +1553 -700
  35. package/fesm2022/forty-cdk-table.mjs.map +1 -1
  36. package/fesm2022/forty-cdk-virtualization.mjs +53 -22
  37. package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
  38. package/internationalized-date/README.md +23 -23
  39. package/menu/README.md +15 -0
  40. package/package.json +1 -1
  41. package/popover/README.md +1 -1
  42. package/table/README.md +234 -35
  43. package/types/forty-cdk-calendar.d.ts +1 -0
  44. package/types/forty-cdk-combobox.d.ts +114 -6
  45. package/types/forty-cdk-context-menu.d.ts +11 -3
  46. package/types/forty-cdk-core.d.ts +114 -81
  47. package/types/forty-cdk-drag-drop.d.ts +22 -2
  48. package/types/forty-cdk-drawer.d.ts +10 -2
  49. package/types/forty-cdk-dropdown-menu.d.ts +11 -3
  50. package/types/forty-cdk-file-upload.d.ts +4 -1
  51. package/types/forty-cdk-internationalized-date.d.ts +2 -0
  52. package/types/forty-cdk-menu.d.ts +16 -3
  53. package/types/forty-cdk-menubar.d.ts +8 -1
  54. package/types/forty-cdk-pane-resizer.d.ts +2 -1
  55. package/types/forty-cdk-popover.d.ts +32 -5
  56. package/types/forty-cdk-progress.d.ts +2 -1
  57. package/types/forty-cdk-table.d.ts +627 -197
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 tutkli
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tutkli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -36,289 +36,110 @@ Optional — install only if you use the matching entry point / primitives:
36
36
 
37
37
  ## Primitives
38
38
 
39
- Each primitive lives in its own folder under `projects/forty-cdk/` (e.g. [`accordion/`](accordion), [`dialog/`](dialog)) with its own `README.md` and a minimal styleless usage example.
40
-
41
- Every primitive ships as its own **secondary entry point** — import `ForDialog` from `forty-cdk/dialog`, `ForAccordion` from `forty-cdk/accordion`, and so on — backed by the shared `forty-cdk/core` entry point. The `@internationalized/date` adapters live in a dedicated `forty-cdk/internationalized-date` entry point so that optional peer stays truly optional. The main `forty-cdk` barrel is **intentionally empty** (it exports no symbols): always import from the specific `forty-cdk/<primitive>` entry point. Standalone directives plus `"sideEffects": false` mean your bundle only ever includes the primitives you import.
42
-
43
- ## Directive → host element matrix
44
-
45
- Quick reference for "which HTML element should I put this directive on?". Recommendations are derived from each primitive's WAI-ARIA pattern (e.g. focusable triggers as `<button type="button">`, the combobox input as a real `<input>` so caret/selection work) and from each primitive's README usage example. `any element` means the directive is element-agnostic — pick the tag that matches your semantics.
46
-
47
- Selectors marked with `(element)` use an element selector instead of an attribute selector; everything else is `[attribute]`. Form-control hosts (`forSwitch`, `forCheckbox`, `forRadio`, `forToggle`) deliberately render as `<button type="button">` — the directive forces `type="button"` and provides `role="switch"` / `"checkbox"` / `"radio"` / `aria-pressed` so the consumer keeps full keyboard, focus, and form-state behaviour without an `<input>` whose chrome can't be styled.
48
-
49
- ### Accordion
50
-
51
- | Selector | Host |
52
- | ----------------------- | ---------------------------------------------- |
53
- | `[forAccordion]` | `<div>` |
54
- | `[forAccordionItem]` | `<div>` |
55
- | `[forAccordionTrigger]` | `<button>` (wrapped in `<h2>`–`<h6>`, per APG) |
56
- | `[forAccordionContent]` | `<section>` |
57
-
58
- ### Aspect Ratio
59
-
60
- | Selector | Host |
61
- | ------------------ | ------- |
62
- | `[forAspectRatio]` | `<div>` |
63
-
64
- ### Avatar
65
-
66
- | Selector | Host |
67
- | --------------------- | ------------------------------ |
68
- | `[forAvatar]` | `<span>` |
69
- | `img[forAvatarImage]` | `<img>` (selector enforces it) |
70
- | `[forAvatarFallback]` | `<span>` |
71
-
72
- ### Checkbox
73
-
74
- | Selector | Host |
75
- | ------------------------ | --------------------------------- |
76
- | `[forCheckbox]` | `<button type="button">` (forced) |
77
- | `[forCheckboxIndicator]` | `<span>` |
78
-
79
- ### Combobox
80
-
81
- | Selector | Host |
82
- | ------------------------- | ------------------------------------------------------------------- |
83
- | `[forCombobox]` | `<div>` |
84
- | `[forComboboxInput]` | `<input>` (a real text field — `role="combobox"` + caret semantics) |
85
- | `[forComboboxContent]` | `<div>` |
86
- | `[forComboboxOption]` | `<div>` |
87
- | `[forComboboxIndicator]` | `<span>` |
88
- | `[forComboboxEmpty]` | `<div>` |
89
- | `[forComboboxStatus]` | `<div>` |
90
- | `[forComboboxClear]` | `<button>` |
91
- | `[forComboboxChips]` | `<div>` |
92
- | `[forComboboxChip]` | `<span>` |
93
- | `[forComboboxChipRemove]` | `<button>` |
94
- | `[forComboboxGroup]` | `<div>` |
95
- | `[forComboboxGroupLabel]` | `<div>` |
96
- | `[forComboboxSeparator]` | `<div>` |
97
-
98
- ### Context Menu
99
-
100
- | Selector | Host |
101
- | ------------------------------------------------------------ | ---------------- |
102
- | `[forContextMenu]` | `<div>` |
103
- | `[forContextMenuTrigger]` | any element |
104
- | Menu surface pieces (`[forMenuContent]`, `[forMenuItem]`, …) | see _Menu_ below |
105
-
106
- ### Dialog
107
-
108
- | Selector | Host |
109
- | ------------------------ | ---------------------------------------------------------- |
110
- | `[forDialog]` | `<div>` |
111
- | `[forDialogTrigger]` | `<button>` |
112
- | `[forDialogTitle]` | `<h2>` (any heading level works; pick by document outline) |
113
- | `[forDialogDescription]` | `<p>` |
114
- | `[forDialogClose]` | `<button>` |
115
- | `[forDialogBackdrop]` | `<div>` |
116
-
117
- ### Disclosure
118
-
119
- | Selector | Host |
120
- | ------------------------ | ------------------------ |
121
- | `[forDisclosure]` | `<div>` |
122
- | `[forDisclosureTrigger]` | `<button>` |
123
- | `[forDisclosureContent]` | `<div>` (or `<section>`) |
124
-
125
- ### Dropdown Menu
126
-
127
- | Selector | Host |
128
- | ------------------------------------------------------------ | ---------------- |
129
- | `[forDropdownMenu]` | `<div>` |
130
- | `[forDropdownMenuTrigger]` | `<button>` |
131
- | Menu surface pieces (`[forMenuContent]`, `[forMenuItem]`, …) | see _Menu_ below |
132
-
133
- ### Hover Card
134
-
135
- | Selector | Host |
136
- | ----------------------- | ----------- |
137
- | `[forHoverCard]` | `<div>` |
138
- | `[forHoverCardTrigger]` | any element |
139
- | `[forHoverCardContent]` | `<div>` |
140
- | `[forHoverCardArrow]` | `<div>` |
141
-
142
- ### Listbox
143
-
144
- | Selector | Host |
145
- | ----------------------------- | -------- |
146
- | `[forListbox]` | `<div>` |
147
- | `[forListboxOption]` | `<div>` |
148
- | `[forListboxOptionIndicator]` | `<span>` |
149
- | `[forListboxGroup]` | `<div>` |
150
- | `[forListboxGroupLabel]` | `<div>` |
151
-
152
- ### Menu
153
-
154
- | Selector | Host |
155
- | -------------------------- | -------- |
156
- | `[forMenu]` | `<div>` |
157
- | `[forMenuContent]` | `<div>` |
158
- | `[forMenuItem]` | `<div>` |
159
- | `[forMenuItemIndicator]` | `<span>` |
160
- | `[forMenuCheckboxItem]` | `<div>` |
161
- | `[forMenuRadioItem]` | `<div>` |
162
- | `[forMenuRadioGroup]` | `<div>` |
163
- | `[forMenuSeparator]` | `<div>` |
164
- | `[forMenuGroup]` | `<div>` |
165
- | `[forMenuGroupLabel]` | `<div>` |
166
- | `[forMenuSub]` | `<div>` |
167
- | `[forMenuSubTrigger]` | `<div>` |
168
- | `[forMenuHorizontalArrow]` | `<span>` |
169
-
170
- ### Menubar
171
-
172
- | Selector | Host |
173
- | ------------------------------------------------------------ | ---------------- |
174
- | `[forMenubar]` | `<div>` |
175
- | `[forMenubarTrigger]` | `<button>` |
176
- | Menu surface pieces (`[forMenuContent]`, `[forMenuItem]`, …) | see _Menu_ above |
177
-
178
- ### Meter
179
-
180
- | Selector | Host |
181
- | --------------------- | ------- |
182
- | `[forMeter]` | `<div>` |
183
- | `[forMeterIndicator]` | `<div>` |
184
-
185
- ### Navigation Menu
186
-
187
- | Selector | Host |
188
- | ------------------------------ | ---------------------------------------- |
189
- | `[forNavigationMenu]` | `<nav>` |
190
- | `[forNavigationMenuList]` | `<ul>` (or `<div>`) |
191
- | `[forNavigationMenuItem]` | `<li>` (or `<div>`, matching the list) |
192
- | `[forNavigationMenuTrigger]` | `<button>` |
193
- | `[forNavigationMenuLink]` | `<a>` (or `<button>` for in-app actions) |
194
- | `[forNavigationMenuContent]` | `<div>` |
195
- | `[forNavigationMenuViewport]` | `<div>` |
196
- | `[forNavigationMenuIndicator]` | `<div>` |
197
-
198
- ### Pane Resizer
199
-
200
- | Selector | Host |
201
- | ------------------ | ------- |
202
- | `[forPaneResizer]` | `<div>` |
203
-
204
- ### Popover
205
-
206
- | Selector | Host |
207
- | ------------------------- | ------------------------------------- |
208
- | `[forPopover]` | `<div>` |
209
- | `[forPopoverTrigger]` | `<button>` |
210
- | `[forPopoverContent]` | `<div>` |
211
- | `[forPopoverTitle]` | `<h2>` (any heading; pick by outline) |
212
- | `[forPopoverDescription]` | `<p>` |
213
- | `[forPopoverClose]` | `<button>` |
214
- | `[forPopoverArrow]` | `<div>` |
215
- | `[forPopoverAnchor]` | any element |
216
-
217
- ### Progress
218
-
219
- | Selector | Host |
220
- | ------------------------ | ------- |
221
- | `[forProgress]` | `<div>` |
222
- | `[forProgressIndicator]` | `<div>` |
223
-
224
- ### Radio Group
225
-
226
- | Selector | Host |
227
- | ----------------- | --------------------------------- |
228
- | `[forRadioGroup]` | `<div>` |
229
- | `[forRadio]` | `<button type="button">` (forced) |
230
-
231
- ### Scroll Area
232
-
233
- | Selector | Host |
234
- | -------------------------- | ------- |
235
- | `[forScrollArea]` | `<div>` |
236
- | `[forScrollAreaViewport]` | `<div>` |
237
- | `[forScrollAreaContent]` | `<div>` |
238
- | `[forScrollAreaScrollbar]` | `<div>` |
239
- | `[forScrollAreaThumb]` | `<div>` |
240
- | `[forScrollAreaCorner]` | `<div>` |
241
-
242
- ### Select
243
-
244
- | Selector | Host |
245
- | ----------------------- | ---------- |
246
- | `[forSelect]` | `<div>` |
247
- | `[forSelectTrigger]` | `<button>` |
248
- | `[forSelectValue]` | `<span>` |
249
- | `[forSelectContent]` | `<div>` |
250
- | `[forSelectOption]` | `<div>` |
251
- | `[forSelectIndicator]` | `<span>` |
252
- | `[forSelectGroup]` | `<div>` |
253
- | `[forSelectGroupLabel]` | `<div>` |
254
- | `[forSelectSeparator]` | `<div>` |
255
-
256
- ### Separator
257
-
258
- | Selector | Host |
259
- | ---------------- | --------------------------------------------------- |
260
- | `[forSeparator]` | `<div>` (or `<hr>` for the static, decorative case) |
261
-
262
- ### Slider
263
-
264
- | Selector | Host |
265
- | ------------------ | ------- |
266
- | `[forSlider]` | `<div>` |
267
- | `[forSliderTrack]` | `<div>` |
268
- | `[forSliderRange]` | `<div>` |
269
- | `[forSliderThumb]` | `<div>` |
270
-
271
- ### Switch
272
-
273
- | Selector | Host |
274
- | ------------- | --------------------------------- |
275
- | `[forSwitch]` | `<button type="button">` (forced) |
276
-
277
- ### Tabs
278
-
279
- | Selector | Host |
280
- | ------------------ | ---------- |
281
- | `[forTabs]` | `<div>` |
282
- | `[forTabsList]` | `<div>` |
283
- | `[forTabsTrigger]` | `<button>` |
284
- | `[forTabsContent]` | `<div>` |
285
-
286
- ### Toast
287
-
288
- | Selector | Host |
289
- | ------------------------------------------------------ | ---------- |
290
- | `for-toast-viewport` (element) or `[forToastViewport]` | `<div>` |
291
- | `[forToast]` | `<div>` |
292
- | `[forToastTitle]` | `<div>` |
293
- | `[forToastDescription]` | `<div>` |
294
- | `[forToastAction]` | `<button>` |
295
- | `[forToastClose]` | `<button>` |
296
-
297
- ### Toggle
298
-
299
- | Selector | Host |
300
- | ---------------------- | --------------------------------- |
301
- | `[forToggle]` | `<button type="button">` (forced) |
302
- | `[forToggleGroup]` | `<div>` |
303
- | `[forToggleGroupItem]` | `<button type="button">` (forced) |
304
-
305
- ### Toolbar
306
-
307
- | Selector | Host |
308
- | ----------------------- | ---------- |
309
- | `[forToolbar]` | `<div>` |
310
- | `[forToolbarButton]` | `<button>` |
311
- | `[forToolbarLink]` | `<a>` |
312
- | `[forToolbarSeparator]` | `<div>` |
313
-
314
- ### Tooltip
315
-
316
- | Selector | Host |
317
- | --------------------- | ----------- |
318
- | `[forTooltip]` | `<div>` |
319
- | `[forTooltipTrigger]` | any element |
320
- | `[forTooltipContent]` | `<div>` |
321
- | `[forTooltipArrow]` | `<div>` |
39
+ Every primitive ships as its own **secondary entry point** — import `ForDialog` from `forty-cdk/dialog`, `ForAccordion` from `forty-cdk/accordion`, and so on — backed by the shared `forty-cdk/core` entry point. Each lives in its own folder under `projects/forty-cdk/` with its own `README.md` documenting its anatomy, API, keyboard interaction and styling hooks. The `@internationalized/date` adapters live in a dedicated `forty-cdk/internationalized-date` entry point so that optional peer stays truly optional. The main `forty-cdk` barrel is **intentionally empty** (it exports no symbols): always import from the specific `forty-cdk/<primitive>` entry point. Standalone directives plus `"sideEffects": false` mean your bundle only ever includes the primitives you import.
40
+
41
+ The tables below group the primitives by purpose. The link on each name opens that primitive's README — the canonical reference for which HTML element each directive belongs on, its inputs / outputs, `data-*` attributes and keyboard map.
42
+
43
+ ### Overlays
44
+
45
+ | Primitive | What it is |
46
+ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
47
+ | [Dialog](dialog) | Modal window with a focus trap, scroll lock and Escape / dismiss handling. Also openable imperatively via `ForDialogManager`. |
48
+ | [Drawer](drawer) | Side or bottom sheet on the modal-dialog pattern, adding pointer-driven swipe-to-dismiss and snap points. |
49
+ | [Popover](popover) | Non-modal floating panel anchored to its trigger by floating-ui, dismissed on Escape or outside interaction. |
50
+ | [Hover Card](hover-card) | Floating card that opens on hover to preview the content behind a link, with a pointer bridge. |
51
+ | [Tooltip](tooltip) | Small floating label that describes its trigger on hover or focus, without ever taking focus itself. |
52
+ | [Toast](toast) | Brief, auto-dismissing notifications stacked in a corner, opened programmatically via `ForToastManager`. |
53
+
54
+ ### Menus
55
+
56
+ | Primitive | What it is |
57
+ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
58
+ | [Menu](menu) | The shared menu surface — items, checkbox / radio items, groups, separators and submenus — composed by every menu-family primitive. |
59
+ | [Dropdown Menu](dropdown-menu) | A button that opens a menu of actions, with full keyboard navigation, typeahead and submenus. |
60
+ | [Context Menu](context-menu) | A menu opened by right-click or long-press, anchored to the pointer position. |
61
+ | [Menubar](menubar) | A horizontal bar of menus, as in a desktop application, with roving tabindex across the triggers. |
62
+
63
+ ### Navigation
64
+
65
+ | Primitive | What it is |
66
+ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
67
+ | [Navigation Menu](navigation-menu) | A site-navigation header on the disclosure pattern: buttons expand panels of links into a shared viewport. |
68
+ | [Breadcrumbs](breadcrumbs) | A labelled navigation landmark for a breadcrumb trail, with `aria-current="page"` on the current page. |
69
+ | [Pagination](pagination) | A navigation landmark that derives a page list with ellipsis gaps, plus previous / next buttons. |
70
+ | [Tabs](tabs) | A tablist that switches between panels of content. |
71
+ | [Toolbar](toolbar) | A container that groups a set of controls under roving-tabindex navigation. |
72
+ | [Stepper](stepper) | A multi-step wizard on the Tabs pattern: step list, per-step panels, linear gating and progress. |
73
+
74
+ ### Forms & input
75
+
76
+ | Primitive | What it is |
77
+ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
78
+ | [Button](button) | Turns any element into an accessible button with keyboard activation; disabled stays focusable and state is reflected as `data-*`. |
79
+ | [Field](field) | Headless wiring that ties a label, description and error region to a control and reflects validation state as `data-*`. |
80
+ | [Fieldset](fieldset) | Headless grouping that gives related fields a shared accessible name plus an optional shared disabled state. |
81
+ | [Input](input) | Attribute directives for single- and multi-line text: a string `value()` that auto-wires with Signal Forms and reflects every form state. |
82
+ | [Search](search) | A `role="searchbox"` input mirrored to a signal, paired with a self-hiding clear button; reuses Input's form-value wiring. |
83
+ | [Number Input](number-input) | A numeric spinbutton with keyboard stepping, optional +/− buttons, min / max / step clamping and Intl formatting. |
84
+ | [OTP Input](otp-input) | A one-time-code / PIN field: typed and pasted characters fill styled slots, with masking and a complete event. |
85
+ | [File Upload](file-upload) | A headless drag-and-drop / dialog file-selection zone over a visually-hidden native `<input type="file">`. |
86
+ | [Switch](switch) | A binary on / off control toggled by click, Enter or Space. |
87
+ | [Checkbox](checkbox) | A checkbox supporting the three states checked, unchecked and indeterminate. |
88
+ | [Toggle](toggle) | A two-state button that stays pressed or unpressed. |
89
+ | [Radio Group](radio-group) | A set of radio buttons where only one option can be selected, with arrow-key navigation. |
90
+ | [Slider](slider) | A draggable thumb that picks a numeric value along a track. |
91
+ | [Select](select) | A trigger that opens a portaled listbox popup to pick one or many options, with groups and separators. |
92
+ | [Combobox](combobox) | An editable input paired with a filterable listbox popup, single or multi selection with chips. |
93
+ | [Listbox](listbox) | A scrollable list of selectable options with roving-tabindex navigation, single or multi selection. |
94
+
95
+ ### Date & time
96
+
97
+ | Primitive | What it is |
98
+ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
99
+ | [Calendar](calendar) | A single-date calendar grid (APG Grid) over a pluggable date adapter, with roving-tabindex navigation. |
100
+ | [Date Field](date-field) | A segmented date (and optional time) input — each part a spinbutton with locale-driven order and clamping. |
101
+ | [Date Picker](date-picker) | A trigger that opens a floating calendar to pick a date, composing Calendar inside a dismissable popover. |
102
+ | [Date Range Field](date-range-field) | Two labelled spinbutton endpoints (start / end) sharing locale, granularity and bounds. |
103
+ | [Time Field](time-field) | A segmented time-of-day input with 12 / 24-hour cycles, optional seconds and min / max clamping. |
104
+ | [Time Picker](time-picker) | A trigger that opens a floating listbox of generated time slots over a pluggable date adapter. |
105
+ | [Time Range Field](time-range-field) | Two time-of-day endpoints (start / end) sharing the hour cycle and min / max bounds. |
106
+
107
+ ### Disclosure & content
108
+
109
+ | Primitive | What it is |
110
+ | ------------------------ | -------------------------------------------------------------------------------------------------------------------- |
111
+ | [Accordion](accordion) | A stack of collapsible sections, optionally allowing multiple panels open at once. |
112
+ | [Disclosure](disclosure) | A single trigger that shows or hides a related region of content. |
113
+ | [Carousel](carousel) | A slideshow of panels with previous / next controls, indicators, looping, multi-slide views and accessible autoplay. |
114
+
115
+ ### Data & layout
116
+
117
+ | Primitive | What it is |
118
+ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
119
+ | [Table](table) | A headless data table over a native `<table>` or `<div>` grid: sticky headers, 2D keyboard navigation, row selection, sortable headers, column resizing and reordering. |
120
+ | [Tree](tree) | A nested tree view for hierarchical data: expandable nodes with roving-tabindex navigation, selection and typeahead. |
121
+ | [Scroll Area](scroll-area) | A scrollable region with cross-browser, stylable synthetic scrollbars. |
122
+ | [Pane Resizer](pane-resizer) | A focusable divider that resizes the panes on either side — draggable and keyboard-operable. |
123
+ | [Separator](separator) | A static, optionally semantic divider between groups of content, horizontal or vertical. |
124
+ | [Aspect Ratio](aspect-ratio) | A container that keeps its content at a fixed width-to-height ratio. |
125
+ | [Avatar](avatar) | A user image with a graceful fallback across its loading lifecycle. |
126
+
127
+ ### Feedback
128
+
129
+ | Primitive | What it is |
130
+ | -------------------- | ---------------------------------------------------------------------------- |
131
+ | [Progress](progress) | A bar that reflects the completion progress of a task. |
132
+ | [Meter](meter) | A gauge that shows a scalar value within a known range, bucketed into bands. |
133
+
134
+ ### Utilities
135
+
136
+ Headless — no DOM or ARIA of their own; an `inject*` / provider API that other primitives compose.
137
+
138
+ | Utility | What it is |
139
+ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
140
+ | [Breakpoints](breakpoints) | A signal-first, zoneless, SSR-safe viewport breakpoint observer (`injectBreakpoints`). |
141
+ | [Drag & Drop](drag-drop) | Headless, accessible drag-and-drop for sortable lists and cross-list transfers, keyboard and pointer driven. |
142
+ | [Virtualization](virtualization) | A headless windowing core (`injectVirtualizer`) plus a `[forVirtualViewport]` layer that renders only the visible slice of huge lists. |
322
143
 
323
144
  ## Building
324
145
 
@@ -76,7 +76,7 @@ A sentinel option (an "Add new…" action, a "No results" row, a pinned default)
76
76
  </div>
77
77
  ```
78
78
 
79
- Static and `@for`-rendered options share the same registry, navigation order (DOM order), filtering, and label cache.
79
+ Static and `@for`-rendered options share the same registry, navigation order (DOM order), filtering, and label cache. This is right when the entry _selects_ (adds to `value` and commits). For a pinned entry that is a pure side-effect and must **not** land in `value` — "Create new…", "Manage tags…" — reach for [`[forComboboxAction]`](#action-items) instead.
80
80
 
81
81
  ### Signal Forms
82
82
 
@@ -109,6 +109,8 @@ Input tables are not yet tabulated for this primitive. See the feature sections
109
109
  | `[forComboboxOption]` | `data-state` | `checked` \| `unchecked` (membership in `value()`, both modes) |
110
110
  | `[forComboboxOption]` | `data-highlighted` | present / absent (the current `aria-activedescendant`) |
111
111
  | `[forComboboxOption]` | `data-disabled` | present / absent |
112
+ | `[forComboboxAction]` | `data-highlighted` | present / absent (the action currently holds DOM focus) |
113
+ | `[forComboboxAction]` | `data-disabled` | present / absent |
112
114
  | `[forComboboxIndicator]` | `data-state` | `checked` \| `unchecked` (mirrors the parent option) |
113
115
  | `[forComboboxChip]` | `data-value` | the chip's serialized value (verbatim string, or `itemToFormValue`) |
114
116
  | `[forComboboxChip]` | `data-disabled` | present / absent |
@@ -195,6 +197,77 @@ Why the list part is required, not optional: a `role="listbox"` may only own `op
195
197
  </ng-template>
196
198
  ```
197
199
 
200
+ ## Action items
201
+
202
+ A combobox popup often needs an entry that is an **action**, not a value —
203
+ "Create new …", "Manage tags …", "Clear all". Semantically these are
204
+ `role="button"` actions, not `role="option"` selections, so `[forComboboxAction]`
205
+ renders one that stays out of the option/value collection entirely.
206
+
207
+ ```html
208
+ <div forCombobox #combobox="forCombobox" [(query)]="query" [(value)]="value">
209
+ <input forComboboxInput placeholder="Search…" />
210
+ @if (combobox.open()) {
211
+ <div forComboboxContent>
212
+ <button forComboboxAction (action)="createNew(query())">Create "{{ query() }}"</button>
213
+ @for (it of filtered; track it.id) {
214
+ <div forComboboxOption [value]="it.id" [label]="it.label">{{ it.label }}</div>
215
+ }
216
+ </div>
217
+ }
218
+ </div>
219
+ ```
220
+
221
+ An action:
222
+
223
+ - **never touches `value` / `options()`.** It registers in a collection separate
224
+ from options, so `options()`, `aria-setsize`, and `aria-posinset` are
225
+ unaffected and activation emits `(action)` instead of mutating `[(value)]`. The
226
+ consumer decides what happens and whether to close the popup afterwards.
227
+ - **is `role="button"`, not `role="option"`.** Assistive tech announces it as an
228
+ action, not as one of N choices.
229
+ - **is reached by Tab, not the arrow keys** (see below), so it stays reachable no
230
+ matter how long — or how virtualized — the option list is.
231
+
232
+ Use `[forComboboxAction]` for a pinned side-effect. For an entry that _does_
233
+ select (an "Add new" row that adds an item and commits it to `value`), use a
234
+ plain `[forComboboxOption]` — see [Static options alongside the `@for`](#static-options-alongside-the-for).
235
+
236
+ ### Focus & keyboard (model A)
237
+
238
+ While the popup is open, **Tab / Shift+Tab** cycle DOM focus around the ring
239
+ `[input, …enabled actions]` (in DOM order, wrapping both ways) **without
240
+ dismissing** the popup. Options stay arrow-navigated via `aria-activedescendant`;
241
+ actions stay Tab-focused — the two models never mix. This keeps a pinned action
242
+ reachable in a bounded number of keypresses regardless of the option count, which
243
+ a bottom-pinned option cannot guarantee under infinite scroll.
244
+
245
+ Because focus is trapped in the input↔actions ring while open, **Escape** (or an
246
+ outside pointer) is how you leave: Escape from an action closes the popup and
247
+ returns focus to the input (editable anatomy) or the `[forComboboxTrigger]`
248
+ (picker anatomy). Activation is **click / Enter / Space** and routes to `(action)`
249
+ only. With no action registered, Tab keeps its default "close and let Tab flow on"
250
+ behaviour, so existing comboboxes are unchanged.
251
+
252
+ Actions live inside `[forComboboxContent]` (beside `[forComboboxList]` in the
253
+ picker anatomy), so they are naturally "inside" the outside-pointer / outside-focus
254
+ dismissal checks, exactly like the input.
255
+
256
+ ### API
257
+
258
+ | Member | Type | Notes |
259
+ | ------------ | -------------- | ---------------------------------------------------------------------------------------------------------- |
260
+ | `[disabled]` | `boolean` | Drops the action out of the focus ring (`tabindex` removed), reflects `aria-disabled`, ignores activation. |
261
+ | `(action)` | `output<void>` | Fired on click / Enter / Space. Never mutates `[(value)]`. |
262
+
263
+ `[forComboboxAction]` host-binds `role="button"`, `type="button"`, a
264
+ primitive-managed `tabindex`, `aria-disabled` (when disabled), and reflects
265
+ `data-highlighted` while it holds DOM focus + `data-disabled` when disabled.
266
+
267
+ > **Out of scope (v1):** grouped action clusters / multiple action zones,
268
+ > submenu-style nested actions, and actions that mutate `value` (use a plain
269
+ > `[forComboboxOption]`).
270
+
198
271
  ## Self-hiding pieces
199
272
 
200
273
  `[forComboboxClear]` (nothing to clear) and `[forComboboxEmpty]` (options exist) hide themselves with an inline `display: none` in addition to the `hidden` attribute that removes them from the accessibility tree. Because the inline style beats any author selector rule, you can give these pieces a custom `display` (e.g. `display: inline-flex` for an icon) without a `.x[hidden] { display: none }` workaround — the directive's `display: none` still wins while the piece is hidden, and your `display` applies once it shows.
@@ -480,19 +553,20 @@ The native `<input>` handles caret movement and BiDi from the document's CSS `di
480
553
 
481
554
  Focus stays in the input throughout — arrow keys move the listbox's _active descendant_ (the highlighted option), they do not move DOM focus.
482
555
 
483
- | Key | Action |
484
- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
485
- | **ArrowDown** | Open listbox + move activedescendant to next enabled option (or first when none). |
486
- | **ArrowUp** | Open listbox + move activedescendant to previous enabled option (or last when none). |
487
- | **Home** _(open)_ | Move activedescendant to first enabled option. |
488
- | **End** _(open)_ | Move activedescendant to last enabled option. |
489
- | **PageUp** _(open)_ | Move activedescendant to first enabled option. |
490
- | **PageDown** _(open)_ | Move activedescendant to last enabled option. |
491
- | **Enter** _(open)_ | Activate the activedescendant (single: replace + close; multi: toggle + stay open). |
492
- | **Escape** _(open)_ | Close the listbox. Focus stays in the input. |
493
- | **Tab** _(open)_ | Close the listbox and let Tab flow to the next focusable. |
494
- | **Backspace** _(empty input, multi only)_ | Focus the last chip; a second Backspace there removes it. |
495
- | Printable keys | Update `query`. With `'inline'` / `'both'` autocomplete, complete the rest of the first match into the input as selected text. |
556
+ | Key | Action |
557
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
558
+ | **ArrowDown** | Open listbox + move activedescendant to next enabled option (or first when none). |
559
+ | **ArrowUp** | Open listbox + move activedescendant to previous enabled option (or last when none). |
560
+ | **Home** _(open)_ | Move activedescendant to first enabled option. |
561
+ | **End** _(open)_ | Move activedescendant to last enabled option. |
562
+ | **PageUp** _(open)_ | Move activedescendant to first enabled option. |
563
+ | **PageDown** _(open)_ | Move activedescendant to last enabled option. |
564
+ | **Enter** _(open)_ | Activate the activedescendant (single: replace + close; multi: toggle + stay open). |
565
+ | **Escape** _(open)_ | Close the listbox. Focus stays in the input. |
566
+ | **Tab** _(open, no action)_ | Close the listbox and let Tab flow to the next focusable. |
567
+ | **Tab / Shift+Tab** _(open, action present)_ | Move focus around the input↔actions ring without dismissing — see [Action items](#action-items). |
568
+ | **Backspace** _(empty input, multi only)_ | Focus the last chip; a second Backspace there removes it. |
569
+ | Printable keys | Update `query`. With `'inline'` / `'both'` autocomplete, complete the rest of the first match into the input as selected text. |
496
570
 
497
571
  Hovering an option also makes it the activedescendant, so mouse and keyboard intent stay synchronized.
498
572