forty-cdk 0.36.0 → 0.38.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 +4 -4
- package/accordion/README.md +8 -8
- package/avatar/README.md +1 -1
- package/breadcrumbs/README.md +2 -2
- package/button/README.md +12 -6
- package/calendar/README.md +16 -13
- package/checkbox/README.md +7 -3
- package/combobox/README.md +30 -7
- package/context-menu/README.md +9 -3
- package/date-field/README.md +25 -16
- package/date-picker/README.md +47 -28
- package/dialog/README.md +1 -1
- package/drag-drop/README.md +56 -8
- package/drawer/README.md +26 -21
- package/dropdown-menu/README.md +6 -1
- package/fesm2022/forty-cdk-button.mjs +5 -4
- package/fesm2022/forty-cdk-button.mjs.map +1 -1
- package/fesm2022/forty-cdk-calendar.mjs +96 -39
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
- package/fesm2022/forty-cdk-combobox.mjs +54 -27
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-context-menu.mjs +58 -12
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-core-overlay.mjs +134 -45
- package/fesm2022/forty-cdk-core-overlay.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +225 -55
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs +6 -4
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-defaults.mjs.map +1 -1
- package/fesm2022/forty-cdk-dialog.mjs +3 -1
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
- package/fesm2022/forty-cdk-drag-drop.mjs +289 -63
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
- package/fesm2022/forty-cdk-drawer.mjs +125 -48
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
- package/fesm2022/forty-cdk-field.mjs +1 -1
- package/fesm2022/forty-cdk-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +62 -18
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs +10 -20
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-number-input.mjs +18 -11
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -1
- package/fesm2022/forty-cdk-pagination.mjs +8 -4
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
- package/fesm2022/forty-cdk-pane-resizer.mjs +2 -2
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-search.mjs +16 -1
- package/fesm2022/forty-cdk-search.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +22 -6
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
- package/fesm2022/forty-cdk-table-virtualization.mjs +29 -9
- package/fesm2022/forty-cdk-table-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +92 -28
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs +58 -27
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-toast.mjs +5 -1
- package/fesm2022/forty-cdk-toast.mjs.map +1 -1
- package/fesm2022/forty-cdk-tooltip.mjs +7 -2
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
- package/fesm2022/forty-cdk-tree.mjs +51 -36
- package/fesm2022/forty-cdk-tree.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtual-reorder.mjs +4 -17
- package/fesm2022/forty-cdk-virtual-reorder.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs +77 -14
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/field/README.md +9 -9
- package/fieldset/README.md +1 -1
- package/file-upload/README.md +1 -1
- package/hover-card/README.md +17 -13
- package/input/README.md +2 -2
- package/listbox/README.md +79 -89
- package/menu/README.md +4 -4
- package/menubar/README.md +6 -6
- package/navigation-menu/README.md +37 -34
- package/number-input/README.md +11 -7
- package/otp-input/README.md +2 -2
- package/package.json +1 -1
- package/pagination/README.md +2 -1
- package/pane-resizer/README.md +3 -3
- package/popover/README.md +34 -31
- package/progress/README.md +14 -5
- package/radio-group/README.md +9 -5
- package/scroll-area/README.md +1 -1
- package/search/README.md +12 -6
- package/select/README.md +24 -4
- package/stepper/README.md +8 -8
- package/switch/README.md +11 -11
- package/table/README.md +25 -17
- package/table-virtualization/README.md +3 -1
- package/time-field/README.md +25 -16
- package/time-picker/README.md +55 -36
- package/toast/README.md +1 -1
- package/toggle/README.md +13 -4
- package/toolbar/README.md +1 -1
- package/tooltip/README.md +26 -21
- package/tree/README.md +2 -0
- package/types/forty-cdk-button.d.ts +7 -1
- package/types/forty-cdk-calendar.d.ts +90 -28
- package/types/forty-cdk-combobox.d.ts +40 -24
- package/types/forty-cdk-context-menu.d.ts +14 -1
- package/types/forty-cdk-core-overlay.d.ts +74 -30
- package/types/forty-cdk-core.d.ts +85 -38
- package/types/forty-cdk-date-field.d.ts +6 -4
- package/types/forty-cdk-date-picker.d.ts +8 -6
- package/types/forty-cdk-defaults.d.ts +6 -5
- package/types/forty-cdk-dialog.d.ts +4 -1
- package/types/forty-cdk-drag-drop.d.ts +52 -8
- package/types/forty-cdk-drawer.d.ts +54 -31
- package/types/forty-cdk-field.d.ts +3 -4
- package/types/forty-cdk-listbox.d.ts +46 -18
- package/types/forty-cdk-navigation-menu.d.ts +33 -30
- package/types/forty-cdk-number-input.d.ts +15 -15
- package/types/forty-cdk-pagination.d.ts +8 -1
- package/types/forty-cdk-pane-resizer.d.ts +4 -3
- package/types/forty-cdk-popover.d.ts +3 -3
- package/types/forty-cdk-search.d.ts +8 -1
- package/types/forty-cdk-select.d.ts +19 -5
- package/types/forty-cdk-stepper.d.ts +11 -7
- package/types/forty-cdk-table-virtualization.d.ts +4 -3
- package/types/forty-cdk-table.d.ts +93 -35
- package/types/forty-cdk-time-field.d.ts +6 -5
- package/types/forty-cdk-time-picker.d.ts +38 -10
- package/types/forty-cdk-toast.d.ts +1 -0
- package/types/forty-cdk-tooltip.d.ts +32 -23
- package/types/forty-cdk-virtual-reorder.d.ts +5 -3
- package/types/forty-cdk-virtualization.d.ts +58 -23
- package/virtual-reorder/README.md +7 -1
- package/virtualization/README.md +187 -30
package/README.md
CHANGED
|
@@ -26,10 +26,10 @@ Required:
|
|
|
26
26
|
|
|
27
27
|
Optional — install only if you use the matching entry point / primitives:
|
|
28
28
|
|
|
29
|
-
| Peer | Needed by
|
|
30
|
-
| ---------------------------------- |
|
|
31
|
-
| `@angular/forms` `^22.1.0` |
|
|
32
|
-
| `@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/date-adapter` — install this peer only when you import that entry point.
|
|
29
|
+
| Peer | Needed by |
|
|
30
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `@angular/forms` `^22.1.0` | The form-control entry points: `forty-cdk/checkbox`, `combobox`, `date-field`, `date-picker`, `input`, `listbox`, `number-input`, `otp-input`, `radio-group`, `search`, `select`, `slider`, `switch`, `time-field`, `time-picker` and `toggle`. Their controls implement `FormValueControl` / `FormCheckboxControl` from `@angular/forms/signals` for `[formField]` auto-wiring. The contract is type-only, so no published bundle references the package, but those entry points' type declarations do: a project that type-checks its dependencies (`skipLibCheck: false`) needs the peer as soon as it imports one of them. Every other entry point, `forty-cdk/field` and `forty-cdk/stepper` included, compiles without it. |
|
|
32
|
+
| `@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/date-adapter` — install this peer only when you import that entry point. |
|
|
33
33
|
|
|
34
34
|
### Regular dependencies
|
|
35
35
|
|
package/accordion/README.md
CHANGED
|
@@ -111,14 +111,14 @@ A disabled item cannot be toggled and is skipped by the arrow keys, while stayin
|
|
|
111
111
|
|
|
112
112
|
### `ForAccordion`
|
|
113
113
|
|
|
114
|
-
| Property | Type | Description
|
|
115
|
-
| ------------- | ----------------------------------- |
|
|
116
|
-
| `value` | `model<readonly string[]>` | Currently open item values. In single mode the array has 0 or 1 element.<br>**Default:** —
|
|
117
|
-
| `multiple` | `input<boolean>` | When true, multiple items can be open simultaneously.<br>**Default:** `false`
|
|
118
|
-
| `collapsible` | `input<boolean>` | Single mode only: when true, the open item can be collapsed by clicking it. Otherwise once any item is open, exactly one stays open.<br>**Default:** `false`
|
|
119
|
-
| `disabled` | `input<boolean>` | When true, disables every item: each trigger reflects the native `disabled` attribute and cannot toggle. Composes with a per-item `[disabled]`.<br>**Default:** `false`
|
|
120
|
-
| `orientation` | `input<'horizontal' \| 'vertical'>` | Layout direction of the trigger list. In horizontal mode ArrowLeft/Right replace ArrowUp/Down.<br>**Default:** `'vertical'`
|
|
121
|
-
| `dir` | `input<'ltr' \| 'rtl'>`
|
|
114
|
+
| Property | Type | Description |
|
|
115
|
+
| ------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
116
|
+
| `value` | `model<readonly string[]>` | Currently open item values. In single mode the array has 0 or 1 element.<br>**Default:** — |
|
|
117
|
+
| `multiple` | `input<boolean>` | When true, multiple items can be open simultaneously.<br>**Default:** `false` |
|
|
118
|
+
| `collapsible` | `input<boolean>` | Single mode only: when true, the open item can be collapsed by clicking it. Otherwise once any item is open, exactly one stays open.<br>**Default:** `false` |
|
|
119
|
+
| `disabled` | `input<boolean>` | When true, disables every item: each trigger reflects the native `disabled` attribute and cannot toggle. Composes with a per-item `[disabled]`.<br>**Default:** `false` |
|
|
120
|
+
| `orientation` | `input<'horizontal' \| 'vertical'>` | Layout direction of the trigger list. In horizontal mode ArrowLeft/Right replace ArrowUp/Down.<br>**Default:** `'vertical'` |
|
|
121
|
+
| `dir` | `input<'ltr' \| 'rtl' \| null>` | Writing direction. Only relevant in horizontal mode, where it swaps the meaning of Left/Right arrows. `null` inherits the nearest `[dir]` / `<html dir>`, else `'ltr'`.<br>**Default:** `null` |
|
|
122
122
|
|
|
123
123
|
| Data attribute | Values |
|
|
124
124
|
| ------------------ | -------------------------- |
|
package/avatar/README.md
CHANGED
|
@@ -22,7 +22,7 @@ It is headless and presentational: it tracks the load lifecycle of an `<img>` an
|
|
|
22
22
|
|
|
23
23
|
## Examples
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
The image loads, so `data-status` settles on `loaded` and the initials never render: `fallbackDelayMs` holds the fallback back for 500 ms, so a fast load never flashes it. Failed load below breaks the URL to show the `error` path.
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
28
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
package/breadcrumbs/README.md
CHANGED
|
@@ -126,10 +126,10 @@ Implements the [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/pat
|
|
|
126
126
|
|
|
127
127
|
## Styling
|
|
128
128
|
|
|
129
|
-
forty-cdk ships no styles
|
|
129
|
+
forty-cdk ships no styles: put your own class on each piece and key your CSS off the `aria-current="page"` the current item reflects, not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
|
|
130
130
|
|
|
131
131
|
```css
|
|
132
|
-
[
|
|
132
|
+
.bc-link[aria-current='page'] {
|
|
133
133
|
font-weight: bold;
|
|
134
134
|
color: inherit;
|
|
135
135
|
text-decoration: none;
|
package/button/README.md
CHANGED
|
@@ -58,20 +58,26 @@ Disabled buttons stay focusable so assistive technology can announce them. The n
|
|
|
58
58
|
A surrounding disabled `[forFieldset]` disables the button too: its `disabled` input is OR'd with the group's, so `aria-disabled` / `data-disabled` are reflected and activation is suppressed. This matters most on a non-native host (`<div forButton>`), which a native `<fieldset disabled>` cannot reach.
|
|
59
59
|
|
|
60
60
|
```html
|
|
61
|
-
<
|
|
62
|
-
<
|
|
61
|
+
<div forFieldset [disabled]="locked()">
|
|
62
|
+
<span forFieldsetLegend>Account</span>
|
|
63
63
|
<button forButton (activate)="save()">Save</button>
|
|
64
|
-
</
|
|
64
|
+
</div>
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
On a native `<fieldset>` host, `[forFieldset]` sets the native `disabled` attribute, and HTML then disables every native `<button>` inside it: the button leaves the tab order and cannot be focused, whatever `forButton` does. The guarantee that a disabled button stays focusable holds for a non-native group host like the `<div>` above, and for a non-native button host (`<div forButton>`) in either group.
|
|
68
|
+
|
|
67
69
|
## Preserve consumer `type`
|
|
68
70
|
|
|
69
|
-
A native `<button>` without an explicit `type` attribute defaults to `type="button"`. A consumer-set `type
|
|
71
|
+
A native `<button>` without an explicit `type` attribute defaults to `type="button"`. A consumer-set `type` is preserved, whether it is a static attribute or a binding, and later writes to a binding are followed:
|
|
70
72
|
|
|
71
73
|
```html
|
|
72
74
|
<button type="submit" forButton>Submit form</button>
|
|
75
|
+
|
|
76
|
+
<button [attr.type]="kind()" forButton>Save</button>
|
|
73
77
|
```
|
|
74
78
|
|
|
79
|
+
A binding that resolves to `null` removes the attribute, so the button falls back to the HTML default, which is `submit` inside a form.
|
|
80
|
+
|
|
75
81
|
## API
|
|
76
82
|
|
|
77
83
|
### `ForButton`
|
|
@@ -118,12 +124,12 @@ Implements the [WAI-ARIA Button pattern](https://www.w3.org/WAI/ARIA/apg/pattern
|
|
|
118
124
|
forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [API](#api), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
|
|
119
125
|
|
|
120
126
|
```css
|
|
121
|
-
[
|
|
127
|
+
.btn[data-disabled] {
|
|
122
128
|
opacity: 0.4;
|
|
123
129
|
pointer-events: none;
|
|
124
130
|
}
|
|
125
131
|
|
|
126
|
-
[
|
|
132
|
+
.btn[data-pressed] {
|
|
127
133
|
transform: scale(0.97);
|
|
128
134
|
}
|
|
129
135
|
```
|
package/calendar/README.md
CHANGED
|
@@ -188,7 +188,7 @@ export class CalendarDefaultExample {
|
|
|
188
188
|
|
|
189
189
|
### States
|
|
190
190
|
|
|
191
|
-
One class and one directive, three states. `disabled` turns off focus movement and selection for the whole calendar; `readonly` keeps days focusable and the grid still pages, but clicking or pressing `Enter` no longer changes the selection. Each reflects a root hook (`data-disabled` and `data-readonly`), and the example's stylesheet keys on nothing else.
|
|
191
|
+
One class and one directive, three states. `disabled` turns off focus movement and selection for the whole calendar; `readonly` keeps days focusable and the grid still pages, but clicking or pressing `Enter` no longer changes the selection, and a click moves only the focused day to the clicked cell. Each reflects a root hook (`data-disabled` and `data-readonly`), and the example's stylesheet keys on nothing else.
|
|
192
192
|
|
|
193
193
|
### Constraints & week start
|
|
194
194
|
|
|
@@ -253,11 +253,11 @@ Set `selectionMode="range"` and bind `[(range)]` to get date-range selection. In
|
|
|
253
253
|
|
|
254
254
|
**Interaction model.** Click (or `Enter` / `Space`) a first cell to set the anchor; the grid enters selecting state. Click (or `Enter` / `Space`) a second cell **in either direction** to commit the range. Clicking before the anchor commits the inverted band `[click, anchor]` (matching the hover preview) rather than starting over. There is no separate "start over" gesture and no explicit Escape-to-cancel: once a range is committed, the next click begins a fresh anchor, and `range` keeps the committed range until the second click replaces it.
|
|
255
255
|
|
|
256
|
-
**Keyboard in range mode.** `Enter` / `Space` on the focused cell sets the anchor on the first press and commits on the second (same key as single mode). While selecting, arrow / `Home` / `End` / `PageUp` / `PageDown` move the keyboard focus and update the preview cursor (the keyboard equivalent of pointer hover); moving before the anchor previews (and commits) the inverted band.
|
|
256
|
+
**Keyboard in range mode.** `Enter` / `Space` on the focused cell sets the anchor on the first press and commits on the second (same key as single mode). While selecting, arrow / `Home` / `End` / `PageUp` / `PageDown` move the keyboard focus and update the preview cursor (the keyboard equivalent of pointer hover); moving before the anchor previews (and commits) the inverted band. The preview follows whichever device moved last: a keyboard move, a selection or a page change takes the cursor back from a pointer resting on the grid, and the pointer takes it again when it enters another cell.
|
|
257
257
|
|
|
258
258
|
**`min` / `max` / `isDateUnavailable`** still gate both endpoints. An unavailable or out-of-bounds date cannot become an anchor or an end.
|
|
259
259
|
|
|
260
|
-
**`minRangeLength` / `maxRangeLength`.** Constrain the inclusive day count of the committed range. A click that would violate either limit
|
|
260
|
+
**`minRangeLength` / `maxRangeLength`.** Constrain the inclusive day count of the committed range. A click that would violate either limit commits nothing: the anchor is preserved, and the clicked cell becomes the focused day and the end of the preview. Both default to `null` (unconstrained).
|
|
261
261
|
|
|
262
262
|
**Cell facets (range mode).** Four boolean `data-*` attributes are added to `[forCalendarCell]`:
|
|
263
263
|
|
|
@@ -290,7 +290,7 @@ Set `selectionMode="range"` and bind `[(range)]` to get date-range selection. In
|
|
|
290
290
|
}
|
|
291
291
|
```
|
|
292
292
|
|
|
293
|
-
**`aria-selected`** in range mode is `"true"` across every committed-range cell (inclusive)
|
|
293
|
+
**`aria-selected`** in range mode is `"true"` across every committed-range cell (inclusive), and the grid carries `aria-multiselectable="true"`. While a new anchor is pending, `aria-selected` is `"false"` everywhere, but `range` keeps the previously committed range until the second click replaces it.
|
|
294
294
|
|
|
295
295
|
**Scope.** Range mode is day-granular only: `granularity` / time is orthogonal and not supported alongside it.
|
|
296
296
|
|
|
@@ -306,7 +306,7 @@ Set `selectionMode="range"` and bind `[(range)]` to get date-range selection. In
|
|
|
306
306
|
| `goToMonth(month)` | Set the visible month within the current visible year. `month` is **1-12**. |
|
|
307
307
|
| `goToYear(year)` | Set the visible year, keeping the current visible month. |
|
|
308
308
|
|
|
309
|
-
All three methods re-apply the user's intended day-of-month (clamped to the target month's length), clamp the result into `[min, max]`, and announce the new period politely when the
|
|
309
|
+
All three methods re-apply the user's intended day-of-month (clamped to the target month's length), clamp the result into `[min, max]`, and announce the new period politely when the period the active view shows changes. They keep DOM focus on the caller and do not move focus into the grid. They are a no-op while the calendar is `disabled`.
|
|
310
310
|
|
|
311
311
|
### Read accessors and predicates
|
|
312
312
|
|
|
@@ -395,15 +395,17 @@ The lower-level hooks (`visibleMonthNumber()`, `visibleYear()`, `monthOptions()`
|
|
|
395
395
|
|
|
396
396
|
`ForCalendar` supports two additional views (**month grid** and **year grid**) so users can jump quickly to a different month or year without paging one at a time. All three views share a single `[(view)]` model and the same `focusedDate` cursor.
|
|
397
397
|
|
|
398
|
+
The heading's `label()` follows the active view, with the same text as the view trigger: the month and year in day view, the year in month view, the block of years in year view. Every grid is named by the heading, so each one is named after the period it shows. A keyboard move or a prev / next press that changes that period announces it. Drilling down changes no value, so a `readonly` calendar still drills from a year to its months and from a month to its days.
|
|
399
|
+
|
|
398
400
|
### Pieces
|
|
399
401
|
|
|
400
|
-
| Class | Selector | Role
|
|
401
|
-
| ------------------------ | -------------------------- |
|
|
402
|
-
| `ForCalendarViewTrigger` | `[forCalendarViewTrigger]` | Button that cycles the view: day → month → year. Auto-disabled when the calendar is disabled.
|
|
403
|
-
| `ForCalendarMonthGrid` | `[forCalendarMonthGrid]` | 4×3 month grid (`role="grid"`). Exposes `rows()`, an array of `CalendarMonthRow`, each with three `
|
|
404
|
-
| `ForCalendarMonthCell` | `[forCalendarMonthCell]` | One month (`role="gridcell"`). Requires `[month]` (1–12). Click drills down to day view for that month.
|
|
405
|
-
| `ForCalendarYearGrid` | `[forCalendarYearGrid]` | 4×3 year grid (`role="grid"`). Exposes `rows()`, an array of `CalendarYearRow`, each with three `CalendarYearOption`.
|
|
406
|
-
| `ForCalendarYearCell` | `[forCalendarYearCell]` | One year (`role="gridcell"`). Requires `[year]`. Click drills down to month view for that year.
|
|
402
|
+
| Class | Selector | Role |
|
|
403
|
+
| ------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
404
|
+
| `ForCalendarViewTrigger` | `[forCalendarViewTrigger]` | Button that cycles the view: day → month → year. Auto-disabled when the calendar is disabled. |
|
|
405
|
+
| `ForCalendarMonthGrid` | `[forCalendarMonthGrid]` | 4×3 month grid (`role="grid"`). Exposes `rows()`, an array of `CalendarMonthRow`, each with three `CalendarMonthOption`. |
|
|
406
|
+
| `ForCalendarMonthCell` | `[forCalendarMonthCell]` | One month (`role="gridcell"`). Requires `[month]` (1–12). Click drills down to day view for that month. |
|
|
407
|
+
| `ForCalendarYearGrid` | `[forCalendarYearGrid]` | 4×3 year grid (`role="grid"`). Exposes `rows()`, an array of `CalendarYearRow`, each with three `CalendarYearOption`. |
|
|
408
|
+
| `ForCalendarYearCell` | `[forCalendarYearCell]` | One year (`role="gridcell"`). Requires `[year]`. Click drills down to month view for that year. |
|
|
407
409
|
|
|
408
410
|
### `view` model — `ForCalendar`
|
|
409
411
|
|
|
@@ -571,6 +573,7 @@ Implements the [WAI-ARIA Grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
|
571
573
|
- **`aria-labelledby`** wires the grid to the heading so it names the visible period. Paging the month is announced through a dedicated off-screen `aria-live="polite"` region (owned by `[forCalendar]`), so the period is read on navigation without the heading double-announcing as both a live region and the grid's label.
|
|
572
574
|
- **`aria-label`** on every cell carries the full localized date (e.g. `"Monday, June 15, 2026"`) so screen readers announce the whole date, not the bare day number that stays the cell's visible content. Outside-month padding days are suffixed (`" (outside month)"`) so they are distinguishable. Override the format via `ForCalendar`'s `dateLabel` input.
|
|
573
575
|
- **`aria-selected`** is always emitted (`"true"` / `"false"`); **`aria-current="date"`** marks today; **`aria-disabled`** marks unavailable dates (truthy-only).
|
|
576
|
+
- **`aria-multiselectable="true"`** is set on the day grid in `selectionMode="range"`, where a committed range selects several cells, and is absent in single mode.
|
|
574
577
|
- **Roving tabindex**: exactly one cell (the focused date) is tabbable. `Tab` enters and leaves the grid in one stop.
|
|
575
578
|
- **Boolean `data-*`** on the cell, present when true and absent when false: `data-selected`, `data-today`, `data-highlighted` (the focused/roving cell), `data-disabled` and `data-outside-month`.
|
|
576
579
|
|
|
@@ -609,7 +612,7 @@ import { CalendarDate, today, getLocalTimeZone } from '@internationalized/date';
|
|
|
609
612
|
@Component({
|
|
610
613
|
selector: 'app-date',
|
|
611
614
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
612
|
-
|
|
615
|
+
template: `<p>Today is {{ date()?.toString() }}</p>`,
|
|
613
616
|
})
|
|
614
617
|
export class DatePage {
|
|
615
618
|
readonly date = signal<CalendarDate | null>(null);
|
package/checkbox/README.md
CHANGED
|
@@ -134,12 +134,12 @@ One class and one directive, three states. `disabled` and `readonly` both keep t
|
|
|
134
134
|
|
|
135
135
|
```ts
|
|
136
136
|
import { Component, signal } from '@angular/core';
|
|
137
|
-
import { form, required } from '@angular/forms/signals';
|
|
137
|
+
import { form, FormField, required } from '@angular/forms/signals';
|
|
138
138
|
import { ForCheckbox } from 'forty-cdk/checkbox';
|
|
139
139
|
|
|
140
140
|
@Component({
|
|
141
141
|
selector: 'demo-checkout',
|
|
142
|
-
imports: [ForCheckbox
|
|
142
|
+
imports: [ForCheckbox, FormField],
|
|
143
143
|
template: `
|
|
144
144
|
<button forCheckbox class="cb-row" [formField]="checkout.acceptTerms">
|
|
145
145
|
<span class="cb">
|
|
@@ -166,13 +166,17 @@ export class DemoCheckout {
|
|
|
166
166
|
| `disabled` / `readonly` / `required` / `invalid` / `pending` | `input<boolean>` | Reflected as the matching `aria-*` / `data-*` attributes. A disabled checkbox stays focusable (per APG), with `aria-disabled="true"` + `data-disabled` and no native `disabled`; click is a no-op.<br>**Default:** — |
|
|
167
167
|
| `name` | `input<string>` | Reflected on `name` (empty string omits the attribute). A native reset of the owning `<form>` restores `checked` and `indeterminate` to their first-render values.<br>**Default:** `''` |
|
|
168
168
|
| `errors` | `input<readonly ValidationError.WithOptionalFieldTree[]>` | Validation errors fed by `[formField]`.<br>**Default:** — |
|
|
169
|
-
| `touched` | `model<boolean>` | Set to `true` on blur.<br>**Default:**
|
|
169
|
+
| `touched` | `model<boolean>` | Set to `true` on blur.<br>**Default:** `false` |
|
|
170
170
|
|
|
171
171
|
| Data attribute | Values |
|
|
172
172
|
| --------------- | ------------------------------------------- |
|
|
173
173
|
| `data-state` | `checked` \| `unchecked` \| `indeterminate` |
|
|
174
174
|
| `data-disabled` | present \| absent |
|
|
175
175
|
| `data-readonly` | present \| absent |
|
|
176
|
+
| `data-touched` | present \| absent |
|
|
177
|
+
| `data-dirty` | present \| absent |
|
|
178
|
+
| `data-pending` | present \| absent |
|
|
179
|
+
| `data-invalid` | present \| absent |
|
|
176
180
|
|
|
177
181
|
### `ForCheckboxIndicator`
|
|
178
182
|
|
package/combobox/README.md
CHANGED
|
@@ -196,7 +196,23 @@ The primitive never owns the scroll container, so it virtualizes with any window
|
|
|
196
196
|
|
|
197
197
|
## API
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
Only the positioning inputs every anchored root inherits are tabulated for this primitive so far. See the feature sections below for the other inputs and their prose descriptions.
|
|
200
|
+
|
|
201
|
+
### `ForCombobox` positioning inputs
|
|
202
|
+
|
|
203
|
+
| Property | Type | Description |
|
|
204
|
+
| --------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
205
|
+
| `side` | `input<FloatingSide>` | Anchor side (`'top'` / `'right'` / `'bottom'` / `'left'`). Falls back to `provideForComboboxDefaults`.<br>**Default:** `'bottom'` |
|
|
206
|
+
| `align` | `input<FloatingAlign>` | Alignment along `side` (`'start'` / `'center'` / `'end'`). Falls back to `provideForComboboxDefaults`.<br>**Default:** `'start'` |
|
|
207
|
+
| `sideOffset` | `input<number>` | Gap (px) between the anchor and the content along the main axis. Falls back to `provideForComboboxDefaults`.<br>**Default:** `4` |
|
|
208
|
+
| `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0` |
|
|
209
|
+
| `avoidCollisions` | `input<boolean>` | When `true`, `flip` and `shift` keep the content inside the viewport. Set `false` for strict positioning where overflow is acceptable.<br>**Default:** `true` |
|
|
210
|
+
| `collisionPadding` | `input<number>` | Padding (px) for the `flip` / `shift` / `size` collision middlewares. Falls back to `provideForComboboxDefaults`.<br>**Default:** `8` |
|
|
211
|
+
| `sticky` | `input<'partial' \| 'always' \| false>` | `'always'` keeps the requested placement even when the content leaves the viewport. `'partial'` and `false` let `shift` slide it back into view.<br>**Default:** `'partial'` |
|
|
212
|
+
| `hideWhenDetached` | `input<boolean>` | When `true`, the content carries `data-detached` while the anchor has scrolled out of every clipping ancestor, so CSS can hide it.<br>**Default:** `false` |
|
|
213
|
+
| `clipUntilPositioned` | `input<boolean>` | Clips the content until its first position resolves, so it never flashes at the viewport corner. Set `false` so an `animate.enter` plays from its first frame.<br>**Default:** `true` |
|
|
214
|
+
|
|
215
|
+
The content anchors to the field box when a `[forComboboxAnchor]` is present; see [Anchoring to a field box](#anchoring-to-a-field-box).
|
|
200
216
|
|
|
201
217
|
### Data attributes
|
|
202
218
|
|
|
@@ -205,6 +221,10 @@ Input tables are not yet tabulated for this primitive. See the feature sections
|
|
|
205
221
|
| `[forCombobox]` | `data-state` | `open` \| `closed` |
|
|
206
222
|
| `[forCombobox]` | `data-disabled` | present / absent |
|
|
207
223
|
| `[forCombobox]` | `data-readonly` | present / absent |
|
|
224
|
+
| `[forCombobox]` | `data-touched` | present / absent |
|
|
225
|
+
| `[forCombobox]` | `data-dirty` | present / absent |
|
|
226
|
+
| `[forCombobox]` | `data-pending` | present / absent |
|
|
227
|
+
| `[forCombobox]` | `data-invalid` | present / absent |
|
|
208
228
|
| `[forComboboxInput]` | `data-state` | `open` \| `closed` |
|
|
209
229
|
| `[forComboboxInput]` | `data-disabled` | present / absent |
|
|
210
230
|
| `[forComboboxToggle]` | `data-state` | `open` \| `closed` |
|
|
@@ -363,7 +383,9 @@ An action:
|
|
|
363
383
|
- **never touches `value` / `options()`.** It registers in a collection separate
|
|
364
384
|
from options, so `options()`, `aria-setsize`, and `aria-posinset` are
|
|
365
385
|
unaffected and activation emits `(activate)` instead of mutating `[(value)]`. The
|
|
366
|
-
consumer decides what happens and whether to close the popup afterwards.
|
|
386
|
+
consumer decides what happens and whether to close the popup afterwards. In the
|
|
387
|
+
editable anatomy a close from `(activate)` returns focus to the input, unless
|
|
388
|
+
your handler already moved it out of the popup.
|
|
367
389
|
- **is `role="button"`, not `role="option"`.** Assistive tech announces it as an
|
|
368
390
|
action, not as one of N choices.
|
|
369
391
|
- **is reached by Tab, not the arrow keys** (see below), so it stays reachable no
|
|
@@ -395,10 +417,10 @@ outside-focus dismissal checks, exactly like the input.
|
|
|
395
417
|
|
|
396
418
|
### Action item API
|
|
397
419
|
|
|
398
|
-
| Member | Type | Notes
|
|
399
|
-
| ------------ | -------------- |
|
|
400
|
-
| `[disabled]` | `boolean` | Drops the action out of the focus ring (`tabindex`
|
|
401
|
-
| `(activate)` | `output<void>` | Fired on click / Enter / Space. Never mutates `[(value)]`.
|
|
420
|
+
| Member | Type | Notes |
|
|
421
|
+
| ------------ | -------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
422
|
+
| `[disabled]` | `boolean` | Drops the action out of the focus ring (it keeps `tabindex="-1"`), reflects `aria-disabled`, ignores activation. |
|
|
423
|
+
| `(activate)` | `output<void>` | Fired on click / Enter / Space. Never mutates `[(value)]`. |
|
|
402
424
|
|
|
403
425
|
`[forComboboxAction]` host-binds `role="button"`, `type="button"` (on a native
|
|
404
426
|
`<button>` host only, leaving any other element with no `type`), a primitive-managed
|
|
@@ -575,7 +597,7 @@ Each dismiss reason emits a vetoable event from `[forCombobox]`. Call `preventDe
|
|
|
575
597
|
|
|
576
598
|
The two anatomies have different focus models:
|
|
577
599
|
|
|
578
|
-
- **Editable anatomy**: the input retains focus the entire time the listbox is open and on close; the active option is tracked via `aria-activedescendant`, never via `.focus()`.
|
|
600
|
+
- **Editable anatomy**: the input retains focus the entire time the listbox is open and on close; the active option is tracked via `aria-activedescendant`, never via `.focus()`. The one automatic move is back to the input, when a close unmounts the popup while focus is inside it (a `[forComboboxAction]` whose `(activate)` closes the popup), gated by `[returnFocus]`. `(autoFocusOnOpen)` / `(autoFocusOnClose)` never fire. If you need to move focus elsewhere, do it from your own keydown handler. The combobox won't fight you.
|
|
579
601
|
- **Picker anatomy**: focus moves into the input on open and returns to the `[forComboboxTrigger]` on close, exactly like the other trigger-anchored overlays (`[forPopover]`, `[forDropdownMenu]`, `[forSelect]`). Both moves emit a vetoable event on `[forCombobox]`; call `preventDefault()` to keep focus where it is:
|
|
580
602
|
|
|
581
603
|
| Output | When | `preventDefault()` effect |
|
|
@@ -822,6 +844,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
822
844
|
- `[forComboboxTrigger]` (picker anatomy) is a real `<button>` reflecting `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` (the dialog surface, while open), an `id` (a static one is kept), and native `disabled` from the combobox's effective disabled. The popup is a dialog rather than a listbox because focus moves into the search input inside it. It is exempt from the popup's outside-pointer dismissal layer, like the input.
|
|
823
845
|
- `[forComboboxContent]` and `[forComboboxList]` cancel a `mousedown` that would otherwise move focus onto the surface or blur the input, so a press between options leaves focus in the input. A press on a focusable descendant is let through.
|
|
824
846
|
- `[forComboboxToggle]` (editable anatomy) is a real `<button>` with `tabindex="-1"`, a localizable `aria-label`, `aria-expanded`, `aria-controls` (the listbox, while open) and native `disabled`. It cancels `mousedown` so focus stays in the input, and it is exempt from the outside-pointer dismissal layer.
|
|
847
|
+
- `[forComboboxClear]` has `tabindex="-1"` and a localizable `aria-label`. It cancels `mousedown` too, and is exempt from the outside-pointer dismissal layer, so clearing an open listbox leaves it open with focus in the input and the combobox untouched.
|
|
825
848
|
- In single mode, `aria-selected="true"` follows the activedescendant (the option Enter would activate). In multi mode it follows membership in `value()`, so every selected option carries `aria-selected="true"` simultaneously.
|
|
826
849
|
- `data-state="checked" | "unchecked"` always reflects membership in `value()`, so consumers can paint a checkmark icon with pure CSS regardless of mode.
|
|
827
850
|
- `data-highlighted=""` marks the option that is the current `aria-activedescendant`. Because focus stays on the `<input>`, there is no `:focus` on the option to style. `data-highlighted` is the canonical CSS hook.
|
package/context-menu/README.md
CHANGED
|
@@ -88,8 +88,13 @@ The same menu vocabulary the Dropdown Menu exposes, anchored to the pointer on r
|
|
|
88
88
|
| `sideOffset` | `input<number>` | Gap (px) between the pointer and the menu along the main axis.<br>**Default:** `0` |
|
|
89
89
|
| `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0` |
|
|
90
90
|
| `fallbackAxisSideDirection` | `input<'none' \| 'start' \| 'end'>` | When both sides of the preferred axis overflow, lets `flip` drop the menu to a perpendicular side instead of clipping. `'none'` keeps only the opposite same-axis placement. The default is read from `provideForContextMenuDefaults` for the surrounding scope. Set it once for the whole app rather than per call site.<br>**Default:** `'none'` |
|
|
91
|
+
| `avoidCollisions` | `input<boolean>` | When `true`, `flip` and `shift` keep the content inside the viewport. Set `false` for strict positioning where overflow is acceptable.<br>**Default:** `true` |
|
|
92
|
+
| `collisionPadding` | `input<number>` | Padding (px) for the `flip` / `shift` / `size` collision middlewares. The default is read from `provideForContextMenuDefaults` for the surrounding scope.<br>**Default:** `8` |
|
|
93
|
+
| `sticky` | `input<'partial' \| 'always' \| false>` | `'always'` keeps the requested placement even when the content leaves the viewport. `'partial'` and `false` let `shift` slide it back into view.<br>**Default:** `'partial'` |
|
|
94
|
+
| `hideWhenDetached` | `input<boolean>` | When `true`, the content carries `data-detached` while the anchor has scrolled out of every clipping ancestor, so CSS can hide it.<br>**Default:** `false` |
|
|
95
|
+
| `clipUntilPositioned` | `input<boolean>` | Clips the content until its first position resolves, so it never flashes at the viewport corner. Set `false` so an `animate.enter` plays from its first frame.<br>**Default:** `true` |
|
|
91
96
|
| `loop` | `input<boolean>` | Whether arrow navigation wraps.<br>**Default:** `true` |
|
|
92
|
-
| `dir` | `input<
|
|
97
|
+
| `dir` | `input<'ltr' \| 'rtl' \| null>` | Writing direction. `null` inherits the nearest `[dir]` / `<html dir>`, else `'ltr'`. In RTL the swap is automatic: ArrowLeft opens submenus and ArrowRight closes them. Inherited by every nested `[forMenuSub]` underneath unless overridden.<br>**Default:** `null` |
|
|
93
98
|
| `disabled` | `input<boolean>` | When `true`, the contextmenu event falls through to the native browser menu.<br>**Default:** `false` |
|
|
94
99
|
| `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true` |
|
|
95
100
|
| `returnFocus` | `input<boolean>` | When `true`, focus returns on close to the trigger element, or to the focused element inside it that a keyboard activation started from.<br>**Default:** `true` |
|
|
@@ -134,10 +139,11 @@ forty-cdk ships no styles: put your own class on each piece and key your CSS off
|
|
|
134
139
|
|
|
135
140
|
## Behavior notes
|
|
136
141
|
|
|
137
|
-
- **Trigger is NOT exempt** from outside-pointer / outside-focus checks. Unlike DropdownMenu (where the trigger button toggles via its own click handler), the context-menu region is treated like any other "outside" element: a left-click on the region while the menu is open closes it. Right-clicking again
|
|
142
|
+
- **Trigger is NOT exempt** from outside-pointer / outside-focus checks. Unlike DropdownMenu (where the trigger button toggles via its own click handler), the context-menu region is treated like any other "outside" element: a left-click on the region while the menu is open closes it. Right-clicking again reopens the menu at the new position. When the close and the reopen land in the same task, as they do where the browser fires `contextmenu` on mouse down (macOS, Linux), the mounted surface is reused rather than rebuilt: it moves to the new point without replaying `animate.enter` / `animate.leave`, and focus moves back to its first item, honouring `(autoFocusOnOpen)`. Where `contextmenu` follows mouse up (Windows), the surface renders closed in between and is mounted again, so its animations replay. Keep enter/leave animations cheap, or gate expensive ones, since a user can fire this rapidly.
|
|
143
|
+
- **Nested regions.** A `[forContextMenuTrigger]` inside another one takes precedence: a right-click, `Shift+F10`, the `ContextMenu` key or a long-press on the inner region opens the inner menu only, and the outer menu opens from the parts of its region the inner one does not cover. A disabled inner region lets the outer menu open. The trigger also ignores a `contextmenu` or key press that a handler beneath it already cancelled with `preventDefault()`, so calling it on a descendant is how you keep a context menu from opening there.
|
|
138
144
|
- **Virtual anchor.** Right-click captures a 0×0 rect at the pointer location. `Shift+F10` and `ContextMenu` snapshot the bounding rect of the focused element (or the trigger if focus is on it directly), so the menu floats off the element under attention. Closing a keyboard-opened menu returns focus to that same element, so a row in a roving-tabindex list keeps its position; when a menu action removed it, focus falls back to the region. Both forms feed floating-ui's `flip` and `shift` middleware, so corners and screen edges work without special-casing.
|
|
139
145
|
- **Keyboard activators only fire while focus is inside the trigger.** Keyboard events dispatch to the focused element, so `Shift+F10` / `ContextMenu` anywhere outside the trigger goes to the browser default. The trigger is focusable by default (host-bound `tabindex="-1"`), so this works out of the box and focus can return to it programmatically when the menu closes. Your own `tabindex` wins over that default: use `tabindex="0"` if you want the region itself reachable via Tab.
|
|
140
|
-
- **Native menu suppressed.** The trigger calls `event.preventDefault()` on `contextmenu` and on the keyboard activators. Set `disabled` to let the browser's native menu surface for that region.
|
|
146
|
+
- **Native menu suppressed.** The trigger calls `event.preventDefault()` on `contextmenu` and on the keyboard activators. After a keyboard open it also cancels the `contextmenu` the platform raises when the key is released (Windows), wherever focus has moved by then, so the browser's menu is not painted over the one that just opened. Set `disabled` to let the browser's native menu surface for that region.
|
|
141
147
|
- **An open that renders nothing closes again.** When a right-click, `Shift+F10`, the `ContextMenu` key or a long-press opens a menu whose template renders no `[forMenuContent]`, the trigger closes it after the next render with reason `'programmatic'`, and dev mode logs `FORCDK-CONTEXT-MENU-002` once. The native menu cannot come back for that gesture, because it was already suppressed, so bind `[disabled]` to the same condition that renders the content: `[disabled]="!hasActions(col)"` beside `@if (hasActions(col))`.
|
|
142
148
|
- **Touch long-press.** The trigger runs its own long-press timer (a `touch` `pointerdown` held ~500 ms, without lifting or moving past a small tolerance, opens the menu at the touch point). This is required because iOS Safari never fires the `contextmenu` event a long-press synthesizes elsewhere; where the browser does synthesize it (Android, desktop touch emulation) the two paths stay mutually exclusive, so the menu opens exactly once. For the press to survive on iOS, suppress the native callout / text-selection on the trigger with CSS, since otherwise the OS gesture cancels the press:
|
|
143
149
|
|
package/date-field/README.md
CHANGED
|
@@ -87,7 +87,7 @@ export class DateFieldDefaultExample {
|
|
|
87
87
|
|
|
88
88
|
### Date & time
|
|
89
89
|
|
|
90
|
-
With a time-capable adapter, a `granularity`
|
|
90
|
+
With a time-capable adapter, a `granularity` finer than `'day'` appends hour / minute segments and the value becomes a `CalendarDateTime`. A 12-hour cycle adds an AM/PM segment you toggle with `↑` / `↓` or by typing `a` / `p`.
|
|
91
91
|
|
|
92
92
|
### Localized segment labels
|
|
93
93
|
|
|
@@ -99,14 +99,14 @@ With a time-capable adapter, a `granularity` coarser than `'day'` appends hour /
|
|
|
99
99
|
|
|
100
100
|
```ts
|
|
101
101
|
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
102
|
-
import { form } from '@angular/forms/signals';
|
|
102
|
+
import { form, FormField } from '@angular/forms/signals';
|
|
103
103
|
import { CalendarDate } from '@internationalized/date';
|
|
104
104
|
import { ForDateField, ForDateFieldLiteral, ForDateFieldSegment } from 'forty-cdk/date-field';
|
|
105
105
|
|
|
106
106
|
@Component({
|
|
107
107
|
selector: 'app-dob-form',
|
|
108
108
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
109
|
-
imports: [ForDateField, ForDateFieldSegment, ForDateFieldLiteral],
|
|
109
|
+
imports: [ForDateField, ForDateFieldSegment, ForDateFieldLiteral, FormField],
|
|
110
110
|
template: `
|
|
111
111
|
<div
|
|
112
112
|
forDateField
|
|
@@ -139,7 +139,7 @@ export class DobFormField {
|
|
|
139
139
|
|
|
140
140
|
### Date & time range
|
|
141
141
|
|
|
142
|
-
With a time-capable adapter, a `granularity`
|
|
142
|
+
With a time-capable adapter, a `granularity` finer than `'day'` appends time segments to both endpoints and the value becomes a `CalendarDateTime` range, which is handy for a check-in to check-out with times. A 12-hour `hourCycle` adds an AM/PM segment to each side.
|
|
143
143
|
|
|
144
144
|
### Range in Signal Forms
|
|
145
145
|
|
|
@@ -154,7 +154,7 @@ With a time-capable adapter, a `granularity` coarser than `'day'` appends time s
|
|
|
154
154
|
| `value` | `model<D \| null>` | Two-way bindable entered date, or `null` while any segment is empty. The `FormValueControl` backing.<br>**Default:** `null` |
|
|
155
155
|
| `minDate` | `input<D \| null>` | Minimum date (inclusive). A composed value below it is clamped up. Named `minDate` (see note below).<br>**Default:** `null` |
|
|
156
156
|
| `maxDate` | `input<D \| null>` | Maximum date (inclusive). A composed value above it is clamped down.<br>**Default:** `null` |
|
|
157
|
-
| `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision. `'day'` is date-only;
|
|
157
|
+
| `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision. `'day'` is date-only; a finer granularity appends time segments. See below.<br>**Default:** `'day'` |
|
|
158
158
|
| `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the time segments. `null` → the scope's `hourCycle`, then the locale. 12-hour adds the AM/PM segment.<br>**Default:** `null` |
|
|
159
159
|
| `locale` | `input<string \| null>` | BCP 47 locale driving segment order, separators, and month name. `null` → the adapter's `locale()`, then the runtime locale.<br>**Default:** `null` |
|
|
160
160
|
| `placeholder` | `input<Partial<Record<SegmentType, string>>>` | Per-segment placeholder while empty. Unspecified parts fall back to the scope's `placeholder`, then to `dd` / `mm` / `yyyy` / `hh` / `mm` / `ss` / `--`.<br>**Default:** `{}` |
|
|
@@ -181,6 +181,11 @@ A native reset of the owning `<form>` returns `value` to what it held when the f
|
|
|
181
181
|
| `[forDateField]` | `data-disabled` | present \| absent |
|
|
182
182
|
| `[forDateField]` | `data-readonly` | present \| absent |
|
|
183
183
|
| `[forDateField]` | `data-empty` | present \| absent |
|
|
184
|
+
| `[forDateField]` | `data-required` | present \| absent |
|
|
185
|
+
| `[forDateField]` | `data-touched` | present \| absent |
|
|
186
|
+
| `[forDateField]` | `data-dirty` | present \| absent |
|
|
187
|
+
| `[forDateField]` | `data-pending` | present \| absent |
|
|
188
|
+
| `[forDateField]` | `data-invalid` | present \| absent |
|
|
184
189
|
| `[forDateFieldSegment]` | `data-highlighted` | present \| absent |
|
|
185
190
|
| `[forDateFieldSegment]` | `data-placeholder` | present \| absent |
|
|
186
191
|
| `[forDateFieldSegment]` | `data-disabled` | present \| absent |
|
|
@@ -284,7 +289,7 @@ readonly booking = form(this.model);
|
|
|
284
289
|
| `value` | `model<DateRange<D> \| null>` | Two-way bindable committed range, or `null` while incomplete or out of order. The `FormValueControl` backing.<br>**Default:** `null` |
|
|
285
290
|
| `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` |
|
|
286
291
|
| `maxDate` | `input<D \| null>` | Maximum date (inclusive) for both endpoints. A composed endpoint above it is clamped down.<br>**Default:** `null` |
|
|
287
|
-
| `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision shared by both endpoints. `'day'` is date-only;
|
|
292
|
+
| `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision shared by both endpoints. `'day'` is date-only; a finer granularity appends time segments.<br>**Default:** `'day'` |
|
|
288
293
|
| `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the time segments. `null` → the scope's `hourCycle`, then the locale. 12-hour adds the AM/PM segment.<br>**Default:** `null` |
|
|
289
294
|
| `locale` | `input<string \| null>` | BCP 47 locale driving segment order, separators, and month name. `null` → the adapter's `locale()`, then the runtime locale.<br>**Default:** `null` |
|
|
290
295
|
| `placeholder` | `input<Partial<Record<SegmentType, string>>>` | Per-segment placeholder while empty, applied to both endpoints. Unspecified parts fall back to the scope's `placeholder`.<br>**Default:** `{}` |
|
|
@@ -297,7 +302,7 @@ The endpoint groups each accept an `ariaLabel` input for their own group label,
|
|
|
297
302
|
|
|
298
303
|
> **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.
|
|
299
304
|
|
|
300
|
-
`[forDateRangeField]` reflects the same `data-disabled` / `data-readonly` / `data-empty`
|
|
305
|
+
`[forDateRangeField]` reflects the same root hooks as `[forDateField]` (`data-disabled` / `data-readonly` / `data-empty` / `data-required` and the four form-state hooks), 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.
|
|
301
306
|
|
|
302
307
|
### Ordering
|
|
303
308
|
|
|
@@ -315,14 +320,14 @@ Each endpoint is its own tab stop, so `Tab` moves start group → end group →
|
|
|
315
320
|
|
|
316
321
|
Key behavior applies per segment. Horizontal arrows mirror under `dir="rtl"`.
|
|
317
322
|
|
|
318
|
-
| Key | Behavior
|
|
319
|
-
| -------------------------- |
|
|
320
|
-
| **0–9** | Type the value; auto-advances to the next segment when full.
|
|
321
|
-
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps.
|
|
322
|
-
| **ArrowLeft / ArrowRight** | Move to the previous / next segment (no wrap).
|
|
323
|
-
| **Home / End** | Jump to the segment minimum / maximum.
|
|
324
|
-
| **Backspace** | Delete the last entered digit; the value becomes `null` when the last digit is removed.
|
|
325
|
-
| **Delete** | Clear the whole segment (the value becomes `null` until refilled).
|
|
323
|
+
| Key | Behavior |
|
|
324
|
+
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
325
|
+
| **0–9** | Type the value; auto-advances to the next segment when full. |
|
|
326
|
+
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. An empty date part seeds from today; an empty hour from its minimum (midnight, or 1 AM in 12-hour), minute and second from 0. |
|
|
327
|
+
| **ArrowLeft / ArrowRight** | Move to the previous / next segment (no wrap). |
|
|
328
|
+
| **Home / End** | Jump to the segment minimum / maximum. |
|
|
329
|
+
| **Backspace** | Delete the last entered digit; the value becomes `null` when the last digit is removed. |
|
|
330
|
+
| **Delete** | Clear the whole segment (the value becomes `null` until refilled). |
|
|
326
331
|
|
|
327
332
|
The day clamps to the current month's length (e.g. 31 → 28 in February), and a composed value is clamped into `[minDate, maxDate]`. While focus stays in the field its segments keep what was entered, so editing one part never rewrites the others; they show the clamped value once focus leaves. Stepping the hour skips a wall-clock hour the day does not have, such as a spring-forward DST gap.
|
|
328
333
|
|
|
@@ -336,18 +341,22 @@ Composes the [WAI-ARIA Spinbutton pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
336
341
|
- **Literals are `aria-hidden`** and never focusable, so assistive tech reads only the spinbutton segments.
|
|
337
342
|
- **Boolean `data-*`** on each segment, present when true and absent when false: `data-highlighted` (focused/roving), `data-placeholder` (empty), `data-disabled`, `data-readonly`.
|
|
338
343
|
- **`aria-readonly` belongs on the segments, not the group.** WAI-ARIA supports it on `role="spinbutton"` but not on `role="group"`, so each segment carries `aria-readonly="true"` while the group reflects the `data-readonly` styling hook only.
|
|
344
|
+
- **On-screen keyboards work.** A segment of a field that is neither disabled nor read-only is a `contenteditable="plaintext-only"` editing host with `inputmode="numeric"`, so tapping it on a touch device opens the numeric keypad (the AM/PM segment opens the text keyboard). What the keyboard or an input method types goes through the [Keyboard](#keyboard) map above and is never inserted into the segment, which keeps rendering `{{ seg.text }}`.
|
|
339
345
|
|
|
340
346
|
## Styling
|
|
341
347
|
|
|
342
|
-
The library is styleless, so style the boolean `data-*` hooks yourself: `[data-highlighted]` (the focused/roving segment), `[data-placeholder]` (empty), `[data-disabled]` and `[data-readonly]` on the segments, and `[data-empty]` / `[data-disabled]` / `[data-readonly]` on the root group.
|
|
348
|
+
The library is styleless, so style the boolean `data-*` hooks yourself: `[data-highlighted]` (the focused/roving segment), `[data-placeholder]` (empty), `[data-disabled]` and `[data-readonly]` on the segments, and `[data-empty]` / `[data-disabled]` / `[data-readonly]` / `[data-required]` plus the form-state hooks `[data-touched]` / `[data-dirty]` / `[data-pending]` / `[data-invalid]` on the root group.
|
|
343
349
|
|
|
344
350
|
forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [Data attributes](#data-attributes), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
|
|
345
351
|
|
|
352
|
+
A segment is an editing host, so the browser paints a text caret in the focused one. Hide it with `caret-color: transparent` when your highlight already marks that segment.
|
|
353
|
+
|
|
346
354
|
```css
|
|
347
355
|
.date-field-segment[data-placeholder] {
|
|
348
356
|
color: GrayText;
|
|
349
357
|
}
|
|
350
358
|
.date-field-segment[data-highlighted] {
|
|
359
|
+
caret-color: transparent;
|
|
351
360
|
background: Highlight;
|
|
352
361
|
color: HighlightText;
|
|
353
362
|
}
|