forty-cdk 0.21.1 → 0.22.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 +50 -28
- package/accordion/README.md +2 -2
- package/aspect-ratio/README.md +1 -1
- package/calendar/README.md +3 -3
- package/combobox/README.md +9 -9
- package/context-menu/README.md +1 -1
- package/date-field/README.md +72 -0
- package/date-picker/README.md +6 -5
- package/dialog/README.md +1 -1
- package/disclosure/README.md +1 -1
- package/drag-drop/README.md +3 -4
- package/drawer/README.md +2 -2
- package/dropdown-menu/README.md +2 -2
- package/fesm2022/forty-cdk-accordion.mjs +16 -7
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -1
- package/fesm2022/forty-cdk-avatar.mjs +58 -11
- package/fesm2022/forty-cdk-avatar.mjs.map +1 -1
- package/fesm2022/forty-cdk-breakpoints.mjs +7 -2
- package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -1
- package/fesm2022/forty-cdk-button.mjs +1 -3
- package/fesm2022/forty-cdk-button.mjs.map +1 -1
- package/fesm2022/forty-cdk-calendar.mjs +9 -4
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
- package/fesm2022/forty-cdk-carousel.mjs +15 -6
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
- package/fesm2022/forty-cdk-checkbox.mjs +7 -2
- package/fesm2022/forty-cdk-checkbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-combobox.mjs +110 -102
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-context-menu.mjs +18 -15
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +762 -1359
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-field.mjs +663 -42
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs +69 -50
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-dialog.mjs +18 -10
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
- package/fesm2022/forty-cdk-disclosure.mjs +8 -3
- package/fesm2022/forty-cdk-disclosure.mjs.map +1 -1
- package/fesm2022/forty-cdk-drag-drop.mjs +15 -5
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
- package/fesm2022/forty-cdk-drawer.mjs +202 -127
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
- package/fesm2022/forty-cdk-dropdown-menu.mjs +10 -8
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-field.mjs +15 -7
- package/fesm2022/forty-cdk-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-fieldset.mjs +7 -3
- package/fesm2022/forty-cdk-fieldset.mjs.map +1 -1
- package/fesm2022/forty-cdk-file-upload.mjs +7 -2
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
- package/fesm2022/forty-cdk-hover-card.mjs +14 -10
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs +2 -2
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +29 -4
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-menu.mjs +42 -9
- package/fesm2022/forty-cdk-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-menubar.mjs +40 -54
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
- package/fesm2022/forty-cdk-meter.mjs +7 -2
- package/fesm2022/forty-cdk-meter.mjs.map +1 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs +65 -117
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-number-input.mjs +7 -2
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -1
- package/fesm2022/forty-cdk-otp-input.mjs +7 -2
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -1
- package/fesm2022/forty-cdk-pagination.mjs +7 -2
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs +16 -12
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-progress.mjs +7 -2
- package/fesm2022/forty-cdk-progress.mjs.map +1 -1
- package/fesm2022/forty-cdk-radio-group.mjs +14 -4
- package/fesm2022/forty-cdk-radio-group.mjs.map +1 -1
- package/fesm2022/forty-cdk-scroll-area.mjs +16 -3
- package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -1
- package/fesm2022/forty-cdk-search.mjs +7 -2
- package/fesm2022/forty-cdk-search.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +69 -50
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-slider.mjs +26 -10
- package/fesm2022/forty-cdk-slider.mjs.map +1 -1
- package/fesm2022/forty-cdk-stepper.mjs +15 -6
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
- package/fesm2022/forty-cdk-table-virtualization.mjs +13 -4
- package/fesm2022/forty-cdk-table-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +198 -185
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-tabs.mjs +22 -2
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-field.mjs +687 -40
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs +24 -16
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-toast.mjs +60 -68
- package/fesm2022/forty-cdk-toast.mjs.map +1 -1
- package/fesm2022/forty-cdk-toggle.mjs +7 -2
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
- package/fesm2022/forty-cdk-toolbar.mjs +13 -2
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -1
- package/fesm2022/forty-cdk-tooltip.mjs +14 -10
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
- package/fesm2022/forty-cdk-tree.mjs +186 -71
- package/fesm2022/forty-cdk-tree.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtual-reorder.mjs +11 -5
- package/fesm2022/forty-cdk-virtual-reorder.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs +8 -4
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/hover-card/README.md +13 -13
- package/internationalized-date/README.md +1 -1
- package/menu/README.md +8 -8
- package/menubar/README.md +1 -1
- package/package.json +1 -9
- package/popover/README.md +13 -13
- package/select/README.md +13 -13
- package/shared/README.md +1 -28
- package/table/README.md +3 -3
- package/time-field/README.md +77 -0
- package/time-picker/README.md +2 -2
- package/tooltip/README.md +13 -18
- package/tree/README.md +54 -26
- package/types/forty-cdk-accordion.d.ts +3 -4
- package/types/forty-cdk-avatar.d.ts +35 -11
- package/types/forty-cdk-button.d.ts +1 -3
- package/types/forty-cdk-calendar.d.ts +1 -1
- package/types/forty-cdk-carousel.d.ts +29 -18
- package/types/forty-cdk-combobox.d.ts +124 -111
- package/types/forty-cdk-context-menu.d.ts +10 -9
- package/types/forty-cdk-core.d.ts +592 -994
- package/types/forty-cdk-date-field.d.ts +415 -38
- package/types/forty-cdk-date-picker.d.ts +28 -39
- package/types/forty-cdk-dialog.d.ts +6 -8
- package/types/forty-cdk-disclosure.d.ts +1 -1
- package/types/forty-cdk-drag-drop.d.ts +2 -2
- package/types/forty-cdk-drawer.d.ts +6 -8
- package/types/forty-cdk-dropdown-menu.d.ts +2 -2
- package/types/forty-cdk-field.d.ts +2 -3
- package/types/forty-cdk-internationalized-date.d.ts +2 -2
- package/types/forty-cdk-listbox.d.ts +7 -0
- package/types/forty-cdk-menu.d.ts +11 -3
- package/types/forty-cdk-menubar.d.ts +33 -49
- package/types/forty-cdk-navigation-menu.d.ts +54 -110
- package/types/forty-cdk-popover.d.ts +2 -2
- package/types/forty-cdk-radio-group.d.ts +1 -1
- package/types/forty-cdk-scroll-area.d.ts +3 -0
- package/types/forty-cdk-select.d.ts +142 -59
- package/types/forty-cdk-slider.d.ts +5 -2
- package/types/forty-cdk-stepper.d.ts +2 -3
- package/types/forty-cdk-table.d.ts +212 -230
- package/types/forty-cdk-tabs.d.ts +15 -0
- package/types/forty-cdk-time-field.d.ts +441 -36
- package/types/forty-cdk-time-picker.d.ts +4 -4
- package/types/forty-cdk-toast.d.ts +14 -16
- package/types/forty-cdk-toolbar.d.ts +6 -0
- package/types/forty-cdk-tree.d.ts +120 -78
- package/types/forty-cdk-virtual-reorder.d.ts +3 -3
- package/types/forty-cdk-virtualization.d.ts +2 -3
- package/date-range-field/README.md +0 -253
- package/fesm2022/forty-cdk-date-range-field.mjs +0 -656
- package/fesm2022/forty-cdk-date-range-field.mjs.map +0 -1
- package/fesm2022/forty-cdk-time-range-field.mjs +0 -696
- package/fesm2022/forty-cdk-time-range-field.mjs.map +0 -1
- package/time-range-field/README.md +0 -263
- package/types/forty-cdk-date-range-field.d.ts +0 -432
- package/types/forty-cdk-time-range-field.d.ts +0 -467
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Headless / styleless UI primitives for Angular with WAI-ARIA accessibility built
|
|
|
4
4
|
Designed from the ground up for modern Angular — the API is built around signals, standalone
|
|
5
5
|
directives, and dependency-injection composition.
|
|
6
6
|
|
|
7
|
+
**Browsing?** The [documentation site](https://tutkli.github.io/forty-cdk/) renders every primitive with live examples.
|
|
8
|
+
|
|
7
9
|
**New here?** [Your first overlay](../../docs/your-first-overlay.md) walks one Popover from empty markup to styled-and-animated and explains the two concepts every overlay shares: the `@if` / open-state model and the portal → global CSS requirement.
|
|
8
10
|
|
|
9
11
|
**Styling these primitives?** [Styling forty-cdk](../../docs/styling.md) explains the three hooks you style against — your own class (not the directive selector), `data-*` state attributes, and `--for-*` custom properties — and links to each primitive's styling reference.
|
|
@@ -18,21 +20,24 @@ npm install forty-cdk
|
|
|
18
20
|
|
|
19
21
|
Required:
|
|
20
22
|
|
|
21
|
-
- `@angular/common` `^22.0.
|
|
22
|
-
- `@angular/core` `^22.0.
|
|
23
|
+
- `@angular/common` `^22.0.1`
|
|
24
|
+
- `@angular/core` `^22.0.1`
|
|
23
25
|
|
|
24
26
|
Optional — install only if you use the matching entry point / primitives:
|
|
25
27
|
|
|
26
28
|
| Peer | Needed by |
|
|
27
29
|
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
-
| `@angular/forms` `^22.0.
|
|
29
|
-
| `@internationalized/date` `^3.0.0` | The `forty-cdk/internationalized-date` entry point (`InternationalizedDateAdapter`, `InternationalizedDateTimeAdapter`). The date/time primitives themselves only
|
|
30
|
-
|
|
31
|
-
`@angular/forms/signals` is stable as of Angular 22, so the peer follows the standard major range (`^22.0.0`).
|
|
30
|
+
| `@angular/forms` `^22.0.1` | Form-control primitives (`Switch`, `Checkbox`, `RadioGroup`, `Listbox`, `Select`, `Slider`, `Combobox`, …). They implement `FormValueControl` / `FormCheckboxControl` from `@angular/forms/signals` for `[formField]` auto-wiring. The contract is type-only, so the published bundle never references the package — consumers using only non-form primitives can skip it. |
|
|
31
|
+
| `@internationalized/date` `^3.0.0` | The `forty-cdk/internationalized-date` entry point (`InternationalizedDateAdapter`, `InternationalizedDateTimeAdapter`). The date/time primitives themselves depend only on the abstract `DateAdapter` contract from `forty-cdk/shared` — install this peer only when you import that entry point. |
|
|
32
32
|
|
|
33
33
|
### Regular dependencies
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Two packages are regular dependencies, installed automatically and never declared as peers, because nothing of either crosses the public API by value. Both tree-shake out of a bundle that imports no primitive using them.
|
|
36
|
+
|
|
37
|
+
| Dependency | Used by |
|
|
38
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| `@floating-ui/dom` | Positioning for the anchored overlays — `Tooltip`, `Popover`, `Menu`, `Combobox`, `Select`, `Date Picker`, `Time Picker`, `Hover Card`. |
|
|
40
|
+
| `@tanstack/virtual-core` | The windowing core behind `forty-cdk/virtualization`, and therefore `forty-cdk/table-virtualization` and `forty-cdk/virtual-reorder`. |
|
|
36
41
|
|
|
37
42
|
## Entry points
|
|
38
43
|
|
|
@@ -46,6 +51,24 @@ Optional — install only if you use the matching entry point / primitives:
|
|
|
46
51
|
|
|
47
52
|
`forty-cdk/core` resolves too, but it is **not** public: it holds the engines and DI singletons the library refactors freely, and it exists so every primitive resolves that shared implementation to one compiled module. If a symbol you need is not exported by the three specifiers above, it is internal by design — [open an issue](https://github.com/tutkli/forty-cdk/issues) rather than importing from `core`.
|
|
48
53
|
|
|
54
|
+
## Errors
|
|
55
|
+
|
|
56
|
+
Every error and warning the library reports carries a stable code and, where they add something, the cause and the fix:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
[forty-cdk/dialog] FORCDK-DIALOG-001: ForDialogTitle must be used inside a [forDialog] element.
|
|
60
|
+
|
|
61
|
+
Cause: No FOR_DIALOG_CONTEXT provider is visible from ForDialogTitle. Angular resolves a
|
|
62
|
+
directive's dependencies at the template's declaration site rather than where it is stamped, so a
|
|
63
|
+
piece declared in an ng-template outside the root resolves nothing even when it renders inside it.
|
|
64
|
+
|
|
65
|
+
Fix: Move ForDialogTitle inside a [forDialog] element, declaring any ng-template it lives in there too.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The code is `FORCDK-<AREA>-<NUMBER>`, where the area is the entry point you imported from — so `FORCDK-DATE-PICKER-003` came from `forty-cdk/date-picker`, and `FORCDK-CORE-*` from machinery shared across primitives (those still print the prefix of the primitive you actually wrote). **A code is stable and always means the same failure**, so it is safe to search for, quote in an issue, or match on in your own error handling; a retired code is never reused for something else.
|
|
69
|
+
|
|
70
|
+
Warnings are dev-mode only. Errors are not: a piece that resolved no context would fail one line later anyway, so it throws in production too and says why.
|
|
71
|
+
|
|
49
72
|
## Primitives
|
|
50
73
|
|
|
51
74
|
Every primitive ships as its own secondary entry point, and each lives in its own folder under `projects/forty-cdk/` with its own `README.md` documenting its anatomy, API, keyboard interaction and styling hooks. Standalone directives plus `"sideEffects": false` mean your bundle only ever includes the primitives you import.
|
|
@@ -106,15 +129,13 @@ The tables below group the primitives by purpose. The link on each name opens th
|
|
|
106
129
|
|
|
107
130
|
### Date & time
|
|
108
131
|
|
|
109
|
-
| Primitive
|
|
110
|
-
|
|
|
111
|
-
| [Calendar](calendar)
|
|
112
|
-
| [Date Field](date-field)
|
|
113
|
-
| [Date Picker](date-picker)
|
|
114
|
-
| [
|
|
115
|
-
| [Time
|
|
116
|
-
| [Time Picker](time-picker) | A trigger that opens a floating listbox of generated time slots over a pluggable date adapter. |
|
|
117
|
-
| [Time Range Field](time-range-field) | Two time-of-day endpoints (start / end) sharing the hour cycle and min / max bounds. |
|
|
132
|
+
| Primitive | What it is |
|
|
133
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
134
|
+
| [Calendar](calendar) | A single-date calendar grid (APG Grid) over a pluggable date adapter, with roving-tabindex navigation. |
|
|
135
|
+
| [Date Field](date-field) | A segmented date (and optional time) input — each part a spinbutton with locale-driven order and clamping. Ships `ForDateRangeField` too. |
|
|
136
|
+
| [Date Picker](date-picker) | A trigger that opens a floating calendar to pick a date, composing Calendar inside a dismissible popover. Ships `ForDateRangePicker` too. |
|
|
137
|
+
| [Time Field](time-field) | A segmented time-of-day input with 12 / 24-hour cycles, optional seconds and min / max clamping. Ships `ForTimeRangeField` too. |
|
|
138
|
+
| [Time Picker](time-picker) | A trigger that opens a floating listbox of generated time slots over a pluggable date adapter. |
|
|
118
139
|
|
|
119
140
|
### Disclosure & content
|
|
120
141
|
|
|
@@ -126,15 +147,16 @@ The tables below group the primitives by purpose. The link on each name opens th
|
|
|
126
147
|
|
|
127
148
|
### Data & layout
|
|
128
149
|
|
|
129
|
-
| Primitive
|
|
130
|
-
|
|
|
131
|
-
| [Table](table)
|
|
132
|
-
| [Tree](tree)
|
|
133
|
-
| [Scroll Area](scroll-area)
|
|
134
|
-
| [Pane Resizer](pane-resizer)
|
|
135
|
-
| [Separator](separator)
|
|
136
|
-
| [Aspect Ratio](aspect-ratio)
|
|
137
|
-
| [Avatar](avatar)
|
|
150
|
+
| Primitive | What it is |
|
|
151
|
+
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
152
|
+
| [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. |
|
|
153
|
+
| [Tree](tree) | A nested tree view for hierarchical data: expandable nodes with roving-tabindex navigation, selection and typeahead. |
|
|
154
|
+
| [Scroll Area](scroll-area) | A scrollable region with cross-browser, stylable synthetic scrollbars. |
|
|
155
|
+
| [Pane Resizer](pane-resizer) | A focusable divider that resizes the panes on either side — draggable and keyboard-operable. |
|
|
156
|
+
| [Separator](separator) | A static, optionally semantic divider between groups of content, horizontal or vertical. |
|
|
157
|
+
| [Aspect Ratio](aspect-ratio) | A container that keeps its content at a fixed width-to-height ratio. |
|
|
158
|
+
| [Avatar](avatar) | A user image with a graceful fallback across its loading lifecycle. |
|
|
159
|
+
| [Visually Hidden](visually-hidden) | Hides content visually while keeping it in the accessibility tree — screen-reader-only labels and announcements. |
|
|
138
160
|
|
|
139
161
|
### Feedback
|
|
140
162
|
|
|
@@ -158,7 +180,7 @@ Headless — no DOM or ARIA of their own; an `inject*` / provider API that other
|
|
|
158
180
|
## Building
|
|
159
181
|
|
|
160
182
|
```bash
|
|
161
|
-
|
|
183
|
+
pnpm build
|
|
162
184
|
```
|
|
163
185
|
|
|
164
186
|
Build artifacts land in `dist/forty-cdk` (consumed locally via the `forty-cdk` path alias in the root `tsconfig.json`).
|
|
@@ -169,11 +191,11 @@ Tests run on Vitest via the Angular CLI builder `@angular/build:unit-test`:
|
|
|
169
191
|
|
|
170
192
|
```bash
|
|
171
193
|
pnpm test # all specs, single pass
|
|
172
|
-
pnpm
|
|
194
|
+
pnpm test:watch # watch mode
|
|
173
195
|
pnpm exec ng test forty-cdk --include "../accordion/src/accordion.spec.ts" # single file (path relative to projects/forty-cdk/src/)
|
|
174
196
|
pnpm exec ng test forty-cdk --filter "Enter and Space select" # tests by name (regex)
|
|
175
197
|
```
|
|
176
198
|
|
|
177
199
|
The `-- <path>` / `-- -t "<name>"` passthrough forms do **not** work on this setup (pnpm mangles the quoted `--`, so `ng` rejects it) — use the builder's own `--include` (repeatable) and `--filter` (regex) flags instead.
|
|
178
200
|
|
|
179
|
-
|
|
201
|
+
The whole suite runs under `provideZonelessChangeDetection()`, so reactivity is verified without Zone.js on every spec rather than in a per-primitive case.
|
package/accordion/README.md
CHANGED
|
@@ -111,12 +111,12 @@ export class DemoFaq {
|
|
|
111
111
|
|
|
112
112
|
- **Heading wrapper is your job.** The library does not render a heading around the trigger — wrap it in the heading level (`<h2>`–`<h6>`) appropriate to your document outline. Without it, screen-reader landmark navigation is broken.
|
|
113
113
|
- **Use a real `<button type="button">` for the trigger.** Native Enter / Space activation and focus come for free; the directive does not synthesize them.
|
|
114
|
-
- **`role="region"`** is added to every panel automatically. APG recommends suppressing it on accordions with 6+ panels to avoid landmark proliferation
|
|
114
|
+
- **`role="region"`** is added to every panel automatically. APG recommends suppressing it on accordions with 6+ panels to avoid landmark proliferation; there is currently no opt-out.
|
|
115
115
|
- **Closed panels leave the accessibility tree.** While closed, `ForAccordionContent` sets `aria-hidden="true"` and `inert` on the panel, removing it from both the accessibility tree and the focus order. The directive does **not** apply `[hidden]`, so pick how to hide it visually:
|
|
116
116
|
- **Mount / unmount with `@if (item.expanded())`** — the panel is absent from the DOM while closed; the cleanest path for `animate.enter` / `animate.leave`. The trigger emits `aria-controls` only while expanded, so the reference never dangles at an unmounted panel.
|
|
117
117
|
- **Leave it mounted** — preserve internal state or run CSS-only transitions off `data-state`. Add `display: none` (or your own collapse animation) keyed on `[data-state="closed"]` to also hide it visually.
|
|
118
118
|
- **`aria-disabled`** is applied to the open trigger only when single mode is active and `collapsible=false`, indicating the user cannot collapse it from this trigger.
|
|
119
|
-
- **A truly disabled item (`[disabled]` on `[forAccordionItem]`) uses the native `disabled` attribute on the trigger, by design.**
|
|
119
|
+
- **A truly disabled item (`[disabled]` on `[forAccordionItem]`) uses the native `disabled` attribute on the trigger, by design.** The trigger is a real single-purpose `<button>`, not a roving-tabindex collection item (each trigger stays independently in the Tab order; arrow-key navigation is the APG-optional enhancement on top). The disabled trigger leaves the Tab order and the arrow-key navigation (which already skips it), but stays in the accessibility tree so screen readers announce it as unavailable in browse mode. The [APG Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) does not require disabled headers to remain focusable.
|
|
120
120
|
|
|
121
121
|
## Styling
|
|
122
122
|
|
package/aspect-ratio/README.md
CHANGED
|
@@ -91,7 +91,7 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
|
|
|
91
91
|
|
|
92
92
|
## Behavior notes
|
|
93
93
|
|
|
94
|
-
- **Browser support.** Native `aspect-ratio` is in Baseline 2021 (Chrome 88+, Firefox 89+, Safari 15+)
|
|
94
|
+
- **Browser support.** Native `aspect-ratio` is in Baseline 2021 (Chrome 88+, Firefox 89+, Safari 15+), so no polyfill is needed on any browser Angular itself supports.
|
|
95
95
|
- **Width still on you.** The directive only sets `aspect-ratio`; you decide width / max-width / display. The height is computed from the ratio.
|
|
96
96
|
- **Children fill the box.** Use `width: 100%; height: 100%; object-fit: cover` on inner media to fill without distortion. The directive imposes no styles on children.
|
|
97
97
|
- **No role, no a11y.** This is a layout utility. The element it sits on keeps whatever semantics you give it (`<div>`, `<figure>`, `<a>`, …).
|
package/calendar/README.md
CHANGED
|
@@ -24,9 +24,9 @@ bootstrapApplication(App, {
|
|
|
24
24
|
});
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
`@internationalized/date` is a widely-used immutable date primitive; it works in every browser today with no polyfill, and its reference-equality-on-mutation makes it signal-friendly.
|
|
27
|
+
`@internationalized/date` is a widely-used immutable date primitive; it works in every browser today with no polyfill, and its reference-equality-on-mutation makes it signal-friendly.
|
|
28
28
|
|
|
29
|
-
**Calendar system (Gregorian).** The adapter seam abstracts the date _library_ and locale-aware _formatting_, not the calendar _system_'s month structure.
|
|
29
|
+
**Calendar system (Gregorian).** The adapter seam abstracts the date _library_ and locale-aware _formatting_, not the calendar _system_'s month structure. Both `@internationalized/date` adapters build Gregorian dates, so the grid stays Gregorian regardless of the runtime locale, and the grid, the month picker and the date field all assume a Gregorian-structured year — exactly twelve months, `month` **1-12**, the year ending at month 12. Adapters over calendars with a different month structure (e.g. a 13-month year) are not supported. The optional `compareDate` hook overrides day-only _ordering_ only — it does not make the grid non-Gregorian.
|
|
30
30
|
|
|
31
31
|
## Anatomy
|
|
32
32
|
|
|
@@ -205,7 +205,7 @@ Set `selectionMode="range"` and bind `[(range)]` to get date-range selection. In
|
|
|
205
205
|
|
|
206
206
|
**`aria-selected`** in range mode is `"true"` across every committed-range cell (inclusive). During selecting (range null), it is `"false"` everywhere.
|
|
207
207
|
|
|
208
|
-
**
|
|
208
|
+
**Scope.** Range mode is day-granular only — `granularity` / time is orthogonal and not supported alongside it.
|
|
209
209
|
|
|
210
210
|
## Month / year navigation
|
|
211
211
|
|
package/combobox/README.md
CHANGED
|
@@ -140,7 +140,7 @@ Focus stays on the `<input>` the whole time the listbox is open, so options neve
|
|
|
140
140
|
|
|
141
141
|
## Anchoring to a field box
|
|
142
142
|
|
|
143
|
-
By default the listbox is positioned against `[forComboboxInput]`. When the input lives inside a decorated field box — padding, a prefix icon, a clear button, or the multi-mode chip cluster — anchoring to the bare `<input>` makes the panel narrower than the visible field and offset from its edge. Wrap the field box in `[forComboboxAnchor]` so floating-ui positions (and sizes, via `--for-anchor-width`) the listbox against the box instead:
|
|
143
|
+
By default the listbox is positioned against `[forComboboxInput]`. When the input lives inside a decorated field box — padding, a prefix icon, a clear button, or the multi-mode chip cluster — anchoring to the bare `<input>` makes the panel narrower than the visible field and offset from its edge. Wrap the field box in `[forComboboxAnchor]` so floating-ui positions (and sizes, via `--for-floating-anchor-width`) the listbox against the box instead:
|
|
144
144
|
|
|
145
145
|
```html
|
|
146
146
|
<div forCombobox #combobox="forCombobox" [(query)]="query" [(value)]="value">
|
|
@@ -150,7 +150,7 @@ By default the listbox is positioned against `[forComboboxInput]`. When the inpu
|
|
|
150
150
|
<button class="clear" (click)="combobox.clear()">×</button>
|
|
151
151
|
</div>
|
|
152
152
|
@if (combobox.open()) {
|
|
153
|
-
<div forComboboxContent style="width: var(--for-anchor-width)">
|
|
153
|
+
<div forComboboxContent style="width: var(--for-floating-anchor-width)">
|
|
154
154
|
@for (it of filtered; track it.id) {
|
|
155
155
|
<div forComboboxOption [value]="it.id" [label]="it.label">{{ it.label }}</div>
|
|
156
156
|
}
|
|
@@ -633,13 +633,13 @@ forty-cdk ships no styles. Add your own class to each piece — the for\* select
|
|
|
633
633
|
|
|
634
634
|
`[forComboboxContent]` is portaled to `document.body` and gets its position resolved by floating-ui. The resolved geometry is exposed as custom properties on the content host (cleared on close):
|
|
635
635
|
|
|
636
|
-
| Custom property
|
|
637
|
-
|
|
|
638
|
-
| `--for-anchor-width` | px | Anchor (input / wrapper) width — match the listbox to the input with `width: var(--for-anchor-width)`.
|
|
639
|
-
| `--for-anchor-height` | px | Anchor height.
|
|
640
|
-
| `--for-available-width` | px | Space available along the inline axis (floating-ui `size` middleware) — clamp with `max-width`.
|
|
641
|
-
| `--for-available-height` | px | Space available along the block axis — clamp with `max-height`.
|
|
642
|
-
| `--for-content-transform-origin` | `<origin>` keywords | `transform-origin` matching the resolved side / align, so a `scale` enter animation pivots from the input.
|
|
636
|
+
| Custom property | Type / range | Meaning |
|
|
637
|
+
| ----------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
638
|
+
| `--for-floating-anchor-width` | px | Anchor (input / wrapper) width — match the listbox to the input with `width: var(--for-floating-anchor-width)`. |
|
|
639
|
+
| `--for-floating-anchor-height` | px | Anchor height. |
|
|
640
|
+
| `--for-floating-available-width` | px | Space available along the inline axis (floating-ui `size` middleware) — clamp with `max-width`. |
|
|
641
|
+
| `--for-floating-available-height` | px | Space available along the block axis — clamp with `max-height`. |
|
|
642
|
+
| `--for-floating-content-transform-origin` | `<origin>` keywords | `transform-origin` matching the resolved side / align, so a `scale` enter animation pivots from the input. |
|
|
643
643
|
|
|
644
644
|
> `[forComboboxContent]` is portaled to `document.body`, so it lives outside your component's view-encapsulated styles. Style it with global CSS (or a class you pass through) and the shared positioner properties above. See [Styling floating content](../../../docs/styling-floating-content.md) for the full positioner-variable list and the portal styling rules.
|
|
645
645
|
|
package/context-menu/README.md
CHANGED
|
@@ -161,7 +161,7 @@ Same vetoable dismiss API as DropdownMenu. Call `preventDefault()` on the emitte
|
|
|
161
161
|
|
|
162
162
|
forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
|
|
163
163
|
|
|
164
|
-
> The menu content (`[forMenuContent]`, from the [`menu/`](../menu/README.md) folder) portals to `document.body`, so it sits outside the trigger's DOM subtree — descendant selectors won't reach it. Style it with **global CSS** or a class on the content element. The content host also exposes the shared positioner custom properties (`--for-anchor-width` / `--for-anchor-height`, `--for-available-width` / `--for-available-height`, `--for-content-transform-origin`); see [Styling floating content](../../../docs/styling-floating-content.md) for the full list and the animation rules.
|
|
164
|
+
> The menu content (`[forMenuContent]`, from the [`menu/`](../menu/README.md) folder) portals to `document.body`, so it sits outside the trigger's DOM subtree — descendant selectors won't reach it. Style it with **global CSS** or a class on the content element. The content host also exposes the shared positioner custom properties (`--for-floating-anchor-width` / `--for-floating-anchor-height`, `--for-floating-available-width` / `--for-floating-available-height`, `--for-floating-content-transform-origin`); see [Styling floating content](../../../docs/styling-floating-content.md) for the full list and the animation rules.
|
|
165
165
|
|
|
166
166
|
```css
|
|
167
167
|
.context-menu-trigger[data-state='open'] {
|
package/date-field/README.md
CHANGED
|
@@ -188,6 +188,78 @@ providers: [
|
|
|
188
188
|
|
|
189
189
|
`segmentLabels` supplies each segment's default `aria-label`, keyed by part type. Unset keys keep the library default (the part name, and `'AM/PM'` for the `dayPeriod` segment), so overriding a single key never wipes the rest. A segment's own `[ariaLabel]` still wins over the scope default.
|
|
190
190
|
|
|
191
|
+
## Range selection — `ForDateRangeField`
|
|
192
|
+
|
|
193
|
+
For a date range use the dedicated `ForDateRangeField` root (selector `[forDateRangeField]`), shipped from this same entry point. It is the keyboard-first, form-capable counterpart to [DateRangePicker](../date-picker/README.md): two labelled `role="group"` endpoints (start / end), each holding a row of spinbutton segments — the same machinery as `ForDateField` — nested inside one outer `role="group"`. It implements `FormValueControl<DateRange<D> | null>`, the **same** contract as `ForDateRangePicker`, so the committed range auto-wires with `[formField]`. The value stays `null` until **both** endpoints are fully entered and ordered (`start <= end`).
|
|
194
|
+
|
|
195
|
+
The pieces are the range-specific `[forDateRangeFieldStart]` / `[forDateRangeFieldEnd]` endpoint groups plus `[forDateRangeFieldSegment]` / `[forDateRangeFieldLiteral]`; each endpoint exposes its own `segments()` list, so the same `@for` template renders both sides.
|
|
196
|
+
|
|
197
|
+
```html
|
|
198
|
+
<div forDateRangeField [(value)]="stay" ariaLabel="Stay">
|
|
199
|
+
<div forDateRangeFieldStart #start="forDateRangeFieldStart">
|
|
200
|
+
@for (seg of start.segments(); track seg.id) { @if (seg.isLiteral) {
|
|
201
|
+
<span forDateRangeFieldLiteral>{{ seg.text }}</span>
|
|
202
|
+
} @else {
|
|
203
|
+
<span forDateRangeFieldSegment [segment]="seg.type!">{{ seg.text }}</span>
|
|
204
|
+
} }
|
|
205
|
+
</div>
|
|
206
|
+
<span aria-hidden="true">–</span>
|
|
207
|
+
<div forDateRangeFieldEnd #end="forDateRangeFieldEnd">
|
|
208
|
+
@for (seg of end.segments(); track seg.id) { @if (seg.isLiteral) {
|
|
209
|
+
<span forDateRangeFieldLiteral>{{ seg.text }}</span>
|
|
210
|
+
} @else {
|
|
211
|
+
<span forDateRangeFieldSegment [segment]="seg.type!">{{ seg.text }}</span>
|
|
212
|
+
} }
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import {
|
|
219
|
+
ForDateRangeField,
|
|
220
|
+
ForDateRangeFieldEnd,
|
|
221
|
+
ForDateRangeFieldLiteral,
|
|
222
|
+
ForDateRangeFieldSegment,
|
|
223
|
+
ForDateRangeFieldStart,
|
|
224
|
+
} from 'forty-cdk/date-field';
|
|
225
|
+
import type { DateRange } from 'forty-cdk/shared';
|
|
226
|
+
|
|
227
|
+
readonly model = signal({ stay: null as DateRange<CalendarDate> | null });
|
|
228
|
+
readonly booking = form(this.model);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `ForDateRangeField` API
|
|
232
|
+
|
|
233
|
+
| Property | Type | Description |
|
|
234
|
+
| ------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
235
|
+
| `value` | `model<DateRange<D> \| null>` | Two-way bindable committed range, or `null` while incomplete or out of order. The `FormValueControl` backing.<br>**Default:** `null` |
|
|
236
|
+
| `minDate` | `input<D \| null>` | Minimum date (inclusive) for both endpoints. A composed endpoint below it is clamped up. Named `minDate` — see note below.<br>**Default:** `null` |
|
|
237
|
+
| `maxDate` | `input<D \| null>` | Maximum date (inclusive) for both endpoints. A composed endpoint above it is clamped down.<br>**Default:** `null` |
|
|
238
|
+
| `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision shared by both endpoints. `'day'` is date-only; coarser-than-day appends time segments.<br>**Default:** `'day'` |
|
|
239
|
+
| `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the time segments. `null` → locale. 12-hour adds the AM/PM segment.<br>**Default:** `null` |
|
|
240
|
+
| `locale` | `input<string \| null>` | BCP 47 locale driving segment order, separators, and month name. `null` → runtime locale.<br>**Default:** `null` |
|
|
241
|
+
| `placeholder` | `input<Partial<Record<SegmentType, string>>>` | Per-segment placeholder while empty, applied to both endpoints.<br>**Default:** `{}` |
|
|
242
|
+
| `ariaLabel` | `input<string \| null>` | Accessible name for the whole range field group. Emits no `aria-label` while `null`.<br>**Default:** `null` |
|
|
243
|
+
| `dir` | `input<'ltr' \| 'rtl' \| null>` | Writing direction. `null` resolves the ambient direction; mirrors ArrowLeft / ArrowRight segment navigation.<br>**Default:** `null` |
|
|
244
|
+
|
|
245
|
+
The endpoint groups each accept an `ariaLabel` input for their own group label, falling back to the scope defaults (`'Start date'` / `'End date'`). Plus the shared `FormUiControl` members bound automatically by `[formField]`.
|
|
246
|
+
|
|
247
|
+
> **Why `minDate` / `maxDate`, not `min` / `max`?** Beyond the reason above, `FormUiControl.min` / `max` are additionally typed `NonNullable<TValue>` — the range object itself — which is meaningless as a bound.
|
|
248
|
+
|
|
249
|
+
`[forDateRangeField]` reflects the same `data-disabled` / `data-readonly` / `data-empty` hooks as `[forDateField]`, plus `data-range-error`; `[forDateRangeFieldSegment]` reflects the same four segment hooks. `data-empty` marks the field only while **both** endpoints are entirely empty; a partially-filled or complete-but-disordered range is **not** empty.
|
|
250
|
+
|
|
251
|
+
### Ordering
|
|
252
|
+
|
|
253
|
+
The two endpoints are typed independently, so order is not guaranteed by construction the way the picker's two-click flow guarantees it. The field preserves the `DateRange` `end >= start` invariant by **never emitting an out-of-order range**: when both endpoints are complete but `start > end`, the typed segments are kept (not silently rewritten), `value` stays `null`, and the root reflects `aria-invalid="true"` + `data-range-error` so the disorder is perceivable and stylable. Editing either endpoint back into order emits the range.
|
|
254
|
+
|
|
255
|
+
### Range keyboard and accessibility
|
|
256
|
+
|
|
257
|
+
Each endpoint is its own tab stop, so `Tab` moves start group → end group → next control; arrows move between segments **within** an endpoint. Every other key behaves as in the [Keyboard](#keyboard) table below. Roving tabindex is per endpoint, and `aria-invalid="true"` is reflected on the root when the form marks it invalid **or** when two complete endpoints are out of order; everything else matches the [Accessibility](#accessibility) notes below.
|
|
258
|
+
|
|
259
|
+
### Range scope defaults
|
|
260
|
+
|
|
261
|
+
`provideForDateRangeFieldDefaults` mirrors `provideForDateFieldDefaults` and adds `startLabel` / `endLabel` for the two endpoint group `aria-label`s (`'Start date'` / `'End date'` by default). Both wrapper patterns work via `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_OUTPUTS` — see [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
|
|
262
|
+
|
|
191
263
|
## Keyboard
|
|
192
264
|
|
|
193
265
|
Key behavior applies per segment. Horizontal arrows mirror under `dir="rtl"`.
|
package/date-picker/README.md
CHANGED
|
@@ -219,7 +219,7 @@ By default the surface is positioned against `[forDatePickerTrigger]`. When the
|
|
|
219
219
|
</div>
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
-
`[forDatePickerAnchor]` changes **only** positioning. The trigger keeps `aria-haspopup` / `aria-expanded` / `aria-controls`, the click toggle, focus return on close, and its exemption from outside-pointer dismissal. Without an anchor the surface falls back to the trigger, so existing markup is unaffected. At most one `[forDatePickerAnchor]` per `[forDatePicker]` — a second one throws `[forty-cdk/date-picker]`. (A calendar has its own intrinsic width and ignores `--for-anchor-width`, so the anchor mainly affects start / side alignment to the box edge.)
|
|
222
|
+
`[forDatePickerAnchor]` changes **only** positioning. The trigger keeps `aria-haspopup` / `aria-expanded` / `aria-controls`, the click toggle, focus return on close, and its exemption from outside-pointer dismissal. Without an anchor the surface falls back to the trigger, so existing markup is unaffected. At most one `[forDatePickerAnchor]` per `[forDatePicker]` — a second one throws `[forty-cdk/date-picker]`. (A calendar has its own intrinsic width and ignores `--for-floating-anchor-width`, so the anchor mainly affects start / side alignment to the box edge.)
|
|
223
223
|
|
|
224
224
|
## Modal vs non-modal
|
|
225
225
|
|
|
@@ -275,10 +275,11 @@ The value display (`[forDatePickerValue]`) automatically appends the time to its
|
|
|
275
275
|
|
|
276
276
|
For date-range selection use the dedicated `ForDateRangePicker` root (selector `[forDateRangePicker]`). It is the root **and** the form value, implementing `FormValueControl<DateRange<D> | null>`, so the committed range auto-wires with `[formField]` exactly like any other control.
|
|
277
277
|
|
|
278
|
-
It reuses the same pieces — `[forDatePickerTrigger]`, `[forDatePickerContent]`, `[forDatePickerValue]`, `[forDatePickerAnchor]` — through a shared base, and provides `FOR_DATE_PICKER_CONTEXT` so they resolve under it. Project a `[forCalendar]` in `selectionMode="range"` and bind its range to the picker's `value`; the two-click anchor → commit flow keeps `value` `null` until both endpoints are chosen (the form never sees a half-entered range), and `start <= end` is an invariant. Range is day-granular
|
|
278
|
+
It reuses the same pieces — `[forDatePickerTrigger]`, `[forDatePickerContent]`, `[forDatePickerValue]`, `[forDatePickerAnchor]` — through a shared base, and provides `FOR_DATE_PICKER_CONTEXT` so they resolve under it. Project a `[forCalendar]` in `selectionMode="range"` and bind its range to the picker's `value`; the two-click anchor → commit flow keeps `value` `null` until both endpoints are chosen (the form never sees a half-entered range), and `start <= end` is an invariant. Range is day-granular (no time composition).
|
|
279
279
|
|
|
280
280
|
```ts
|
|
281
|
-
import {
|
|
281
|
+
import { ForDateRangePicker } from 'forty-cdk/date-picker';
|
|
282
|
+
import type { DateRange } from 'forty-cdk/shared';
|
|
282
283
|
import { form } from '@angular/forms/signals';
|
|
283
284
|
|
|
284
285
|
interface Booking {
|
|
@@ -338,7 +339,7 @@ Implements the [WAI-ARIA Date Picker Dialog pattern](https://www.w3.org/WAI/ARIA
|
|
|
338
339
|
|
|
339
340
|
- **`role="combobox"`** on the trigger with **`aria-haspopup="dialog"`**, `aria-expanded` reflecting `open()`, and `aria-controls` pointing at the surface while open — the same shape `[forSelectTrigger]` / `[forTimePickerTrigger]` ship, with the `dialog` popup token ARIA 1.2 allows for a combobox surface. The role is also what makes the form-control ARIA below legal: `role="button"` supports neither `aria-readonly` nor `aria-required`.
|
|
340
341
|
- **`role="dialog"`** on the surface, named by `[ariaLabel]` (or `aria-labelledby` the trigger when no label is set). `aria-modal="true"` only in modal mode (truthy-only).
|
|
341
|
-
- **Form-control ARIA** (`aria-readonly` / `aria-required` / `aria-invalid` / `aria-busy`) is reflected on the focusable trigger so assistive tech announces validity on the element that takes focus, alongside the `data-readonly` styling hook. The disabled state is the exception: it reflects through the native `disabled` attribute alone (plus `data-disabled`), never `aria-disabled` — one channel
|
|
342
|
+
- **Form-control ARIA** (`aria-readonly` / `aria-required` / `aria-invalid` / `aria-busy`) is reflected on the focusable trigger so assistive tech announces validity on the element that takes focus, alongside the `data-readonly` styling hook. The disabled state is the exception: it reflects through the native `disabled` attribute alone (plus `data-disabled`), never `aria-disabled` — one channel only.
|
|
342
343
|
- **Inside a `[forField]` the labelled element is the trigger**, not the `[forDatePicker]` / `[forDateRangePicker]` wrapper: the field's `controlId` and its `aria-labelledby` / `aria-describedby` / `aria-errormessage` land on `[forDatePickerTrigger]`, so `[forLabel]`'s `for` points at the element that takes focus, clicking a non-`<label>` `[forLabel]` opens the surface, and Signal Forms' focus-on-error reaches the trigger. `role="combobox"` takes its name from the author, so this is the channel that names the control — the root's `[ariaLabel]` names the `role="dialog"` surface instead.
|
|
343
344
|
- **Focus management**: focus enters the surface on open (the calendar's roving cell in non-modal mode) and returns to the trigger on close, both vetoable via `(autoFocusOnOpen)` / `(autoFocusOnClose)`.
|
|
344
345
|
- **Dismissal**: Escape (`(escapeKeyDown)`) and outside-pointer (`(pointerDownOutside)` / `(interactOutside)`) close the surface, each vetoable.
|
|
@@ -347,7 +348,7 @@ Implements the [WAI-ARIA Date Picker Dialog pattern](https://www.w3.org/WAI/ARIA
|
|
|
347
348
|
|
|
348
349
|
forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed under [Data attributes](#data-attributes).
|
|
349
350
|
|
|
350
|
-
> `[forDatePickerContent]` is portaled to `document.body`, so it lives outside your component's view-encapsulated styles. Style it with **global CSS** (or a class you pass through) rather than component-scoped rules — see [Styling floating content](../../../docs/styling-floating-content.md). In non-modal (anchored) mode the surface also exposes the shared positioner custom properties (`--for-anchor-width` / `--for-anchor-height`, `--for-available-width` / `--for-available-height`, `--for-content-transform-origin`); that same guide tabulates the full set.
|
|
351
|
+
> `[forDatePickerContent]` is portaled to `document.body`, so it lives outside your component's view-encapsulated styles. Style it with **global CSS** (or a class you pass through) rather than component-scoped rules — see [Styling floating content](../../../docs/styling-floating-content.md). In non-modal (anchored) mode the surface also exposes the shared positioner custom properties (`--for-floating-anchor-width` / `--for-floating-anchor-height`, `--for-floating-available-width` / `--for-floating-available-height`, `--for-floating-content-transform-origin`); that same guide tabulates the full set.
|
|
351
352
|
|
|
352
353
|
```css
|
|
353
354
|
.date-picker-trigger .date-picker-value[data-placeholder] {
|
package/dialog/README.md
CHANGED
|
@@ -75,7 +75,7 @@ This is different from trigger-anchored overlays (Popover, DropdownMenu, etc.) w
|
|
|
75
75
|
|
|
76
76
|
The payload is a `ForDialogCloseReason` string (`'escape'`, `'backdrop'`, `'pointerDownOutside'`, `'focusOutside'`, `'closeButton'`, `'programmatic'`) — use it if you need to branch on why the dialog closed, for example to show a "save changes?" prompt before dismissing. Emitting `(dismiss)` without acting on it is always safe: you can call `preventDefault()` on the preceding dismiss outputs (`(escapeKeyDown)`, `(pointerDownOutside)`, `(focusOutside)`, `(interactOutside)`) to suppress the `(dismiss)` entirely.
|
|
77
77
|
|
|
78
|
-
> **
|
|
78
|
+
> **The declarative and imperative surfaces spell this differently, on purpose.** The output is `(dismiss)` — an output named `close` would collide with the native DOM event and break any wrapper re-exposing it through `hostDirectives`. Nothing else changes name: the imperative handle method is `ForDialogRef.close()`, the directive selector is `[forDialogClose]`, and the payload type is `ForDialogCloseReason`.
|
|
79
79
|
|
|
80
80
|
### Trigger / surface id wiring
|
|
81
81
|
|
package/disclosure/README.md
CHANGED
|
@@ -76,7 +76,7 @@ The library ships no styles. Hide animations / transitions can be driven off `da
|
|
|
76
76
|
| `data-state` | `open` \| `closed` |
|
|
77
77
|
| `data-disabled` | present \| absent |
|
|
78
78
|
|
|
79
|
-
Reflects on its host: `id`, `aria-expanded`, `aria-controls`, `disabled`, `data-state`. Toggles the state on click. The disabled reflection (the native `disabled` attribute plus `data-disabled`; no `aria-disabled
|
|
79
|
+
Reflects on its host: `id`, `aria-expanded`, `aria-controls`, `disabled`, `data-state`. Toggles the state on click. The disabled reflection (the native `disabled` attribute plus `data-disabled`; no `aria-disabled` — one channel only) and the click guard follow the effective state — the trigger's own `disabled` OR the root's.
|
|
80
80
|
|
|
81
81
|
`aria-controls` is emitted only while open — mirroring the overlay triggers' open-only gating — so the reference never dangles at an unmounted panel under the recommended `@if (open())` mount pattern.
|
|
82
82
|
|
package/drag-drop/README.md
CHANGED
|
@@ -392,8 +392,8 @@ Both `data-dragging` rows hold for a drag a **coordinator** composing the list o
|
|
|
392
392
|
than starting through `[forDraggable]` itself — the keyboard lift of `[forVirtualReorder]`, and
|
|
393
393
|
the virtualized branch of `[forTableRowReorder]`. Those intercept the lift key before the item
|
|
394
394
|
sees it, so the list carries no lift state for the gesture, and the coordinator marks the item
|
|
395
|
-
instead
|
|
396
|
-
|
|
395
|
+
instead. Styling keyed off either attribute therefore behaves the same whether the collection is
|
|
396
|
+
windowed or not.
|
|
397
397
|
|
|
398
398
|
The `data-for-drag-preview` row is also the supported hook for **keeping the clone out of element
|
|
399
399
|
queries**. The default preview is a `cloneNode(true)` copy appended to `document.body`, so for the
|
|
@@ -401,8 +401,7 @@ whole gesture — and past the drop, while a settle transition runs — it answe
|
|
|
401
401
|
selector (`[forDraggable]`, or a composed one such as `[forTableRow]`) and repeats its `data-index`.
|
|
402
402
|
`id` and `data-testid` are stripped from the clone and its whole subtree, so a hook that identifies
|
|
403
403
|
a single element stays unambiguous; anything that **enumerates** items by attribute selector during
|
|
404
|
-
a drag must filter the preview out with `:not([data-for-drag-preview])
|
|
405
|
-
([#1691](https://github.com/tutkli/forty-cdk/issues/1691)).
|
|
404
|
+
a drag must filter the preview out with `:not([data-for-drag-preview])`.
|
|
406
405
|
|
|
407
406
|
## Sortable list
|
|
408
407
|
|
package/drawer/README.md
CHANGED
|
@@ -272,7 +272,7 @@ Declaratively the same recipe is the four vetoable outputs on `[forDrawer]`: `(i
|
|
|
272
272
|
|
|
273
273
|
`ForDrawerCloseReason`: `'escape' | 'backdrop' | 'pointerDownOutside' | 'focusOutside' | 'closeButton' | 'swipe' | 'programmatic'`.
|
|
274
274
|
|
|
275
|
-
> **
|
|
275
|
+
> **The declarative and imperative surfaces spell this differently, on purpose.** The output is `(dismiss)` — an output named `close` would collide with the native DOM event and break any wrapper re-exposing it through `hostDirectives`. Nothing else changes name: the imperative handle method is `ForDrawerRef.close()`, the directive selector is `[forDrawerClose]`, and the payload type is `ForDrawerCloseReason`.
|
|
276
276
|
|
|
277
277
|
| Data attribute | Values |
|
|
278
278
|
| ------------------------ | -------------------------------------------- |
|
|
@@ -319,7 +319,7 @@ Three accepted shapes:
|
|
|
319
319
|
- `'NN%'` — equivalent to a fraction (`'50%' === 0.5`).
|
|
320
320
|
- `'NNpx'` — absolute pixel size measured from the anchored edge.
|
|
321
321
|
|
|
322
|
-
Pass them in **strictly increasing** order (closest-to-edge first)
|
|
322
|
+
Pass them in **strictly increasing** order (closest-to-edge first); the directive throws `FORCDK-DRAWER-009` otherwise. Mixed units (`'200px'` next to `0.5`) can only be ordered against the live drawer size, so they are re-checked on first measurement and fail with `FORCDK-DRAWER-010`, which names the offending point and the dimension it resolved against. `fadeFromIndex` must be a valid index into `snapPoints`.
|
|
323
323
|
|
|
324
324
|
```ts
|
|
325
325
|
[snapPoints] =
|
package/dropdown-menu/README.md
CHANGED
|
@@ -185,13 +185,13 @@ Once focus is in the menu, see [`menu/README.md`](../menu/README.md) for the in-
|
|
|
185
185
|
|
|
186
186
|
`[forDropdownMenu]` implements the [WAI-ARIA Menu Button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/). The trigger wires `aria-haspopup="menu"`, `aria-expanded`, and `aria-controls`; the menu surface and item roles come from the shared [`menu/`](../menu/README.md) primitives.
|
|
187
187
|
|
|
188
|
-
A disabled trigger (its own `[disabled]`, or the root's) reflects through a **single channel**: the native `disabled` attribute plus the `data-disabled` styling hook. No `aria-disabled` is emitted — the trigger is a real single-purpose `<button>` and the native attribute already conveys the state to assistive technology
|
|
188
|
+
A disabled trigger (its own `[disabled]`, or the root's) reflects through a **single channel**: the native `disabled` attribute plus the `data-disabled` styling hook. No `aria-disabled` is emitted — the trigger is a real single-purpose `<button>` and the native attribute already conveys the state to assistive technology. Style the disabled trigger off `[disabled]` or `[data-disabled]`, never `[aria-disabled]`.
|
|
189
189
|
|
|
190
190
|
## Styling
|
|
191
191
|
|
|
192
192
|
forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed under [Data attributes](#data-attributes).
|
|
193
193
|
|
|
194
|
-
> The menu content (`[forMenuContent]`) portals to `document.body`, so a class scoped to your trigger's component cannot reach it. Style it with **global CSS** or a class you pass through (see [Styling floating content](../../../docs/styling-floating-content.md)). The content host also exposes the shared positioner custom properties — `--for-anchor-width` / `--for-anchor-height`, `--for-available-width` / `--for-available-height`, and `--for-content-transform-origin` — documented in full in [Styling floating content](../../../docs/styling-floating-content.md).
|
|
194
|
+
> The menu content (`[forMenuContent]`) portals to `document.body`, so a class scoped to your trigger's component cannot reach it. Style it with **global CSS** or a class you pass through (see [Styling floating content](../../../docs/styling-floating-content.md)). The content host also exposes the shared positioner custom properties — `--for-floating-anchor-width` / `--for-floating-anchor-height`, `--for-floating-available-width` / `--for-floating-available-height`, and `--for-floating-content-transform-origin` — documented in full in [Styling floating content](../../../docs/styling-floating-content.md).
|
|
195
195
|
|
|
196
196
|
```css
|
|
197
197
|
.dropdown-menu-trigger .chevron {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, input, booleanAttribute, model, Directive, computed, signal, ElementRef } from '@angular/core';
|
|
3
|
-
import { assertRootContext, Collection, injectTextDirection, moveIndex, IdGenerator, adoptHostId, hostButtonType, registerHandle, reflectDisabled, resolveListNavigation, hostLabelledBy } from 'forty-cdk/core';
|
|
3
|
+
import { orphanContextError, assertRootContext, Collection, injectTextDirection, moveIndex, IdGenerator, adoptHostId, hostButtonType, registerHandle, reflectDisabled, resolveListNavigation, hostLabelledBy } from 'forty-cdk/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* DI token for the accordion's coordination surface, provided by `[forAccordion]`.
|
|
@@ -17,7 +17,12 @@ const FOR_ACCORDION_ITEM_CONTEXT = new InjectionToken('FOR_ACCORDION_ITEM_CONTEX
|
|
|
17
17
|
function injectAccordionContext(piece) {
|
|
18
18
|
const ctx = inject(FOR_ACCORDION_CONTEXT, { optional: true });
|
|
19
19
|
if (!ctx) {
|
|
20
|
-
throw
|
|
20
|
+
throw orphanContextError({
|
|
21
|
+
code: 'FORCDK-ACCORDION-001',
|
|
22
|
+
piece,
|
|
23
|
+
root: '[forAccordion]',
|
|
24
|
+
token: 'FOR_ACCORDION_CONTEXT',
|
|
25
|
+
});
|
|
21
26
|
}
|
|
22
27
|
assertRootContext({
|
|
23
28
|
entryPoint: 'accordion',
|
|
@@ -31,7 +36,12 @@ function injectAccordionContext(piece) {
|
|
|
31
36
|
function injectAccordionItemContext(piece) {
|
|
32
37
|
const ctx = inject(FOR_ACCORDION_ITEM_CONTEXT, { optional: true });
|
|
33
38
|
if (!ctx) {
|
|
34
|
-
throw
|
|
39
|
+
throw orphanContextError({
|
|
40
|
+
code: 'FORCDK-ACCORDION-002',
|
|
41
|
+
piece,
|
|
42
|
+
root: '[forAccordionItem]',
|
|
43
|
+
token: 'FOR_ACCORDION_ITEM_CONTEXT',
|
|
44
|
+
});
|
|
35
45
|
}
|
|
36
46
|
return ctx;
|
|
37
47
|
}
|
|
@@ -186,9 +196,8 @@ class ForAccordionItem {
|
|
|
186
196
|
* When true, this item's trigger ignores clicks and reflects the native
|
|
187
197
|
* `disabled` attribute (not `aria-disabled`): dropped from the Tab order and
|
|
188
198
|
* skipped by arrow-key navigation, but kept in the accessibility tree so
|
|
189
|
-
* screen readers still announce it. See `ForAccordionTrigger` for the
|
|
190
|
-
*
|
|
191
|
-
* {@link disabled} for state.
|
|
199
|
+
* screen readers still announce it. See `ForAccordionTrigger` for the rationale. Bind via
|
|
200
|
+
* `[disabled]`; read the composed {@link disabled} for state.
|
|
192
201
|
*/
|
|
193
202
|
disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), transform: booleanAttribute, alias: 'disabled' });
|
|
194
203
|
/**
|
|
@@ -264,7 +273,7 @@ class ForAccordionTrigger {
|
|
|
264
273
|
/**
|
|
265
274
|
* APG: aria-disabled is true only when the panel is open AND the accordion
|
|
266
275
|
* disallows collapse. A real `disabled` item is reflected via the native
|
|
267
|
-
* `disabled` attribute instead — the sanctioned exception
|
|
276
|
+
* `disabled` attribute instead — the sanctioned exception to that rule:
|
|
268
277
|
* the trigger is a real single-purpose `<button>`, not a roving collection
|
|
269
278
|
* item (every trigger stays independently in the Tab order; the arrow-key
|
|
270
279
|
* navigation is an APG-optional enhancement layered on top, not a
|