forty-cdk 0.9.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.
- package/README.md +104 -283
- package/combobox/README.md +88 -14
- package/fesm2022/forty-cdk-combobox.mjs +176 -8
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +121 -248
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-drag-drop.mjs +6 -1
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
- package/fesm2022/forty-cdk-drawer.mjs +14 -3
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
- package/fesm2022/forty-cdk-file-upload.mjs +7 -1
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
- package/fesm2022/forty-cdk-pane-resizer.mjs +64 -21
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -1
- package/fesm2022/forty-cdk-progress.mjs +3 -2
- package/fesm2022/forty-cdk-progress.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +1336 -605
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs +53 -22
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/package.json +1 -1
- package/table/README.md +225 -28
- package/types/forty-cdk-combobox.d.ts +114 -6
- package/types/forty-cdk-core.d.ts +67 -75
- package/types/forty-cdk-drag-drop.d.ts +22 -2
- package/types/forty-cdk-drawer.d.ts +10 -2
- package/types/forty-cdk-file-upload.d.ts +4 -1
- package/types/forty-cdk-pane-resizer.d.ts +2 -1
- package/types/forty-cdk-progress.d.ts +2 -1
- package/types/forty-cdk-table.d.ts +615 -197
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
|
139
|
-
|
|
|
140
|
-
|
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
package/combobox/README.md
CHANGED
|
@@ -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
|
|
484
|
-
|
|
|
485
|
-
| **ArrowDown**
|
|
486
|
-
| **ArrowUp**
|
|
487
|
-
| **Home** _(open)_
|
|
488
|
-
| **End** _(open)_
|
|
489
|
-
| **PageUp** _(open)_
|
|
490
|
-
| **PageDown** _(open)_
|
|
491
|
-
| **Enter** _(open)_
|
|
492
|
-
| **Escape** _(open)_
|
|
493
|
-
| **Tab** _(open)_
|
|
494
|
-
| **
|
|
495
|
-
|
|
|
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
|
|