forty-cdk 0.39.0 → 0.40.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 +12 -11
- package/accordion/README.md +17 -12
- package/aspect-ratio/README.md +1 -1
- package/avatar/README.md +19 -0
- package/breadcrumbs/README.md +25 -20
- package/breakpoints/README.md +17 -25
- package/calendar/README.md +8 -2
- package/carousel/README.md +37 -30
- package/combobox/README.md +37 -2
- package/context-menu/README.md +25 -1
- package/date-field/README.md +7 -3
- package/date-picker/README.md +30 -1
- package/defaults/README.md +1 -1
- package/dialog/README.md +33 -1
- package/drag-drop/README.md +61 -22
- package/drawer/README.md +27 -1
- package/dropdown-menu/README.md +24 -0
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
- package/fesm2022/forty-cdk-combobox.mjs +18 -50
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-core-overlay.mjs +7 -3
- package/fesm2022/forty-cdk-core-overlay.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +3 -12
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-field.mjs +4 -9
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-defaults.mjs +4 -4
- package/fesm2022/forty-cdk-defaults.mjs.map +1 -1
- package/fesm2022/forty-cdk-dialog.mjs +8 -8
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
- package/fesm2022/forty-cdk-drag-drop.mjs +53 -46
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
- package/fesm2022/forty-cdk-drawer.mjs +46 -58
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
- package/fesm2022/forty-cdk-id-salt.mjs +14 -0
- package/fesm2022/forty-cdk-id-salt.mjs.map +1 -0
- package/fesm2022/forty-cdk-internationalized-date.mjs +10 -4
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-menu.mjs +32 -46
- package/fesm2022/forty-cdk-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-progress.mjs +4 -1
- package/fesm2022/forty-cdk-progress.mjs.map +1 -1
- package/fesm2022/forty-cdk-scroll-area.mjs +14 -3
- package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +18 -50
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-separator.mjs.map +1 -1
- package/fesm2022/forty-cdk-shared.mjs +2 -1
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +4 -1
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-testing.mjs +35 -13
- package/fesm2022/forty-cdk-testing.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-field.mjs +9 -11
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-toast.mjs +8 -4
- package/fesm2022/forty-cdk-toast.mjs.map +1 -1
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -1
- package/fesm2022/forty-cdk-tree.mjs +106 -63
- package/fesm2022/forty-cdk-tree.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtual-reorder.mjs +1 -1
- package/fesm2022/forty-cdk-virtual-reorder.mjs.map +1 -1
- package/file-upload/README.md +12 -12
- package/hover-card/README.md +7 -1
- package/id-salt/README.md +41 -0
- package/listbox/README.md +34 -21
- package/menu/README.md +73 -35
- package/menubar/README.md +25 -1
- package/navigation-menu/README.md +123 -12
- package/number-input/README.md +19 -0
- package/package.json +5 -1
- package/pagination/README.md +39 -16
- package/popover/README.md +8 -8
- package/progress/README.md +41 -9
- package/radio-group/README.md +19 -0
- package/scroll-area/README.md +35 -6
- package/search/README.md +23 -9
- package/select/README.md +13 -11
- package/separator/README.md +2 -2
- package/shared/README.md +6 -6
- package/slider/README.md +19 -0
- package/stepper/README.md +30 -14
- package/tabs/README.md +39 -15
- package/testing/README.md +1 -1
- package/time-field/README.md +7 -3
- package/toast/README.md +6 -2
- package/toggle/README.md +87 -67
- package/toolbar/README.md +19 -0
- package/tree/README.md +45 -44
- package/types/forty-cdk-calendar.d.ts +8 -2
- package/types/forty-cdk-combobox.d.ts +8 -24
- package/types/forty-cdk-context-menu.d.ts +4 -13
- package/types/forty-cdk-core-overlay.d.ts +74 -64
- package/types/forty-cdk-core.d.ts +15 -77
- package/types/forty-cdk-date-field.d.ts +9 -16
- package/types/forty-cdk-defaults.d.ts +33 -33
- package/types/forty-cdk-drag-drop.d.ts +10 -6
- package/types/forty-cdk-drawer.d.ts +2 -1
- package/types/forty-cdk-dropdown-menu.d.ts +3 -11
- package/types/forty-cdk-file-upload.d.ts +6 -4
- package/types/forty-cdk-id-salt.d.ts +40 -0
- package/types/forty-cdk-internationalized-date.d.ts +10 -6
- package/types/forty-cdk-menu.d.ts +25 -38
- package/types/forty-cdk-popover.d.ts +8 -2
- package/types/forty-cdk-progress.d.ts +9 -10
- package/types/forty-cdk-scroll-area.d.ts +49 -10
- package/types/forty-cdk-select.d.ts +23 -41
- package/types/forty-cdk-separator.d.ts +1 -1
- package/types/forty-cdk-shared.d.ts +2 -1
- package/types/forty-cdk-stepper.d.ts +14 -9
- package/types/forty-cdk-table.d.ts +1 -1
- package/types/forty-cdk-testing.d.ts +38 -5
- package/types/forty-cdk-time-field.d.ts +9 -16
- package/types/forty-cdk-time-picker.d.ts +15 -6
- package/types/forty-cdk-toast.d.ts +37 -18
- package/types/forty-cdk-toolbar.d.ts +6 -16
- package/types/forty-cdk-tree.d.ts +40 -20
- package/types/forty-cdk-virtual-reorder.d.ts +5 -1
- package/virtual-reorder/README.md +2 -0
- package/visually-hidden/README.md +16 -3
package/README.md
CHANGED
|
@@ -42,18 +42,19 @@ Two packages are regular dependencies, installed automatically and never declare
|
|
|
42
42
|
|
|
43
43
|
## Entry points
|
|
44
44
|
|
|
45
|
-
**The package name itself exports nothing.** `import { … } from 'forty-cdk'` resolves to no symbol, and your editor will not auto-import anything under the bare package name — by design, so that the bare name never becomes a second import path for a symbol. There are
|
|
45
|
+
**The package name itself exports nothing.** `import { … } from 'forty-cdk'` resolves to no symbol, and your editor will not auto-import anything under the bare package name — by design, so that the bare name never becomes a second import path for a symbol. There are seven specifiers you do import from:
|
|
46
46
|
|
|
47
|
-
| Specifier | What it exports
|
|
48
|
-
| ---------------------------------------- |
|
|
49
|
-
| `forty-cdk/<primitive>` | The primitive's directives, components, context tokens and defaults provider — `ForDialog` from `forty-cdk/dialog`, `ForAccordion` from `forty-cdk/accordion`, and so on for every entry in the tables below.
|
|
50
|
-
| [`forty-cdk/shared`](shared) | The cross-primitive contract types a primitive's public API references — `WritingDirection`, `VetoableEvent`, `DateAdapter`, `FloatingSide`, … — declared once and published once. Eight ship from their own primitive instead; that README names them.
|
|
51
|
-
| `forty-cdk/internationalized-date` | The `@internationalized/date` adapters, kept apart so that optional peer stays genuinely optional.
|
|
52
|
-
| [`forty-cdk/defaults`](defaults) | Every `provideFor<Primitive>Defaults` with its token and interface. Each primitive re-exports its own pair too, but a root provider imported from the primitive loads the primitive at startup; imported from here, it does not.
|
|
53
|
-
| [`forty-cdk/date-adapter`](date-adapter) | The `DateAdapter` contract, the `FOR_DATE_ADAPTER` token and `provideNativeDateAdapter`. `forty-cdk/shared` re-exports them, but a root provider imported from there loads the code every primitive shares at startup; imported from here, it does not.
|
|
54
|
-
| [`forty-cdk/
|
|
47
|
+
| Specifier | What it exports |
|
|
48
|
+
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `forty-cdk/<primitive>` | The primitive's directives, components, context tokens and defaults provider — `ForDialog` from `forty-cdk/dialog`, `ForAccordion` from `forty-cdk/accordion`, and so on for every entry in the tables below. |
|
|
50
|
+
| [`forty-cdk/shared`](shared) | The cross-primitive contract types a primitive's public API references — `WritingDirection`, `VetoableEvent`, `DateAdapter`, `FloatingSide`, … — declared once and published once. Eight ship from their own primitive instead; that README names them. |
|
|
51
|
+
| `forty-cdk/internationalized-date` | The `@internationalized/date` adapters, kept apart so that optional peer stays genuinely optional. |
|
|
52
|
+
| [`forty-cdk/defaults`](defaults) | Every `provideFor<Primitive>Defaults` with its token and interface. Each primitive re-exports its own pair too, but a root provider imported from the primitive loads the primitive at startup; imported from here, it does not. |
|
|
53
|
+
| [`forty-cdk/date-adapter`](date-adapter) | The `DateAdapter` contract, the `FOR_DATE_ADAPTER` token and `provideNativeDateAdapter`. `forty-cdk/shared` re-exports them, but a root provider imported from there loads the code every primitive shares at startup; imported from here, it does not. |
|
|
54
|
+
| [`forty-cdk/id-salt`](id-salt) | `FOR_ID_SALT` and `provideForIdSalt`, the per-app salt for the ids the primitives generate when several forty-cdk apps share a page. `forty-cdk/shared` re-exports them, but a root provider imported from there loads the code every primitive shares at startup; imported from here, it does not. |
|
|
55
|
+
| [`forty-cdk/testing`](testing) | Spec helpers: keyboard and mouse events dispatched the way a browser dispatches them, stubs for the browser APIs jsdom lacks, and dialog and drawer refs for content mounted outside its manager. |
|
|
55
56
|
|
|
56
|
-
`forty-cdk/core` and `forty-cdk/core-overlay` resolve too, but neither is **public**: together they hold the engines and DI singletons the library refactors freely, and they exist so every primitive resolves that shared implementation to one compiled module. They are two rather than one for a bundling reason you get for free: a published module is a bundler's chunk-splitting unit, so keeping the positioning engine (`@floating-ui/dom` and the overlay shells) in its own module means a lazy route that renders no overlay does not load it. Measured on a seven-lazy-route app, that is **41.7 kB raw / 12.1 kB transfer** a non-overlay route no longer pays. If a symbol you need is not exported by the
|
|
57
|
+
`forty-cdk/core` and `forty-cdk/core-overlay` resolve too, but neither is **public**: together they hold the engines and DI singletons the library refactors freely, and they exist so every primitive resolves that shared implementation to one compiled module. They are two rather than one for a bundling reason you get for free: a published module is a bundler's chunk-splitting unit, so keeping the positioning engine (`@floating-ui/dom` and the overlay shells) in its own module means a lazy route that renders no overlay does not load it. Measured on a seven-lazy-route app, that is **41.7 kB raw / 12.1 kB transfer** a non-overlay route no longer pays. If a symbol you need is not exported by the seven specifiers above, it is internal by design — [open an issue](https://github.com/tutkli/forty-cdk/issues) rather than importing from either.
|
|
57
58
|
|
|
58
59
|
## Errors
|
|
59
60
|
|
|
@@ -71,7 +72,7 @@ Fix: Move ForDialogTitle inside a [forDialog] element, declaring any ng-template
|
|
|
71
72
|
|
|
72
73
|
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.
|
|
73
74
|
|
|
74
|
-
Warnings are dev-mode only
|
|
75
|
+
Warnings are dev-mode only, and so are the checks on a value you passed: an invalid column name, a misordered list of snap points or an unbound required input throws in development and is skipped in a production build, where the primitive degrades instead. Do not rely on catching one of those in production. A piece that resolved no context, or an overlay trigger that resolved no root, is different: it would fail one line later anyway, so it throws in every build and says why.
|
|
75
76
|
|
|
76
77
|
## Primitives
|
|
77
78
|
|
package/accordion/README.md
CHANGED
|
@@ -113,12 +113,13 @@ A disabled item cannot be toggled and is skipped by the arrow keys, while stayin
|
|
|
113
113
|
|
|
114
114
|
| Property | Type | Description |
|
|
115
115
|
| ------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
116
|
-
| `value` | `model<readonly string[]>` | Currently open item values. In single mode the array has 0 or 1 element.<br>**Default:**
|
|
116
|
+
| `value` | `model<readonly string[]>` | Currently open item values. In single mode the array has 0 or 1 element.<br>**Default:** `[]` |
|
|
117
117
|
| `multiple` | `input<boolean>` | When true, multiple items can be open simultaneously.<br>**Default:** `false` |
|
|
118
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
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
120
|
| `orientation` | `input<'horizontal' \| 'vertical'>` | Layout direction of the trigger list. In horizontal mode ArrowLeft/Right replace ArrowUp/Down.<br>**Default:** `'vertical'` |
|
|
121
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
|
+
| `loop` | `input<boolean>` | When true, arrow navigation wraps from the last trigger to the first and back. Set `false` to stop at the ends.<br>**Default:** `true` |
|
|
122
123
|
|
|
123
124
|
| Data attribute | Values |
|
|
124
125
|
| ------------------ | -------------------------- |
|
|
@@ -127,10 +128,10 @@ A disabled item cannot be toggled and is skipped by the arrow keys, while stayin
|
|
|
127
128
|
|
|
128
129
|
### `ForAccordionItem`
|
|
129
130
|
|
|
130
|
-
| Property | Type | Description
|
|
131
|
-
| ---------- | ------------------------ |
|
|
132
|
-
| `value` | `input.required<string>` | Unique identifier within the accordion. Required.<br>**Default:** —
|
|
133
|
-
| `disabled` | `input<boolean>` | When true, the trigger ignores clicks and exposes the native `disabled` attribute.<br>**Default:**
|
|
131
|
+
| Property | Type | Description |
|
|
132
|
+
| ---------- | ------------------------ | ---------------------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `value` | `input.required<string>` | Unique identifier within the accordion. Required.<br>**Default:** — |
|
|
134
|
+
| `disabled` | `input<boolean>` | When true, the trigger ignores clicks and exposes the native `disabled` attribute.<br>**Default:** `false` |
|
|
134
135
|
|
|
135
136
|
| Data attribute | Values |
|
|
136
137
|
| ------------------ | -------------------------- |
|
|
@@ -143,6 +144,7 @@ A disabled item cannot be toggled and is skipped by the arrow keys, while stayin
|
|
|
143
144
|
| Data attribute | Values |
|
|
144
145
|
| ------------------ | -------------------------- |
|
|
145
146
|
| `data-state` | `open` \| `closed` |
|
|
147
|
+
| `data-disabled` | present \| absent |
|
|
146
148
|
| `data-orientation` | `horizontal` \| `vertical` |
|
|
147
149
|
|
|
148
150
|
### `ForAccordionContent`
|
|
@@ -150,17 +152,20 @@ A disabled item cannot be toggled and is skipped by the arrow keys, while stayin
|
|
|
150
152
|
| Data attribute | Values |
|
|
151
153
|
| ------------------ | -------------------------- |
|
|
152
154
|
| `data-state` | `open` \| `closed` |
|
|
155
|
+
| `data-disabled` | present \| absent |
|
|
153
156
|
| `data-orientation` | `horizontal` \| `vertical` |
|
|
154
157
|
|
|
158
|
+
`data-disabled` on the item, trigger and content reflects the item's own `[disabled]` or the root's, whichever is set.
|
|
159
|
+
|
|
155
160
|
## Keyboard
|
|
156
161
|
|
|
157
|
-
| Key | Action
|
|
158
|
-
| -------------------------------------------- |
|
|
159
|
-
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Toggle the focused trigger (native button).
|
|
160
|
-
| <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | Move focus between triggers (vertical, default).
|
|
161
|
-
| <kbd>ArrowLeft</kbd> / <kbd>ArrowRight</kbd> | Move focus between triggers (horizontal, flipped under `dir='rtl'`).
|
|
162
|
-
| <kbd>Home</kbd> | Jump to the first trigger.
|
|
163
|
-
| <kbd>End</kbd> | Jump to the last trigger.
|
|
162
|
+
| Key | Action |
|
|
163
|
+
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
164
|
+
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Toggle the focused trigger (native button). |
|
|
165
|
+
| <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | Move focus between triggers (vertical, default). Wraps around unless `loop` is `false`, skips disabled. |
|
|
166
|
+
| <kbd>ArrowLeft</kbd> / <kbd>ArrowRight</kbd> | Move focus between triggers (horizontal, flipped under `dir='rtl'`). Wraps around unless `loop` is `false`, skips disabled. |
|
|
167
|
+
| <kbd>Home</kbd> | Jump to the first trigger. |
|
|
168
|
+
| <kbd>End</kbd> | Jump to the last trigger. |
|
|
164
169
|
|
|
165
170
|
## Accessibility
|
|
166
171
|
|
package/aspect-ratio/README.md
CHANGED
|
@@ -39,7 +39,7 @@ If your ratio is a literal constant, prefer the CSS property directly and keep t
|
|
|
39
39
|
|
|
40
40
|
## Examples
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Narrow the browser window until the frame shrinks and watch it hold its 16 / 9 ratio. The primitive writes the ratio and nothing else, so every border, colour and inset below is your own CSS.
|
|
43
43
|
|
|
44
44
|
```ts
|
|
45
45
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
package/avatar/README.md
CHANGED
|
@@ -96,6 +96,25 @@ When the image errors, the directive flips `shouldShowFallback()` and the initia
|
|
|
96
96
|
| -------------- | ------------------------------------------ |
|
|
97
97
|
| `data-status` | `idle` \| `loading` \| `loaded` \| `error` |
|
|
98
98
|
|
|
99
|
+
## Scoped defaults
|
|
100
|
+
|
|
101
|
+
`provideForAvatarDefaults` configures avatar defaults for an injector subtree, either at the application root or in any component's `providers` array. Partial overrides inherit unspecified keys from the parent scope (or the library fallbacks at the root).
|
|
102
|
+
|
|
103
|
+
| Key | Library fallback | Meaning |
|
|
104
|
+
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
105
|
+
| `fallbackDelayMs` | `0` | Milliseconds to wait before the fallback shows while the image is idle or loading, for avatars that don't set `fallbackDelayMs` themselves. An `error` status always shows the fallback at once. |
|
|
106
|
+
|
|
107
|
+
Per-instance inputs always win over the scope defaults.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { type ApplicationConfig } from '@angular/core';
|
|
111
|
+
import { provideForAvatarDefaults } from 'forty-cdk/defaults';
|
|
112
|
+
|
|
113
|
+
export const appConfig: ApplicationConfig = {
|
|
114
|
+
providers: [provideForAvatarDefaults({ fallbackDelayMs: 500 })],
|
|
115
|
+
};
|
|
116
|
+
```
|
|
117
|
+
|
|
99
118
|
## Accessibility
|
|
100
119
|
|
|
101
120
|
The directive does not impose a `role`. Pair the avatar with visible name text or `aria-label` on the surrounding element when identity matters. Set `alt=""` on the `<img>` for purely decorative avatars next to a name, or provide a meaningful `alt` description if the avatar stands alone.
|
package/breadcrumbs/README.md
CHANGED
|
@@ -23,7 +23,7 @@ A labelled navigation landmark for a breadcrumb trail: links with aria-current='
|
|
|
23
23
|
|
|
24
24
|
## Examples
|
|
25
25
|
|
|
26
|
-
Walk the trail with `Tab`: the last crumb is the page you are on, so it carries `aria-current="page"` and is
|
|
26
|
+
Walk the trail with `Tab`: the last crumb is the page you are on, so it carries `aria-current="page"` and is announced as the current page.
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
29
|
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
@@ -86,22 +86,6 @@ export class BreadcrumbsDefaultExample {
|
|
|
86
86
|
|
|
87
87
|
The primitive renders whatever items you give it, so collapsing a deep path is a consumer decision. Here the middle is folded into an expandable ellipsis button that reveals the hidden crumbs. The trail stays a single accessible navigation landmark either way.
|
|
88
88
|
|
|
89
|
-
## Localizing the label
|
|
90
|
-
|
|
91
|
-
`Breadcrumb` is verbalized by screen readers, so translate it per injector scope with `provideForBreadcrumbsDefaults`. Configure it at the application root, or in any component's `providers` to scope the translation to a subtree. A per-instance `[ariaLabel]` still wins over the scope default.
|
|
92
|
-
|
|
93
|
-
<!-- snippet: fragment -->
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
import { provideForBreadcrumbsDefaults } from 'forty-cdk/defaults';
|
|
97
|
-
|
|
98
|
-
bootstrapApplication(App, {
|
|
99
|
-
providers: [provideForBreadcrumbsDefaults({ label: 'Ruta de navegación' })],
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
For a language the app sets or switches after bootstrap, pass `label` as a function and the overrides as a factory, as [Localizing default text](../shared/README.md#localizing-default-text) shows.
|
|
104
|
-
|
|
105
89
|
## API
|
|
106
90
|
|
|
107
91
|
### `ForBreadcrumbs`
|
|
@@ -112,9 +96,30 @@ For a language the app sets or switches after bootstrap, pass `label` as a funct
|
|
|
112
96
|
|
|
113
97
|
### `ForBreadcrumbItem`
|
|
114
98
|
|
|
115
|
-
| Property | Type | Description
|
|
116
|
-
| --------- | ---------------- |
|
|
117
|
-
| `current` | `input<boolean>` | When true, reflects `aria-current="page"` on the link.<br>**Default:**
|
|
99
|
+
| Property | Type | Description |
|
|
100
|
+
| --------- | ---------------- | ------------------------------------------------------------------------------ |
|
|
101
|
+
| `current` | `input<boolean>` | When true, reflects `aria-current="page"` on the link.<br>**Default:** `false` |
|
|
102
|
+
|
|
103
|
+
## Scoped defaults
|
|
104
|
+
|
|
105
|
+
`provideForBreadcrumbsDefaults` configures breadcrumb defaults for an injector subtree, either at the application root or in any component's `providers` array. Partial overrides inherit unspecified keys from the parent scope (or the library fallbacks at the root).
|
|
106
|
+
|
|
107
|
+
| Key | Library fallback | Meaning |
|
|
108
|
+
| ------- | ---------------- | ------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| `label` | `'Breadcrumb'` | Accessible name of the `navigation` landmark, for trails that don't set `[ariaLabel]` themselves. |
|
|
110
|
+
|
|
111
|
+
`label` is verbalized by screen readers, so translate it here to rename every breadcrumb landmark in the scope. It accepts a string or a function returning one; a function can read a language signal, so the name follows a language the app sets or switches after bootstrap, as [Localizing default text](../shared/README.md#localizing-default-text) shows.
|
|
112
|
+
|
|
113
|
+
Per-instance inputs always win over the scope defaults.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { type ApplicationConfig } from '@angular/core';
|
|
117
|
+
import { provideForBreadcrumbsDefaults } from 'forty-cdk/defaults';
|
|
118
|
+
|
|
119
|
+
export const appConfig: ApplicationConfig = {
|
|
120
|
+
providers: [provideForBreadcrumbsDefaults({ label: 'Ruta de navegación' })],
|
|
121
|
+
};
|
|
122
|
+
```
|
|
118
123
|
|
|
119
124
|
## Accessibility
|
|
120
125
|
|
package/breakpoints/README.md
CHANGED
|
@@ -10,9 +10,15 @@ A signal-first, zoneless, SSR-safe viewport breakpoint observer (injectBreakpoin
|
|
|
10
10
|
|
|
11
11
|
It is a headless reactive utility, not a UI primitive: no DOM, no ARIA, no template. Configure the breakpoint map **once** via a provider and read it anywhere with `injectBreakpoints()`, so there is no need to repeat the breakpoint set at every call site.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Scoped defaults
|
|
14
14
|
|
|
15
|
-
Configuring is optional: without a provider the Tailwind scale
|
|
15
|
+
`provideForBreakpointsDefaults` sets the scope's `breakpoints` map, so `injectBreakpoints()` can be called with no arguments. Configuring is optional: without a provider the Tailwind scale is used. Provide it in your application config, or again in any component's `providers` array to replace the map for that subtree only. The nearest scope wins, and its map replaces the parent's wholesale: it is never merged key by key.
|
|
16
|
+
|
|
17
|
+
| Key | Library fallback | Meaning |
|
|
18
|
+
| ------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `breakpoints` | `forBreakpointsTailwind`: `sm` 640, `md` 768, `lg` 1024, `xl` 1280, `2xl` 1536 | Each breakpoint name paired with its `min-width` threshold in CSS pixels. The query methods derive from these names, sorted ascending by threshold. |
|
|
20
|
+
|
|
21
|
+
The provider takes the map itself rather than an object with a `breakpoints` key, or a function returning the map when it has to be built where `inject()` is available:
|
|
16
22
|
|
|
17
23
|
```ts
|
|
18
24
|
import { ApplicationConfig } from '@angular/core';
|
|
@@ -25,21 +31,12 @@ export const appConfig: ApplicationConfig = {
|
|
|
25
31
|
};
|
|
26
32
|
```
|
|
27
33
|
|
|
28
|
-
Providing it again on a component injector replaces the map for that subtree only (nearest scope wins; the map is replaced wholesale, never merged key-by-key).
|
|
29
|
-
|
|
30
34
|
## Examples
|
|
31
35
|
|
|
32
|
-
Resize the
|
|
36
|
+
Resize the browser window and watch the matched breakpoint change: the breakpoints follow the viewport, not the preview frame. `injectBreakpoints()` hands back signals, so the template, width range included, re-renders without a listener of your own.
|
|
33
37
|
|
|
34
38
|
```ts
|
|
35
|
-
import {
|
|
36
|
-
afterNextRender,
|
|
37
|
-
ChangeDetectionStrategy,
|
|
38
|
-
Component,
|
|
39
|
-
DestroyRef,
|
|
40
|
-
inject,
|
|
41
|
-
signal,
|
|
42
|
-
} from '@angular/core';
|
|
39
|
+
import { ChangeDetectionStrategy, Component, computed } from '@angular/core';
|
|
43
40
|
import { forBreakpointsTailwind, injectBreakpoints } from 'forty-cdk/breakpoints';
|
|
44
41
|
|
|
45
42
|
type TailwindName = keyof typeof forBreakpointsTailwind;
|
|
@@ -52,7 +49,7 @@ type TailwindName = keyof typeof forBreakpointsTailwind;
|
|
|
52
49
|
<div class="bp-readout">
|
|
53
50
|
<span class="bp-readout-label">active breakpoint</span>
|
|
54
51
|
<span class="bp-active">{{ active() ?? 'below sm' }}</span>
|
|
55
|
-
<span class="bp-width">viewport
|
|
52
|
+
<span class="bp-width">viewport {{ range() }}</span>
|
|
56
53
|
</div>
|
|
57
54
|
|
|
58
55
|
<ul class="bp-grid">
|
|
@@ -81,17 +78,12 @@ export class BreakpointsActiveExample {
|
|
|
81
78
|
only: this.bp.only(name),
|
|
82
79
|
}));
|
|
83
80
|
|
|
84
|
-
protected readonly
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
onResize();
|
|
91
|
-
globalThis.addEventListener('resize', onResize, { passive: true });
|
|
92
|
-
destroyRef.onDestroy(() => globalThis.removeEventListener('resize', onResize));
|
|
93
|
-
});
|
|
94
|
-
}
|
|
81
|
+
protected readonly range = computed(() => {
|
|
82
|
+
const index = this.rows.findIndex((row) => row.name === this.active());
|
|
83
|
+
const from = this.rows[index]?.min ?? 0;
|
|
84
|
+
const to = this.rows[index + 1]?.min;
|
|
85
|
+
return to === undefined ? `${from}px and up` : `${from}px to ${to - 1}px`;
|
|
86
|
+
});
|
|
95
87
|
}
|
|
96
88
|
```
|
|
97
89
|
|
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, and a click moves only the focused day to the clicked cell. Each reflects a root hook (`data-disabled` and `data-readonly`)
|
|
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`) that the example's stylesheet keys on. A disabled calendar also disables its prev / next buttons, which reflect `data-disabled` of their own rather than the native attribute.
|
|
192
192
|
|
|
193
193
|
### Constraints & week start
|
|
194
194
|
|
|
@@ -395,6 +395,8 @@ The default window is **anchored to the current year** (not the visible year), s
|
|
|
395
395
|
|
|
396
396
|
**Keep `[forCalendarHeading]` in the DOM.** The grid's `aria-labelledby` points at the heading's id. When dropdowns replace the visible heading, keep a visually-hidden `[forCalendarHeading]` so the grid stays named. Removing the heading entirely leaves `aria-labelledby` pointing at a missing element.
|
|
397
397
|
|
|
398
|
+
**Name each `<select>` yourself.** Neither directive names the element it sits on, and the heading names the grid, not the dropdowns. Without a name a screen reader announces only the value each one holds, so give each an `aria-label` as the snippet above does, or a `<label for>`.
|
|
399
|
+
|
|
398
400
|
The lower-level hooks (`visibleMonthNumber()`, `visibleYear()`, `monthOptions()`, `goToMonth()`, `goToYear()`, `isYearDisabled()`) in the table above remain available for any other UI.
|
|
399
401
|
|
|
400
402
|
## View switching
|
|
@@ -542,6 +544,8 @@ Auto-disabled when the entire previous / next page would be outside `[min, max]`
|
|
|
542
544
|
|
|
543
545
|
## Scoped defaults
|
|
544
546
|
|
|
547
|
+
`provideForCalendarDefaults` configures calendar defaults for an injector subtree, either at the application root or in any component's `providers` array. Partial overrides inherit unspecified keys from the parent scope (or the library fallbacks at the root).
|
|
548
|
+
|
|
545
549
|
```ts
|
|
546
550
|
import { provideForCalendarDefaults } from 'forty-cdk/defaults';
|
|
547
551
|
|
|
@@ -554,6 +558,8 @@ providers: [
|
|
|
554
558
|
];
|
|
555
559
|
```
|
|
556
560
|
|
|
561
|
+
`firstDayOfWeek` sets the first column's weekday as a **0-6** index (`0` = Sunday, `1` = Monday) for calendars that don't bind `[firstDayOfWeek]`. Its fallback `null` takes the week start from the date adapter's `getFirstDayOfWeek()`, and a calendar's own `[firstDayOfWeek]` input always wins over the scope default.
|
|
562
|
+
|
|
557
563
|
`outsideMonthLabel` builds the `aria-label` of a padding day outside the visible month from its formatted full date (default `"<date> (outside month)"`), so assistive tech can tell it apart from the month on screen. A calendar bound to its own `[dateLabel]` formatter ignores it.
|
|
558
564
|
|
|
559
565
|
## Keyboard
|
|
@@ -598,7 +604,7 @@ forty-cdk ships no styles: put your own class on each piece and key your CSS off
|
|
|
598
604
|
.calendar-cell[data-today] {
|
|
599
605
|
outline: 1px solid var(--accent);
|
|
600
606
|
}
|
|
601
|
-
.calendar-cell[data-outside-month] {
|
|
607
|
+
.calendar-cell[data-outside-month]:not([data-selected]) {
|
|
602
608
|
opacity: 0.4;
|
|
603
609
|
}
|
|
604
610
|
.calendar-cell[data-disabled] {
|
package/carousel/README.md
CHANGED
|
@@ -267,7 +267,7 @@ the defaults with `startLabel` / `stopLabel` inputs:
|
|
|
267
267
|
```
|
|
268
268
|
|
|
269
269
|
Both defaults come from the scope's `rotationStartLabel` / `rotationStopLabel`
|
|
270
|
-
(see [
|
|
270
|
+
(see [Scoped defaults](#scoped-defaults)); set either
|
|
271
271
|
input to `null` when the button carries a visible text label and you don't want
|
|
272
272
|
an `aria-label` overriding it.
|
|
273
273
|
|
|
@@ -357,40 +357,47 @@ horizontal carousels (allows vertical page scroll) and `pan-x` for vertical
|
|
|
357
357
|
carousels (allows horizontal page scroll). A mostly-cross-axis swipe is never
|
|
358
358
|
captured, so page scrolling on the perpendicular axis is unaffected.
|
|
359
359
|
|
|
360
|
-
##
|
|
360
|
+
## Scoped defaults
|
|
361
361
|
|
|
362
|
-
|
|
363
|
-
indicator's is `"Go to slide N"`, and the rotation control's swaps between
|
|
364
|
-
`"Start automatic slide show"` and `"Stop automatic slide show"`. The root and
|
|
365
|
-
each slide also carry an `aria-roledescription` (`"carousel"` / `"slide"`), which
|
|
366
|
-
a screen reader speaks in place of the `group` role. Localize them all centrally
|
|
367
|
-
with `provideForCarouselDefaults` instead of setting `ariaLabel` on every slide
|
|
368
|
-
and indicator:
|
|
362
|
+
`provideForCarouselDefaults` configures carousel defaults for an injector subtree, either at the application root or in any component's `providers` array. Partial overrides inherit unspecified keys from the parent scope (or the library fallbacks at the root), so you can localize just the labels and inherit the rest.
|
|
369
363
|
|
|
370
|
-
|
|
364
|
+
| Key | Library fallback | Meaning |
|
|
365
|
+
| ---------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
366
|
+
| `loop` | `false` | Whether `next` past the last slide wraps to the first and `prev` before the first wraps to the last, for carousels that don't set `loop` themselves. |
|
|
367
|
+
| `align` | `'start'` | Alignment of the active slide within the viewport, for carousels that don't set `align` themselves. |
|
|
368
|
+
| `slidesPerView` | `1` | Number of slides visible at once, for carousels that don't set `slidesPerView` themselves. |
|
|
369
|
+
| `containScroll` | `false` | Whether the track offset is clamped so trailing slides sit flush at the viewport edge, for carousels that don't set `containScroll` themselves. See [Contain scroll](#contain-scroll). |
|
|
370
|
+
| `autoplay` | `false` | Whether the carousel rotates automatically on mount (never under reduced motion), for carousels that don't set `autoplay` themselves. |
|
|
371
|
+
| `autoplayInterval` | `5000` | Milliseconds between automatic slide advances, for carousels that don't set `autoplayInterval` themselves. |
|
|
372
|
+
| `slideLabel` | `"N of M"`, e.g. `"2 of 5"` | Builds each slide's default `aria-label` from `position` (1-based) and `total` (the slide count) and returns the string. |
|
|
373
|
+
| `indicatorLabel` | `"Go to slide N"` | Builds each indicator's default `aria-label` from `position` (1-based) and returns the string. |
|
|
374
|
+
| `rotationStartLabel` | `'Start automatic slide show'` | Accessible name of `[forCarouselRotationControl]` while rotation is stopped, for controls that don't set `[startLabel]` themselves. |
|
|
375
|
+
| `rotationStopLabel` | `'Stop automatic slide show'` | Accessible name of `[forCarouselRotationControl]` while rotation is playing, for controls that don't set `[stopLabel]` themselves. |
|
|
376
|
+
| `roleDescription` | `'carousel'` | `aria-roledescription` of the carousel root, spoken in place of its `group` role. An empty string drops the attribute. |
|
|
377
|
+
| `slideRoleDescription` | `'slide'` | `aria-roledescription` of each `[forCarouselSlide]`, spoken in place of its `group` role. An empty string drops the attribute. |
|
|
378
|
+
|
|
379
|
+
Per-instance inputs always win over the scope defaults: an `ariaLabel` on `[forCarouselSlide]` / `[forCarouselIndicator]` overrides the built label, as do `[startLabel]` / `[stopLabel]` on `[forCarouselRotationControl]`.
|
|
380
|
+
|
|
381
|
+
`rotationStartLabel`, `rotationStopLabel`, `roleDescription` and `slideRoleDescription` each accept a string or a function returning one; a function can read a language signal, so the text follows a language the app sets or switches after bootstrap, as [Localizing default text](../shared/README.md#localizing-default-text) shows. Localize them all centrally instead of setting `ariaLabel` on every slide and indicator:
|
|
371
382
|
|
|
372
383
|
```ts
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
384
|
+
import { type ApplicationConfig } from '@angular/core';
|
|
385
|
+
import { provideForCarouselDefaults } from 'forty-cdk/defaults';
|
|
386
|
+
|
|
387
|
+
export const appConfig: ApplicationConfig = {
|
|
388
|
+
providers: [
|
|
389
|
+
provideForCarouselDefaults({
|
|
390
|
+
slideLabel: (position, total) => `Diapositiva ${position} de ${total}`,
|
|
391
|
+
indicatorLabel: (position) => `Ir a la diapositiva ${position}`,
|
|
392
|
+
rotationStartLabel: 'Iniciar la presentación',
|
|
393
|
+
rotationStopLabel: 'Detener la presentación',
|
|
394
|
+
roleDescription: 'carrusel',
|
|
395
|
+
slideRoleDescription: 'diapositiva',
|
|
396
|
+
}),
|
|
397
|
+
],
|
|
398
|
+
};
|
|
383
399
|
```
|
|
384
400
|
|
|
385
|
-
`position` is the 1-based slide index and `total` is the slide count. Overrides
|
|
386
|
-
merge with the parent scope, so you can localize just the labels and inherit the
|
|
387
|
-
rest of the defaults. A per-element `ariaLabel` on `[forCarouselSlide]` /
|
|
388
|
-
`[forCarouselIndicator]` still takes precedence over the localized default, as do
|
|
389
|
-
`[startLabel]` / `[stopLabel]` on `[forCarouselRotationControl]`. For a language
|
|
390
|
-
the app sets or switches after bootstrap, pass the text keys as functions and the
|
|
391
|
-
overrides as a factory, as
|
|
392
|
-
[Localizing default text](../shared/README.md#localizing-default-text) shows.
|
|
393
|
-
|
|
394
401
|
## Indicators map 1:1 to slides
|
|
395
402
|
|
|
396
403
|
The picker assumes **one `[forCarouselIndicator]` per `[forCarouselSlide]`**: the
|
|
@@ -487,7 +494,7 @@ Implements the [WAI-ARIA Carousel pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
487
494
|
`aria-label="N of M"` by default. Override per slide with the `ariaLabel` input,
|
|
488
495
|
or localize the default format and both role descriptions app-wide with
|
|
489
496
|
`provideForCarouselDefaults` (see
|
|
490
|
-
[
|
|
497
|
+
[Scoped defaults](#scoped-defaults)).
|
|
491
498
|
- Slides outside the viewport receive `aria-hidden="true"` and `inert` to remove them from
|
|
492
499
|
the accessibility tree and focus order. The visible set follows the laid-out offset, so
|
|
493
500
|
`align` and `containScroll` decide which slides stay reachable.
|
package/combobox/README.md
CHANGED
|
@@ -239,6 +239,41 @@ The content anchors to the field box when a `[forComboboxAnchor]` is present; se
|
|
|
239
239
|
|
|
240
240
|
Focus stays on the `<input>` the whole time the listbox is open, so options never get `:focus`. `data-highlighted` is the canonical hook for styling the keyboard-active option. A mouse press anywhere on the popup that lands on no focusable element (padding, a group label, an empty or status row) does not move focus either, so typing and the arrow keys keep working after it; the trade-off is that popup text cannot be selected with the mouse, as in a native combobox. A press on a `[forComboboxAction]` still focuses it in every browser. A press on your own `<button>` inside the popup is left to the browser, and macOS Safari and Firefox do not focus a `<button>` on a press.
|
|
241
241
|
|
|
242
|
+
## Scoped defaults
|
|
243
|
+
|
|
244
|
+
`provideForComboboxDefaults` configures combobox defaults for an injector subtree, either at the application root or in any component's `providers` array. Partial overrides inherit unspecified keys from the parent scope (or the library fallbacks at the root).
|
|
245
|
+
|
|
246
|
+
| Key | Library fallback | Meaning |
|
|
247
|
+
| --------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
248
|
+
| `side` | `'bottom'` | Anchor side for comboboxes that don't set `side` themselves. |
|
|
249
|
+
| `align` | `'start'` | Alignment along `side` for comboboxes that don't set `align` themselves. `start` and `end` follow the writing direction. |
|
|
250
|
+
| `sideOffset` | `4` | Main-axis gap (px) for comboboxes that don't set `sideOffset` themselves. |
|
|
251
|
+
| `collisionPadding` | `8` | Collision-middleware padding (px) for comboboxes that don't set it themselves. |
|
|
252
|
+
| `openHighlight` | `'first'` | Where the editable anatomy's highlight lands on open (`'first'` or `'selected'`), for comboboxes that don't set `openHighlight` themselves. See [Open / close behavior](#open--close-behavior). |
|
|
253
|
+
| `restoreQueryOnClose` | `false` | Whether a single-select editable combobox writes the selected option's label back into `query` when the listbox closes without a pick, for comboboxes that don't set it themselves. |
|
|
254
|
+
| `loop` | `true` | Whether arrow navigation wraps past the first / last enabled option, for comboboxes that don't set `loop` themselves. |
|
|
255
|
+
| `toggleAriaLabel` | `'Show options'` | Accessible name of a `[forComboboxToggle]` that sets no `[ariaLabel]`. |
|
|
256
|
+
| `chipsAriaLabel` | `'Selected items'` | Accessible name of a multi-mode `[forComboboxChips]` group that sets no `[ariaLabel]`. |
|
|
257
|
+
| `clearAriaLabel` | `'Clear'` | Accessible name of a `[forComboboxClear]` that sets no `[ariaLabel]`. |
|
|
258
|
+
| `chipRemoveLabel` | `` (label) => `Remove ${label}` `` | Builds the accessible name of every `[forComboboxChipRemove]` from its chip's resolved option label. The piece takes no per-instance `[ariaLabel]`, so this key is the only way to change it. |
|
|
259
|
+
|
|
260
|
+
Per-instance inputs always win over the scope defaults. `toggleAriaLabel`, `chipsAriaLabel` and `clearAriaLabel` accept a string or a function returning one; the library calls the function each time it renders the name, so a function that reads a language signal keeps the name in the current language. See [Localizing default text](../shared/README.md#localizing-default-text).
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { type ApplicationConfig } from '@angular/core';
|
|
264
|
+
import { provideForComboboxDefaults } from 'forty-cdk/defaults';
|
|
265
|
+
|
|
266
|
+
export const appConfig: ApplicationConfig = {
|
|
267
|
+
providers: [
|
|
268
|
+
provideForComboboxDefaults({
|
|
269
|
+
openHighlight: 'selected',
|
|
270
|
+
clearAriaLabel: 'Clear selection',
|
|
271
|
+
chipRemoveLabel: (label) => `Remove ${label} from the selection`,
|
|
272
|
+
}),
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
242
277
|
## Mount/visibility convention
|
|
243
278
|
|
|
244
279
|
`[forComboboxContent]` follows the floating-overlay convention: the consumer's signal drives `@if`, the directive emits dismiss events when it wants to be unmounted. No `[hidden]`. The visible input lives outside the overlay; only the listbox surface portals.
|
|
@@ -751,7 +786,7 @@ When `[forComboboxVirtualized]` is absent or its `[totalCount]` is omitted, the
|
|
|
751
786
|
|
|
752
787
|
## Writing direction
|
|
753
788
|
|
|
754
|
-
`[forCombobox]` exposes a `dir: 'ltr' | 'rtl'` input (default `'ltr'`). It drives:
|
|
789
|
+
`[forCombobox]` exposes a `dir: 'ltr' | 'rtl' | null` input (default `null`: inherited from the nearest `[dir]` ancestor or `<html dir>`, falling back to `'ltr'`). It drives:
|
|
755
790
|
|
|
756
791
|
- **Chip keyboard navigation**: ArrowLeft / ArrowRight roles swap so they follow the visual order of the chip cluster, not the DOM order. See _Chip keyboard_ above.
|
|
757
792
|
- **Default popover placement**: `align` defaults to `'start'` and `side` to `'bottom'`. `start` and `end` resolve against the writing direction, so the default anchors the listbox to the visually-leading edge of the input in both LTR and RTL, and a consumer-provided `[align]` follows the direction the same way. `provideForComboboxDefaults({ align, side })` sets either for a whole scope.
|
|
@@ -851,7 +886,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
851
886
|
- `data-state="checked" | "unchecked"` always reflects membership in `value()`, so consumers can paint a checkmark icon with pure CSS regardless of mode.
|
|
852
887
|
- `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.
|
|
853
888
|
- Disabled options keep the host `aria-disabled="true"`. Click and hover (activedescendant pinning) are no-ops on disabled options.
|
|
854
|
-
- `[forComboboxSeparator]`
|
|
889
|
+
- `[forComboboxSeparator]` is skipped by keyboard navigation. It is always presentational: `role="none"` and no `aria-orientation`, because it sits inside the `role="listbox"` (the content in the editable anatomy, the list in the picker anatomy), which may only own `option` / `group` children. `data-orientation` is always stamped for styling. When the split must be announced, wrap the options on each side in a `[forComboboxGroup]` with a `[forComboboxGroupLabel]`.
|
|
855
890
|
- `[forComboboxGroup]` is purely advisory grouping: options inside still register flatly with the root, so navigation flows through groups without interruption.
|
|
856
891
|
- `[forComboboxEmpty]` and `[forComboboxStatus]` carry `role="status"` and nothing else. The role already implies `aria-live="polite"` and `aria-atomic="true"`, so the empty-state message is announced, whole, when filtering removes all matches. The role is the channel they keep (rather than the attribute pair) because `[forComboboxEmpty]` self-hides and comes back with its message already in the DOM: a live _role_ is what screen readers read reliably on insertion.
|
|
857
892
|
- Non-option pieces (`[forComboboxAction]`, and ideally `[forComboboxEmpty]` / `[forComboboxStatus]`) belong inside `[forComboboxContent]` but **outside** `[forComboboxList]`, because `role="listbox"` may only own `option` / `group` children (`aria-required-owned-elements`). Wrapping the options in a `[forComboboxList]` (the "editable + list" shape) makes those pieces siblings of the listbox. `[forComboboxAction]` **requires** a `[forComboboxList]` and throws `[forty-cdk/combobox]` without one; `[forComboboxEmpty]` / `[forComboboxStatus]` stay lenient in the bare editable anatomy (documented compromise, see the [editable-anatomy caveat](#anatomy)).
|
package/context-menu/README.md
CHANGED
|
@@ -119,6 +119,30 @@ Same vetoable dismiss API as DropdownMenu. Call `preventDefault()` on the emitte
|
|
|
119
119
|
| `[forContextMenuTrigger]` | `data-state` | `open` \| `closed` |
|
|
120
120
|
| `[forContextMenuTrigger]` | `data-disabled` | present \| absent |
|
|
121
121
|
|
|
122
|
+
## Scoped defaults
|
|
123
|
+
|
|
124
|
+
`provideForContextMenuDefaults` configures context-menu defaults for an injector subtree, either at the application root or in any component's `providers` array. Partial overrides inherit unspecified keys from the parent scope (or the library fallbacks at the root).
|
|
125
|
+
|
|
126
|
+
| Key | Library fallback | Meaning |
|
|
127
|
+
| --------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
128
|
+
| `side` | `'bottom'` | Anchor side, relative to the pointer, for context menus that don't set `side` themselves. |
|
|
129
|
+
| `align` | `'start'` | Alignment along `side` for context menus that don't set `align` themselves. |
|
|
130
|
+
| `sideOffset` | `0` | Main-axis gap (px) between the pointer and the menu for context menus that don't set `sideOffset` themselves. |
|
|
131
|
+
| `collisionPadding` | `8` | Collision-middleware padding (px) for context menus that don't set it themselves. |
|
|
132
|
+
| `fallbackAxisSideDirection` | `'none'` | Perpendicular side `flip` may drop to when both sides of the preferred axis overflow. `'none'` keeps only the opposite same-axis placement. Only consulted while `avoidCollisions` is on. |
|
|
133
|
+
| `loop` | `true` | Whether arrow navigation wraps past the first / last enabled item, for context menus that don't set `loop` themselves. |
|
|
134
|
+
|
|
135
|
+
Per-instance inputs always win over the scope defaults. Submenus inside a context menu take their timings and placement from `provideForMenuDefaults`; see the [Menu README](../menu/README.md#scoped-defaults).
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { type ApplicationConfig } from '@angular/core';
|
|
139
|
+
import { provideForContextMenuDefaults } from 'forty-cdk/defaults';
|
|
140
|
+
|
|
141
|
+
export const appConfig: ApplicationConfig = {
|
|
142
|
+
providers: [provideForContextMenuDefaults({ sideOffset: 2, fallbackAxisSideDirection: 'end' })],
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
122
146
|
## Accessibility
|
|
123
147
|
|
|
124
148
|
`[forContextMenu]` implements the [WAI-ARIA Menu pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/). The trigger captures `contextmenu`, `Shift+F10`, and the `ContextMenu` key; the menu surface and item roles come from the shared [`menu/`](../menu/README.md) primitives. Pointer activations anchor at the cursor; keyboard activations anchor at the bounding rect of the focused element so keyboard-only users get the menu next to whatever they're working on.
|
|
@@ -208,4 +232,4 @@ Both triggers carry `[menuPositioning]`, a partial `{ side, align, sideOffset, a
|
|
|
208
232
|
|
|
209
233
|
## Wrapping in a design system
|
|
210
234
|
|
|
211
|
-
Subclass the root and re-provide `FOR_MENU_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers
|
|
235
|
+
Subclass the root and re-provide both of its tokens, `FOR_MENU_CONTEXT` and `FOR_CONTEXT_MENU_CONTEXT`, with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`. Leaving out the second one throws `FORCDK-CONTEXT-MENU-001` from the trigger. [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
|
package/date-field/README.md
CHANGED
|
@@ -232,12 +232,16 @@ providers: [
|
|
|
232
232
|
];
|
|
233
233
|
```
|
|
234
234
|
|
|
235
|
+
`emptySegmentText` (default `'Empty'`) is the `aria-valuetext` an empty segment announces, so a screen reader reports the empty state instead of silence. Translate it along with the segment labels.
|
|
236
|
+
|
|
235
237
|
`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.
|
|
236
238
|
|
|
237
|
-
`placeholder` is the text an empty segment shows, keyed the same way. A field's own `[placeholder]` wins for the parts it names and only those, and a part neither names keeps the letter-repeat default. A nested `provideForDateFieldDefaults` merges both records entry by entry with its parent scope, so a feature overriding only `dayPeriod` keeps the labels and placeholders the application root localized.
|
|
239
|
+
`placeholder` is the text an empty segment shows, keyed the same way. A field's own `[placeholder]` wins for the parts it names and only those, and a part neither names keeps the letter-repeat default. A nested `provideForDateFieldDefaults` merges both records entry by entry with its parent scope, so a feature overriding only `dayPeriod` keeps the labels and placeholders the application root localized.
|
|
238
240
|
|
|
239
241
|
`hourCycle` sets the 12- or 24-hour cycle of every field that doesn't bind `[hourCycle]`, so a product on a 24-hour clock sets it once instead of on each field. Its fallback `null` derives the cycle from the locale.
|
|
240
242
|
|
|
243
|
+
`provideForDateRangeFieldDefaults` configures `[forDateRangeField]` through its own scope, independent of `provideForDateFieldDefaults`. It takes the same `emptySegmentText`, `segmentLabels`, `placeholder` and `hourCycle` keys with the same fallbacks, plus `startAriaLabel` and `endAriaLabel` (default `'Start date'` / `'End date'`), the `aria-label` of the start and end endpoint groups that don't set `ariaLabel` themselves.
|
|
244
|
+
|
|
241
245
|
For a language the app sets or switches after bootstrap, pass the text keys as functions and the overrides as a factory, as [Localizing default text](../shared/README.md#localizing-default-text) shows.
|
|
242
246
|
|
|
243
247
|
## Range selection — `ForDateRangeField`
|
|
@@ -296,7 +300,7 @@ readonly booking = form(this.model);
|
|
|
296
300
|
| `ariaLabel` | `input<string \| null>` | Accessible name for the whole range field group. Emits no `aria-label` while `null`.<br>**Default:** `null` |
|
|
297
301
|
| `dir` | `input<'ltr' \| 'rtl' \| null>` | Writing direction. `null` resolves the ambient direction; mirrors ArrowLeft / ArrowRight segment navigation.<br>**Default:** `null` |
|
|
298
302
|
|
|
299
|
-
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]`.
|
|
303
|
+
The endpoint groups each accept an `ariaLabel` input for their own group label, falling back to the scope defaults (`'Start date'` / `'End date'`); bind `[ariaLabel]="null"` to drop the name. Plus the shared `FormUiControl` members bound automatically by `[formField]`.
|
|
300
304
|
|
|
301
305
|
`ForDateRangeField` exposes the same `clear()` and `reset()` as `ForDateField`. They drop a half-entered range and an out-of-order one, its `data-range-error` included, which writing `null` through `[(value)]` cannot do: such a range already reports `value()` as `null`. Without them, finishing the end endpoint later would commit a range built from a start the user thought was gone. A native reset of the owning `<form>` returns the range to what it held when the field first rendered and runs `reset()`.
|
|
302
306
|
|
|
@@ -314,7 +318,7 @@ Each endpoint is its own tab stop, so `Tab` moves start group → end group →
|
|
|
314
318
|
|
|
315
319
|
### Range scoped defaults
|
|
316
320
|
|
|
317
|
-
`provideForDateRangeFieldDefaults`
|
|
321
|
+
`provideForDateRangeFieldDefaults` configures the range field, endpoint group names included; its keys are listed under [Scoped defaults](#scoped-defaults). 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).
|
|
318
322
|
|
|
319
323
|
## Keyboard
|
|
320
324
|
|