forty-cdk 0.13.0 → 0.14.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/button/README.md +13 -4
- package/combobox/README.md +2 -1
- package/context-menu/README.md +22 -20
- package/date-field/README.md +1 -0
- package/date-range-field/README.md +1 -0
- package/dropdown-menu/README.md +10 -6
- package/fesm2022/forty-cdk-accordion.mjs +4 -3
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -1
- package/fesm2022/forty-cdk-breadcrumbs.mjs +4 -3
- package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -1
- package/fesm2022/forty-cdk-button.mjs +35 -16
- package/fesm2022/forty-cdk-button.mjs.map +1 -1
- package/fesm2022/forty-cdk-calendar.mjs +16 -11
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
- package/fesm2022/forty-cdk-carousel.mjs +13 -9
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
- package/fesm2022/forty-cdk-combobox.mjs +53 -22
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-context-menu.mjs +14 -1
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +481 -67
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-field.mjs +9 -4
- 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-date-range-field.mjs +22 -7
- package/fesm2022/forty-cdk-date-range-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-dropdown-menu.mjs +18 -10
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-field.mjs +50 -19
- package/fesm2022/forty-cdk-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-fieldset.mjs +6 -6
- package/fesm2022/forty-cdk-fieldset.mjs.map +1 -1
- package/fesm2022/forty-cdk-file-upload.mjs +53 -31
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
- package/fesm2022/forty-cdk-hover-card.mjs +6 -6
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +7 -5
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-menu.mjs +117 -75
- package/fesm2022/forty-cdk-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-menubar.mjs +158 -159
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
- package/fesm2022/forty-cdk-meter.mjs +4 -3
- package/fesm2022/forty-cdk-meter.mjs.map +1 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs +104 -55
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-number-input.mjs +72 -30
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -1
- package/fesm2022/forty-cdk-otp-input.mjs +24 -5
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -1
- package/fesm2022/forty-cdk-pagination.mjs +10 -7
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
- package/fesm2022/forty-cdk-pane-resizer.mjs +30 -4
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs +8 -5
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-progress.mjs +4 -3
- package/fesm2022/forty-cdk-progress.mjs.map +1 -1
- package/fesm2022/forty-cdk-radio-group.mjs +8 -1
- package/fesm2022/forty-cdk-radio-group.mjs.map +1 -1
- package/fesm2022/forty-cdk-search.mjs +58 -15
- package/fesm2022/forty-cdk-search.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +42 -12
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-slider.mjs +120 -56
- package/fesm2022/forty-cdk-slider.mjs.map +1 -1
- package/fesm2022/forty-cdk-stepper.mjs +12 -8
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +54 -13
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-tabs.mjs +5 -5
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-field.mjs +9 -4
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs +6 -4
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-range-field.mjs +22 -7
- package/fesm2022/forty-cdk-time-range-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-toast.mjs +8 -9
- package/fesm2022/forty-cdk-toast.mjs.map +1 -1
- package/fesm2022/forty-cdk-toggle.mjs +13 -4
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
- package/fesm2022/forty-cdk-toolbar.mjs +55 -45
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -1
- package/fesm2022/forty-cdk-tooltip.mjs +12 -7
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
- package/fesm2022/forty-cdk-tree.mjs +4 -3
- package/fesm2022/forty-cdk-tree.mjs.map +1 -1
- package/field/README.md +2 -0
- package/fieldset/README.md +4 -4
- package/file-upload/README.md +16 -4
- package/menu/README.md +19 -17
- package/menubar/README.md +43 -32
- package/navigation-menu/README.md +9 -1
- package/number-input/README.md +6 -2
- package/otp-input/README.md +1 -1
- package/package.json +1 -1
- package/pane-resizer/README.md +29 -16
- package/radio-group/README.md +4 -0
- package/search/README.md +50 -0
- package/select/README.md +2 -1
- package/separator/README.md +1 -0
- package/slider/README.md +7 -2
- package/table/README.md +30 -9
- package/time-field/README.md +1 -0
- package/time-range-field/README.md +1 -0
- package/toggle/README.md +19 -17
- package/toolbar/README.md +3 -3
- package/types/forty-cdk-accordion.d.ts +1 -0
- package/types/forty-cdk-breadcrumbs.d.ts +1 -0
- package/types/forty-cdk-button.d.ts +16 -2
- package/types/forty-cdk-calendar.d.ts +5 -0
- package/types/forty-cdk-carousel.d.ts +4 -0
- package/types/forty-cdk-combobox.d.ts +29 -8
- package/types/forty-cdk-context-menu.d.ts +20 -5
- package/types/forty-cdk-core.d.ts +495 -35
- package/types/forty-cdk-date-field.d.ts +6 -0
- package/types/forty-cdk-date-picker.d.ts +2 -0
- package/types/forty-cdk-date-range-field.d.ts +17 -0
- package/types/forty-cdk-dropdown-menu.d.ts +16 -7
- package/types/forty-cdk-field.d.ts +26 -11
- package/types/forty-cdk-fieldset.d.ts +4 -3
- package/types/forty-cdk-file-upload.d.ts +61 -19
- package/types/forty-cdk-hover-card.d.ts +4 -4
- package/types/forty-cdk-listbox.d.ts +2 -0
- package/types/forty-cdk-menu.d.ts +42 -17
- package/types/forty-cdk-menubar.d.ts +162 -73
- package/types/forty-cdk-meter.d.ts +1 -0
- package/types/forty-cdk-navigation-menu.d.ts +51 -7
- package/types/forty-cdk-number-input.d.ts +55 -16
- package/types/forty-cdk-otp-input.d.ts +1 -0
- package/types/forty-cdk-pagination.d.ts +3 -0
- package/types/forty-cdk-pane-resizer.d.ts +18 -2
- package/types/forty-cdk-popover.d.ts +3 -0
- package/types/forty-cdk-progress.d.ts +1 -0
- package/types/forty-cdk-radio-group.d.ts +6 -0
- package/types/forty-cdk-search.d.ts +34 -7
- package/types/forty-cdk-select.d.ts +24 -3
- package/types/forty-cdk-slider.d.ts +55 -10
- package/types/forty-cdk-stepper.d.ts +6 -1
- package/types/forty-cdk-table.d.ts +30 -3
- package/types/forty-cdk-tabs.d.ts +1 -0
- package/types/forty-cdk-time-field.d.ts +6 -0
- package/types/forty-cdk-time-picker.d.ts +2 -0
- package/types/forty-cdk-time-range-field.d.ts +17 -0
- package/types/forty-cdk-toast.d.ts +1 -0
- package/types/forty-cdk-toggle.d.ts +10 -0
- package/types/forty-cdk-toolbar.d.ts +21 -8
- package/types/forty-cdk-tooltip.d.ts +8 -3
- package/types/forty-cdk-tree.d.ts +1 -0
package/button/README.md
CHANGED
|
@@ -34,6 +34,15 @@ Disabled buttons stay focusable so assistive technology can announce them. The n
|
|
|
34
34
|
<button forButton [disabled]="isSaving()" (activate)="save()">Save</button>
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
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.
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<fieldset forFieldset [disabled]="locked()">
|
|
41
|
+
<legend forFieldsetLegend>Account</legend>
|
|
42
|
+
<button forButton (activate)="save()">Save</button>
|
|
43
|
+
</fieldset>
|
|
44
|
+
```
|
|
45
|
+
|
|
37
46
|
### Preserve consumer `type`
|
|
38
47
|
|
|
39
48
|
A native `<button>` without an explicit `type` attribute defaults to `type="button"`. A consumer-set `type="submit"` is preserved:
|
|
@@ -46,10 +55,10 @@ A native `<button>` without an explicit `type` attribute defaults to `type="butt
|
|
|
46
55
|
|
|
47
56
|
### `ForButton`
|
|
48
57
|
|
|
49
|
-
| Property | Type | Description
|
|
50
|
-
| ---------- | ---------------- |
|
|
51
|
-
| `disabled` | `input<boolean>` | Suppresses activation and reflects `aria-disabled` + `data-disabled
|
|
52
|
-
| `activate` | `output<void>` | Fires once per user activation (click, Enter, Space). Never fires when disabled.<br>**Default:** —
|
|
58
|
+
| Property | Type | Description |
|
|
59
|
+
| ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
+
| `disabled` | `input<boolean>` | Suppresses activation and reflects `aria-disabled` + `data-disabled`. OR'd with a surrounding `[forFieldset]`'s disabled state.<br>**Default:** `false` |
|
|
61
|
+
| `activate` | `output<void>` | Fires once per user activation (click, Enter, Space). Never fires when disabled.<br>**Default:** — |
|
|
53
62
|
|
|
54
63
|
The directive reflects boolean `data-*` attributes (present with an empty-string value when true, absent when false). There is no `data-state` — this primitive has no open/closed or checked/unchecked logical state.
|
|
55
64
|
|
package/combobox/README.md
CHANGED
|
@@ -129,6 +129,7 @@ Input tables are not yet tabulated for this primitive. See the feature sections
|
|
|
129
129
|
| `[forComboboxIndicator]` | `data-state` | `checked` \| `unchecked` (mirrors the parent option) |
|
|
130
130
|
| `[forComboboxChip]` | `data-value` | the chip's serialized value (verbatim string, or `itemToFormValue`) |
|
|
131
131
|
| `[forComboboxChip]` | `data-disabled` | present / absent |
|
|
132
|
+
| `[forComboboxSeparator]` | `data-orientation` | `horizontal` \| `vertical` |
|
|
132
133
|
|
|
133
134
|
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.
|
|
134
135
|
|
|
@@ -608,7 +609,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
608
609
|
- `data-state="checked" | "unchecked"` always reflects membership in `value()`, so consumers can paint a checkmark icon with pure CSS regardless of mode.
|
|
609
610
|
- `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.
|
|
610
611
|
- Disabled options keep the host `aria-disabled="true"`. Click and hover (activedescendant pinning) are no-ops on disabled options.
|
|
611
|
-
- `[forComboboxSeparator]`
|
|
612
|
+
- `[forComboboxSeparator]` never registers with the listbox's option collection — keyboard navigation skips it automatically. It carries `role="separator"` and emits `aria-orientation` only for `orientation="vertical"`, because `horizontal` is the ARIA default; `data-orientation` is always stamped for styling. Set `decorative` when the surrounding options already convey the split — it switches the line to `role="none"` and drops `aria-orientation`, matching the [shared separator emission policy](../separator/README.md#accessibility).
|
|
612
613
|
- `[forComboboxGroup]` is purely advisory grouping — options inside still register flatly with the root, so navigation flows through groups without interruption.
|
|
613
614
|
- `[forComboboxEmpty]` carries `role="status"` + `aria-live="polite"` so the empty-state message is announced when filtering removes all matches.
|
|
614
615
|
- Non-option pieces (`[forComboboxAction]`, and ideally `[forComboboxEmpty]` / `[forComboboxStatus]`) belong inside `[forComboboxContent]` but **outside** `[forComboboxList]` — `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
|
@@ -99,26 +99,26 @@ Angular resolves `ng-template` DI at the template's **declaration** site, not wh
|
|
|
99
99
|
|
|
100
100
|
### `ForContextMenu`
|
|
101
101
|
|
|
102
|
-
| Property | Type | Description
|
|
103
|
-
| --------------------------- | --------------------------------------------------------- |
|
|
104
|
-
| `open` | `model<boolean>` | Two-way bindable. Whether the menu is shown.<br>**Default:** `false`
|
|
105
|
-
| `side` | `input<string>` | Anchor side relative to the pointer.<br>**Default:** `'bottom'`
|
|
106
|
-
| `align` | `input<string>` | Alignment along `side` (`'start'` / `'center'` / `'end'`).<br>**Default:** `'start'`
|
|
107
|
-
| `sideOffset` | `input<number>` | Gap (px) between the pointer and the menu along the main axis.<br>**Default:** `0`
|
|
108
|
-
| `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0`
|
|
109
|
-
| `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.<br>**Default:** `'none'`
|
|
110
|
-
| `loop` | `input<boolean>` | Whether arrow navigation wraps.<br>**Default:** `true`
|
|
111
|
-
| `dir` | `input<string>` | Writing direction. In RTL, ArrowLeft opens submenus and ArrowRight closes them — the swap is automatic. Inherited by every nested `[forMenuSub]` underneath unless overridden.<br>**Default:** `'ltr'`
|
|
112
|
-
| `disabled` | `input<boolean>` | When `true`, the contextmenu event falls through to the native browser menu.<br>**Default:** `false`
|
|
113
|
-
| `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true`
|
|
114
|
-
| `returnFocus` | `input<boolean>` | When `true`, focus returns to the trigger element on close.<br>**Default:** `true`
|
|
115
|
-
| `ariaLabel` | `input<string \| null>` |
|
|
116
|
-
| `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissable layer.<br>**Default:** —
|
|
117
|
-
| `pointerDownOutside` | `output<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down on a target outside content + trigger.<br>**Default:** —
|
|
118
|
-
| `focusOutside` | `output<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside content + trigger.<br>**Default:** —
|
|
119
|
-
| `interactOutside` | `output<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite — fires alongside the two above (and shares their veto state).<br>**Default:** —
|
|
120
|
-
| `autoFocusOnOpen` | `output<VetoableEvent>` | Output. Just before the imperative focus move on mount.<br>**Default:** —
|
|
121
|
-
| `autoFocusOnClose` | `output<VetoableEvent>` | Output. Just before the imperative focus move on unmount.<br>**Default:** —
|
|
102
|
+
| Property | Type | Description |
|
|
103
|
+
| --------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
104
|
+
| `open` | `model<boolean>` | Two-way bindable. Whether the menu is shown.<br>**Default:** `false` |
|
|
105
|
+
| `side` | `input<string>` | Anchor side relative to the pointer.<br>**Default:** `'bottom'` |
|
|
106
|
+
| `align` | `input<string>` | Alignment along `side` (`'start'` / `'center'` / `'end'`).<br>**Default:** `'start'` |
|
|
107
|
+
| `sideOffset` | `input<number>` | Gap (px) between the pointer and the menu along the main axis.<br>**Default:** `0` |
|
|
108
|
+
| `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0` |
|
|
109
|
+
| `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.<br>**Default:** `'none'` |
|
|
110
|
+
| `loop` | `input<boolean>` | Whether arrow navigation wraps.<br>**Default:** `true` |
|
|
111
|
+
| `dir` | `input<string>` | Writing direction. In RTL, ArrowLeft opens submenus and ArrowRight closes them — the swap is automatic. Inherited by every nested `[forMenuSub]` underneath unless overridden.<br>**Default:** `'ltr'` |
|
|
112
|
+
| `disabled` | `input<boolean>` | When `true`, the contextmenu event falls through to the native browser menu.<br>**Default:** `false` |
|
|
113
|
+
| `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true` |
|
|
114
|
+
| `returnFocus` | `input<boolean>` | When `true`, focus returns to the trigger element on close.<br>**Default:** `true` |
|
|
115
|
+
| `ariaLabel` | `input<string \| null>` | Accessible name reflected as `aria-label` on `[forMenuContent]`. The root's only name hook for a context menu — the right-click region is never used as an `aria-labelledby` target.<br>**Default:** `null` |
|
|
116
|
+
| `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissable layer.<br>**Default:** — |
|
|
117
|
+
| `pointerDownOutside` | `output<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down on a target outside content + trigger.<br>**Default:** — |
|
|
118
|
+
| `focusOutside` | `output<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside content + trigger.<br>**Default:** — |
|
|
119
|
+
| `interactOutside` | `output<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite — fires alongside the two above (and shares their veto state).<br>**Default:** — |
|
|
120
|
+
| `autoFocusOnOpen` | `output<VetoableEvent>` | Output. Just before the imperative focus move on mount.<br>**Default:** — |
|
|
121
|
+
| `autoFocusOnClose` | `output<VetoableEvent>` | Output. Just before the imperative focus move on unmount.<br>**Default:** — |
|
|
122
122
|
|
|
123
123
|
Same vetoable dismiss API as DropdownMenu. Call `preventDefault()` on the emitted veto to suppress the directive's default action; the original DOM event, when present, is on `.event`.
|
|
124
124
|
|
|
@@ -137,6 +137,8 @@ Same vetoable dismiss API as DropdownMenu. Call `preventDefault()` on the emitte
|
|
|
137
137
|
|
|
138
138
|
`[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.
|
|
139
139
|
|
|
140
|
+
- **Name the menu with `[ariaLabel]`.** Unlike `[forDropdownMenu]` / `[forMenubar]` / `[forMenuSub]`, the content surface emits **no** `aria-labelledby` fallback here: the trigger is the whole right-click region, so pointing the menu's name at it would make screen readers announce the entire row / card text as the menu name. With no `[ariaLabel]` the `role="menu"` surface simply has no accessible name. A consumer-set static `aria-labelledby` on `[forMenuContent]` is still preserved, so pointing at your own visible heading also works. Submenus nested inside a context menu are unaffected — a `[forMenuSubContent]` is still labelled by its `[forMenuSubTrigger]`.
|
|
141
|
+
|
|
140
142
|
## Styling
|
|
141
143
|
|
|
142
144
|
forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
|
package/date-field/README.md
CHANGED
|
@@ -212,6 +212,7 @@ Composes the [WAI-ARIA Spinbutton pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
212
212
|
- **Roving tabindex**: exactly one segment is tabbable, so `Tab` enters and leaves the whole field in one stop; arrows move between segments.
|
|
213
213
|
- **Literals are `aria-hidden`** and never focusable — assistive tech reads only the spinbutton segments.
|
|
214
214
|
- **Boolean `data-*`** on each segment — `data-highlighted` (focused/roving), `data-placeholder` (empty), `data-disabled`, `data-readonly` — present when true, absent when false.
|
|
215
|
+
- **`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.
|
|
215
216
|
|
|
216
217
|
## Styling
|
|
217
218
|
|
|
@@ -242,6 +242,7 @@ Each segment implements the [WAI-ARIA Spinbutton pattern](https://www.w3.org/WAI
|
|
|
242
242
|
- **Roving tabindex per endpoint**: exactly one segment per endpoint is tabbable, so `Tab` steps start group → end group; arrows move between segments within an endpoint.
|
|
243
243
|
- **`aria-invalid="true"`** is reflected on the root when the form marks it invalid **or** when two complete endpoints are out of order.
|
|
244
244
|
- **Literals are `aria-hidden`** and never focusable — assistive tech reads only the spinbutton segments.
|
|
245
|
+
- **`aria-readonly` belongs on the segments, not the groups.** WAI-ARIA supports it on `role="spinbutton"` but not on `role="group"`, so each segment carries `aria-readonly="true"` while the root and both endpoint groups reflect the `data-readonly` styling hook only.
|
|
245
246
|
|
|
246
247
|
## Styling
|
|
247
248
|
|
package/dropdown-menu/README.md
CHANGED
|
@@ -155,11 +155,13 @@ The menu items, content surface, radio groups, separators, and groups live in th
|
|
|
155
155
|
|
|
156
156
|
## Keyboard
|
|
157
157
|
|
|
158
|
-
| Key
|
|
159
|
-
|
|
|
160
|
-
| `Click`
|
|
161
|
-
| `ArrowDown`
|
|
162
|
-
| `ArrowUp`
|
|
158
|
+
| Key | Behavior |
|
|
159
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
160
|
+
| `Click` | Toggles the menu. On open, focus moves to the first enabled item. |
|
|
161
|
+
| `Enter` / `Space` / `ArrowDown` | Opens the menu and focuses the first enabled item; on an already-open menu, moves focus there. |
|
|
162
|
+
| `ArrowUp` | Opens the menu and focuses the last enabled item; on an already-open menu, moves focus there. |
|
|
163
|
+
|
|
164
|
+
The open keys never close the menu — the APG menu-button pattern gives them no close semantics (that's `Escape`, or a pointer click on the trigger). Pressing one while the menu is already open moves focus into it, which is what makes them useful after an `(autoFocusOnOpen)`-vetoed open left focus on the trigger. A menu with no enabled item moves nothing.
|
|
163
165
|
|
|
164
166
|
Once focus is in the menu, see [`menu/README.md`](../menu/README.md) for the in-menu keyboard.
|
|
165
167
|
|
|
@@ -167,6 +169,8 @@ Once focus is in the menu, see [`menu/README.md`](../menu/README.md) for the in-
|
|
|
167
169
|
|
|
168
170
|
`[forDropdownMenu]` implements the [WAI-ARIA Menu Button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/). The trigger wires `aria-haspopup="menu"`, `aria-expanded`, and `aria-controls`; the menu surface and item roles come from the shared [`menu/`](../menu/README.md) primitives.
|
|
169
171
|
|
|
172
|
+
A disabled trigger (its own `[disabled]`, or the root's) reflects through a **single channel**: the native `disabled` attribute plus the `data-disabled` styling hook. No `aria-disabled` is emitted — the trigger is a real single-purpose `<button>` and the native attribute already conveys the state to assistive technology, per the sanctioned native-`disabled` case in [rule #561](https://github.com/tutkli/forty-cdk/issues/561) (D2). Style the disabled trigger off `[disabled]` or `[data-disabled]`, never `[aria-disabled]`.
|
|
173
|
+
|
|
170
174
|
## Styling
|
|
171
175
|
|
|
172
176
|
forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed under [Data attributes](#data-attributes).
|
|
@@ -186,5 +190,5 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
|
|
|
186
190
|
|
|
187
191
|
- **Mount equals open.** The directive does not toggle `[hidden]` — `@if (open())` controls presence so `animate.enter` / `animate.leave` fire on the natural mount cycle.
|
|
188
192
|
- **Trigger is exempt** from outside-pointer / outside-focus checks. Without this, clicking the trigger to close would race with its own toggle handler and reopen immediately.
|
|
189
|
-
- **Initial focus depends on the opening key.** Click / Space / Enter / ArrowDown focus the first enabled item; ArrowUp focuses the last enabled item.
|
|
193
|
+
- **Initial focus depends on the opening key.** Click / Space / Enter / ArrowDown focus the first enabled item; ArrowUp focuses the last enabled item. The same keys re-focus that item when the menu is already open.
|
|
190
194
|
- **Selecting an item closes the menu** by default. To keep the menu open after activation (multi-select pattern), call `$event.preventDefault()` in the item's `(activate)` handler.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, input, booleanAttribute, model, Directive, computed, signal, ElementRef } from '@angular/core';
|
|
3
|
-
import { Collection, injectTextDirection, moveIndex, IdGenerator, adoptHostId, registerHandle, reflectDisabled, resolveListNavigation } from 'forty-cdk/core';
|
|
3
|
+
import { Collection, injectTextDirection, moveIndex, IdGenerator, adoptHostId, registerHandle, reflectDisabled, resolveListNavigation, hostLabelledBy } from 'forty-cdk/core';
|
|
4
4
|
|
|
5
5
|
const FOR_ACCORDION_CONTEXT = new InjectionToken('FOR_ACCORDION_CONTEXT');
|
|
6
6
|
const FOR_ACCORDION_ITEM_CONTEXT = new InjectionToken('FOR_ACCORDION_ITEM_CONTEXT');
|
|
@@ -324,11 +324,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
324
324
|
class ForAccordionContent {
|
|
325
325
|
parent = injectAccordionContext('ForAccordionContent');
|
|
326
326
|
item = injectAccordionItemContext('ForAccordionContent');
|
|
327
|
+
labelledBy = hostLabelledBy(() => this.item.triggerId());
|
|
327
328
|
constructor() {
|
|
328
329
|
this.item.adoptContentId(inject(ElementRef).nativeElement);
|
|
329
330
|
}
|
|
330
331
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAccordionContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
331
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForAccordionContent, isStandalone: true, selector: "[forAccordionContent]", host: { properties: { "id": "item.contentId()", "attr.role": "\"region\"", "attr.aria-labelledby": "
|
|
332
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForAccordionContent, isStandalone: true, selector: "[forAccordionContent]", host: { properties: { "id": "item.contentId()", "attr.role": "\"region\"", "attr.aria-labelledby": "labelledBy()", "attr.aria-hidden": "item.expanded() ? null : \"true\"", "attr.inert": "item.expanded() ? null : \"\"", "attr.data-state": "item.expanded() ? \"open\" : \"closed\"", "attr.data-disabled": "item.disabled() ? \"\" : null", "attr.data-orientation": "parent.orientation()" } }, exportAs: ["forAccordionContent"], ngImport: i0 });
|
|
332
333
|
}
|
|
333
334
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAccordionContent, decorators: [{
|
|
334
335
|
type: Directive,
|
|
@@ -338,7 +339,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
338
339
|
host: {
|
|
339
340
|
'[id]': 'item.contentId()',
|
|
340
341
|
'[attr.role]': '"region"',
|
|
341
|
-
'[attr.aria-labelledby]': '
|
|
342
|
+
'[attr.aria-labelledby]': 'labelledBy()',
|
|
342
343
|
'[attr.aria-hidden]': 'item.expanded() ? null : "true"',
|
|
343
344
|
'[attr.inert]': 'item.expanded() ? null : ""',
|
|
344
345
|
'[attr.data-state]': 'item.expanded() ? "open" : "closed"',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forty-cdk-accordion.mjs","sources":["../../../projects/forty-cdk/accordion/src/accordion-context.ts","../../../projects/forty-cdk/accordion/src/accordion.ts","../../../projects/forty-cdk/accordion/src/accordion-item.ts","../../../projects/forty-cdk/accordion/src/accordion-trigger.ts","../../../projects/forty-cdk/accordion/src/accordion-content.ts","../../../projects/forty-cdk/accordion/src/forty-cdk-accordion.ts"],"sourcesContent":["import { inject, InjectionToken, type Signal } from '@angular/core';\n\nimport { type ListNavigationAction, type WritingDirection } from 'forty-cdk/core';\n\n/**\n * Registry entry for one `ForAccordionTrigger`. Triggers register their host\n * element so the root can drive keyboard navigation over its own triggers\n * without an unscoped DOM query that would leak into nested accordions.\n */\nexport interface ForAccordionTriggerHandle {\n readonly host: HTMLElement;\n readonly disabled: Signal<boolean>;\n}\n\n/**\n * Coordination contract owned by the `ForAccordion` root.\n * Items derive their state from this context; triggers route clicks and\n * keyboard navigation through it.\n */\nexport interface ForAccordionContext {\n readonly multiple: Signal<boolean>;\n readonly collapsible: Signal<boolean>;\n /** Whether the whole accordion is disabled; each item ORs this into its own disabled state. */\n readonly disabled: Signal<boolean>;\n readonly orientation: Signal<'horizontal' | 'vertical'>;\n readonly dir: Signal<WritingDirection>;\n isExpanded(itemValue: string): boolean;\n toggle(itemValue: string): void;\n /** Whether the given item is allowed to collapse right now. */\n canCollapse(itemValue: string): boolean;\n /**\n * Focus a sibling trigger relative to the one currently focused. Disabled\n * triggers are skipped. `currentTrigger` is the element from which the\n * keyboard event originated. Navigation stays within this accordion's own\n * registered triggers, so a nested accordion does not cross-contaminate.\n */\n focusByOffset(currentTrigger: HTMLElement, action: ListNavigationAction): void;\n registerTrigger(handle: ForAccordionTriggerHandle): void;\n unregisterTrigger(handle: ForAccordionTriggerHandle): void;\n}\n\n/**\n * Per-item state contract. Provided by `ForAccordionItem`, consumed by\n * its `ForAccordionTrigger` and `ForAccordionContent`.\n */\nexport interface ForAccordionItemContext {\n readonly expanded: Signal<boolean>;\n readonly disabled: Signal<boolean>;\n readonly value: Signal<string>;\n readonly triggerId: Signal<string>;\n readonly contentId: Signal<string>;\n /** Adopts a consumer-set static `id` on the trigger host into `triggerId`. */\n adoptTriggerId(el: HTMLElement): void;\n /** Adopts a consumer-set static `id` on the content host into `contentId`. */\n adoptContentId(el: HTMLElement): void;\n toggle(): void;\n}\n\nexport const FOR_ACCORDION_CONTEXT = new InjectionToken<ForAccordionContext>(\n 'FOR_ACCORDION_CONTEXT',\n);\n\nexport const FOR_ACCORDION_ITEM_CONTEXT = new InjectionToken<ForAccordionItemContext>(\n 'FOR_ACCORDION_ITEM_CONTEXT',\n);\n\nexport function injectAccordionContext(piece: string): ForAccordionContext {\n const ctx = inject(FOR_ACCORDION_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(`[forty-cdk/accordion] ${piece} must be used inside a [forAccordion] element.`);\n }\n return ctx;\n}\n\nexport function injectAccordionItemContext(piece: string): ForAccordionItemContext {\n const ctx = inject(FOR_ACCORDION_ITEM_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(\n `[forty-cdk/accordion] ${piece} must be used inside a [forAccordionItem] element.`,\n );\n }\n return ctx;\n}\n","import { booleanAttribute, Directive, input, model } from '@angular/core';\n\nimport {\n Collection,\n type ListNavigationAction,\n moveIndex,\n type WritingDirection,\n injectTextDirection,\n} from 'forty-cdk/core';\nimport {\n FOR_ACCORDION_CONTEXT,\n type ForAccordionContext,\n type ForAccordionTriggerHandle,\n} from './accordion-context';\n\n/**\n * Root of the Accordion primitive. Holds the open value(s) and orchestrates\n * single/multiple expansion, collapse rules, and keyboard navigation between\n * triggers.\n *\n * Implements the [WAI-ARIA Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).\n *\n * State is modeled as `value: readonly string[]` regardless of mode:\n * - In single mode (`multiple=false`, default), the array has 0 or 1 element.\n * - In multiple mode, any number of items can be open.\n *\n * @example\n * ```html\n * <div forAccordion [(value)]=\"open\" collapsible>\n * <div forAccordionItem value=\"a\">\n * <h3><button type=\"button\" forAccordionTrigger>A</button></h3>\n * <section forAccordionContent>...</section>\n * </div>\n * <div forAccordionItem value=\"b\">...</div>\n * </div>\n * ```\n */\n@Directive({\n selector: '[forAccordion]',\n exportAs: 'forAccordion',\n host: {\n '[attr.data-orientation]': 'orientation()',\n '[attr.data-disabled]': 'disabled() ? \"\" : null',\n '[attr.dir]': 'dir()',\n },\n providers: [{ provide: FOR_ACCORDION_CONTEXT, useExisting: ForAccordion }],\n})\nexport class ForAccordion implements ForAccordionContext {\n readonly #triggers = new Collection<ForAccordionTriggerHandle>();\n\n /** When true, multiple items can be expanded simultaneously. */\n readonly multiple = input(false, { transform: booleanAttribute });\n\n /**\n * When true, the whole accordion is disabled: every item's trigger reflects\n * the native `disabled` attribute (dropped from the Tab order and skipped by\n * arrow-key navigation) and cannot toggle. Composes with a per-item\n * `[disabled]` — an item is effectively disabled when either is set. Mirrors\n * the root `disabled` on `ForTabs` / `ForStepper` / `ForDisclosure`.\n */\n readonly disabled = input(false, { transform: booleanAttribute });\n\n /**\n * Single mode only: when true, the open item can be collapsed by clicking\n * its trigger. When false, exactly one item stays open at all times once\n * any has been opened.\n */\n readonly collapsible = input(false, { transform: booleanAttribute });\n\n /**\n * Layout direction of the trigger list. `'vertical'` (default) maps\n * ArrowUp/Down to prev/next; `'horizontal'` maps ArrowLeft/Right (with RTL\n * swap when `dir='rtl'`).\n */\n readonly orientation = input<'horizontal' | 'vertical'>('vertical');\n\n /**\n * Writing direction. Only relevant when `orientation='horizontal'`. When\n * unset (default `null`), the inherited ambient direction is resolved from\n * the nearest ancestor carrying a `dir` attribute (or `<html dir>`),\n * defaulting to `'ltr'`. An explicit `[dir]` always wins. The resolved\n * value is reflected to the host `dir` attribute and drives arrow-key\n * semantics.\n */\n readonly _dirInput = input<WritingDirection | null>(null, { alias: 'dir' });\n readonly dir = injectTextDirection(this._dirInput);\n\n /**\n * When true (default), arrow navigation between triggers wraps at the ends —\n * moving past the last trigger focuses the first and vice versa. Set `false`\n * to stop at the boundaries. Mirrors the `loop` input on `ForTabs` and\n * `ForListbox`.\n */\n readonly loop = input(true, { transform: booleanAttribute });\n\n /**\n * Two-way bindable. List of currently expanded item values. In single\n * mode the array has 0 or 1 element. The `model()` change emitter\n * (`(valueChange)`) fires only on internal toggles, never on consumer\n * writes via `[(value)]` — observe transitions without binding back.\n */\n readonly value = model<readonly string[]>([]);\n\n isExpanded(itemValue: string): boolean {\n return this.value().includes(itemValue);\n }\n\n toggle(itemValue: string): void {\n const current = this.value();\n const isOpen = current.includes(itemValue);\n\n if (isOpen) {\n if (!this.canCollapse(itemValue)) {\n return;\n }\n this.value.set(current.filter((v) => v !== itemValue));\n return;\n }\n\n if (this.multiple()) {\n this.value.set([...current, itemValue]);\n } else {\n this.value.set([itemValue]);\n }\n }\n\n canCollapse(_itemValue: string): boolean {\n return this.multiple() || this.collapsible();\n }\n\n focusByOffset(currentTrigger: HTMLElement, action: ListNavigationAction): void {\n const triggers = this.#triggers.items();\n if (triggers.length === 0) {\n return;\n }\n const currentIndex = triggers.findIndex((t) => t.host === currentTrigger);\n if (currentIndex === -1 && action !== 'first' && action !== 'last') {\n return;\n }\n const next = moveIndex(currentIndex < 0 ? 0 : currentIndex, triggers.length, action, {\n loop: this.loop(),\n isDisabled: (i) => triggers[i]?.disabled() ?? false,\n });\n if (next !== null) {\n triggers[next]?.host.focus();\n }\n }\n\n registerTrigger(handle: ForAccordionTriggerHandle): void {\n this.#triggers.register(handle);\n }\n\n unregisterTrigger(handle: ForAccordionTriggerHandle): void {\n this.#triggers.unregister(handle);\n }\n}\n","import { booleanAttribute, computed, Directive, inject, input, signal } from '@angular/core';\n\nimport { adoptHostId, IdGenerator } from 'forty-cdk/core';\nimport {\n FOR_ACCORDION_ITEM_CONTEXT,\n type ForAccordionItemContext,\n injectAccordionContext,\n} from './accordion-context';\n\n/**\n * One section of a `ForAccordion`. Owns the unique `value` identifying this\n * item to the root and exposes the per-item context that the trigger and\n * content read.\n */\n@Directive({\n selector: '[forAccordionItem]',\n exportAs: 'forAccordionItem',\n host: {\n '[attr.data-state]': 'expanded() ? \"open\" : \"closed\"',\n '[attr.data-disabled]': 'disabled() ? \"\" : null',\n '[attr.data-orientation]': 'parent.orientation()',\n },\n providers: [{ provide: FOR_ACCORDION_ITEM_CONTEXT, useExisting: ForAccordionItem }],\n})\nexport class ForAccordionItem implements ForAccordionItemContext {\n protected readonly parent = injectAccordionContext('ForAccordionItem');\n readonly #idGen = inject(IdGenerator);\n\n /** Unique identifier of this item within the accordion. Required. */\n readonly value = input.required<string>();\n\n /**\n * When true, this item's trigger ignores clicks and reflects the native\n * `disabled` attribute (not `aria-disabled`): dropped from the Tab order and\n * skipped by arrow-key navigation, but kept in the accessibility tree so\n * screen readers still announce it. See `ForAccordionTrigger` for the\n * rationale (rule #561 D2). Bind via `[disabled]`; read the composed\n * {@link disabled} for state.\n */\n readonly disabledInput = input(false, { transform: booleanAttribute, alias: 'disabled' });\n\n /**\n * Effective disabled: this item's own `[disabled]` OR'd with the root\n * `[forAccordion]`'s `disabled`. Everything that gates on the item's disabled\n * state — native attribute reflection, trigger click, arrow-navigation skip,\n * `data-disabled` — reads this, so disabling the whole accordion disables\n * every item.\n */\n readonly disabled = computed(() => this.disabledInput() || this.parent.disabled());\n\n readonly #triggerId = signal(this.#idGen.next('for-accordion-trigger'));\n readonly #contentId = signal(this.#idGen.next('for-accordion-content'));\n\n readonly triggerId = this.#triggerId.asReadonly();\n readonly contentId = this.#contentId.asReadonly();\n\n readonly expanded = computed(() => this.parent.isExpanded(this.value()));\n\n /**\n * Adopts a consumer-set static `id` on the `[forAccordionTrigger]` host into\n * `triggerId` (preserving anchors / external `aria-labelledby` references /\n * label `for`) instead of letting the `[id]` host binding clobber it.\n */\n adoptTriggerId(el: HTMLElement): void {\n adoptHostId(el, this.#triggerId);\n }\n\n /** Adopts a consumer-set static `id` on the content host into `contentId`. */\n adoptContentId(el: HTMLElement): void {\n adoptHostId(el, this.#contentId);\n }\n\n toggle(): void {\n if (this.disabled()) {\n return;\n }\n this.parent.toggle(this.value());\n }\n}\n","import { computed, Directive, ElementRef, inject } from '@angular/core';\n\nimport { registerHandle, reflectDisabled, resolveListNavigation } from 'forty-cdk/core';\nimport { injectAccordionContext, injectAccordionItemContext } from './accordion-context';\n\n/**\n * Header button for a `ForAccordionItem`. Apply on a `<button>` wrapped in a\n * heading element (`<h2>`–`<h6>`) so APG landmark navigation works. The\n * directive host-binds `type=\"button\"` so a trigger inside a `<form>` never\n * submits it on toggle.\n *\n * Handles ARIA wiring, click-to-toggle, and the recommended keyboard\n * navigation (ArrowDown / ArrowUp / Home / End).\n *\n * `aria-controls` is emitted only while the item is expanded — mirroring the\n * overlay triggers' open-only gating — so the reference never dangles at an\n * unmounted panel under the recommended `@if (item.expanded())` mount pattern.\n */\n@Directive({\n selector: '[forAccordionTrigger]',\n exportAs: 'forAccordionTrigger',\n host: {\n type: 'button',\n '[id]': 'item.triggerId()',\n '[attr.aria-expanded]': 'item.expanded() ? \"true\" : \"false\"',\n '[attr.aria-controls]': 'item.expanded() ? item.contentId() : null',\n '[attr.aria-disabled]': 'ariaDisabled() ? \"true\" : null',\n '[attr.data-state]': 'item.expanded() ? \"open\" : \"closed\"',\n '[attr.data-disabled]': 'item.disabled() ? \"\" : null',\n '[attr.data-orientation]': 'parent.orientation()',\n '(click)': 'item.toggle()',\n '(keydown)': 'onKeyDown($event)',\n },\n})\nexport class ForAccordionTrigger {\n readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);\n protected readonly parent = injectAccordionContext('ForAccordionTrigger');\n protected readonly item = injectAccordionItemContext('ForAccordionTrigger');\n\n /**\n * APG: aria-disabled is true only when the panel is open AND the accordion\n * disallows collapse. A real `disabled` item is reflected via the native\n * `disabled` attribute instead — the sanctioned exception in rule #561 (D2):\n * the trigger is a real single-purpose `<button>`, not a roving collection\n * item (every trigger stays independently in the Tab order; the arrow-key\n * navigation is an APG-optional enhancement layered on top, not a\n * roving-tabindex collection), so native `disabled` is correct here. The\n * button stays in the accessibility tree — screen readers still announce it\n * as unavailable in browse mode — while being dropped from the Tab order and\n * the arrow-key navigation (which `focusByOffset` already skips), keeping\n * disabled triggers uniformly unreachable. The APG Accordion pattern does not\n * require disabled headers to remain focusable.\n */\n protected readonly ariaDisabled = computed(() => {\n if (this.item.disabled()) {\n return false;\n }\n return this.item.expanded() && !this.parent.canCollapse(this.item.value());\n });\n\n constructor() {\n this.item.adoptTriggerId(this.#host.nativeElement);\n const handle = {\n host: this.#host.nativeElement,\n disabled: this.item.disabled,\n };\n registerHandle(\n handle,\n (h) => this.parent.registerTrigger(h),\n (h) => this.parent.unregisterTrigger(h),\n );\n reflectDisabled(this.item.disabled);\n }\n\n protected onKeyDown(event: KeyboardEvent): void {\n const action = resolveListNavigation(event, {\n orientation: this.parent.orientation(),\n dir: this.parent.dir(),\n });\n if (!action) {\n return;\n }\n event.preventDefault();\n this.parent.focusByOffset(this.#host.nativeElement, action);\n }\n}\n","import { Directive, ElementRef, inject } from '@angular/core';\n\nimport { injectAccordionContext, injectAccordionItemContext } from './accordion-context';\n\n/**\n * Panel revealed by a `ForAccordionTrigger`. The directive does not manage\n * DOM presence — wrap with `@if (item.expanded())` so panels mount and\n * unmount with the expanded state and `animate.enter` / `animate.leave`\n * work natively. If the consumer prefers to keep the panel mounted (for\n * CSS-only transitions or to preserve internal state), the directive\n * reflects `aria-hidden=\"true\"` and `inert` while closed so the panel is\n * removed from the accessibility tree and focus order automatically.\n *\n * APG note: `role=\"region\"` adds the panel to the landmark navigation tree.\n * If the accordion has 6+ simultaneously expandable panels, consider not\n * using region — the directive will gain an opt-out input when this comes up\n * in real usage.\n */\n@Directive({\n selector: '[forAccordionContent]',\n exportAs: 'forAccordionContent',\n host: {\n '[id]': 'item.contentId()',\n '[attr.role]': '\"region\"',\n '[attr.aria-labelledby]': 'item.triggerId()',\n '[attr.aria-hidden]': 'item.expanded() ? null : \"true\"',\n '[attr.inert]': 'item.expanded() ? null : \"\"',\n '[attr.data-state]': 'item.expanded() ? \"open\" : \"closed\"',\n '[attr.data-disabled]': 'item.disabled() ? \"\" : null',\n '[attr.data-orientation]': 'parent.orientation()',\n },\n})\nexport class ForAccordionContent {\n protected readonly parent = injectAccordionContext('ForAccordionContent');\n protected readonly item = injectAccordionItemContext('ForAccordionContent');\n\n constructor() {\n this.item.adoptContentId(inject<ElementRef<HTMLElement>>(ElementRef).nativeElement);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"forty-cdk-accordion.mjs","sources":["../../../projects/forty-cdk/accordion/src/accordion-context.ts","../../../projects/forty-cdk/accordion/src/accordion.ts","../../../projects/forty-cdk/accordion/src/accordion-item.ts","../../../projects/forty-cdk/accordion/src/accordion-trigger.ts","../../../projects/forty-cdk/accordion/src/accordion-content.ts","../../../projects/forty-cdk/accordion/src/forty-cdk-accordion.ts"],"sourcesContent":["import { inject, InjectionToken, type Signal } from '@angular/core';\n\nimport { type ListNavigationAction, type WritingDirection } from 'forty-cdk/core';\n\n/**\n * Registry entry for one `ForAccordionTrigger`. Triggers register their host\n * element so the root can drive keyboard navigation over its own triggers\n * without an unscoped DOM query that would leak into nested accordions.\n */\nexport interface ForAccordionTriggerHandle {\n readonly host: HTMLElement;\n readonly disabled: Signal<boolean>;\n}\n\n/**\n * Coordination contract owned by the `ForAccordion` root.\n * Items derive their state from this context; triggers route clicks and\n * keyboard navigation through it.\n */\nexport interface ForAccordionContext {\n readonly multiple: Signal<boolean>;\n readonly collapsible: Signal<boolean>;\n /** Whether the whole accordion is disabled; each item ORs this into its own disabled state. */\n readonly disabled: Signal<boolean>;\n readonly orientation: Signal<'horizontal' | 'vertical'>;\n readonly dir: Signal<WritingDirection>;\n isExpanded(itemValue: string): boolean;\n toggle(itemValue: string): void;\n /** Whether the given item is allowed to collapse right now. */\n canCollapse(itemValue: string): boolean;\n /**\n * Focus a sibling trigger relative to the one currently focused. Disabled\n * triggers are skipped. `currentTrigger` is the element from which the\n * keyboard event originated. Navigation stays within this accordion's own\n * registered triggers, so a nested accordion does not cross-contaminate.\n */\n focusByOffset(currentTrigger: HTMLElement, action: ListNavigationAction): void;\n registerTrigger(handle: ForAccordionTriggerHandle): void;\n unregisterTrigger(handle: ForAccordionTriggerHandle): void;\n}\n\n/**\n * Per-item state contract. Provided by `ForAccordionItem`, consumed by\n * its `ForAccordionTrigger` and `ForAccordionContent`.\n */\nexport interface ForAccordionItemContext {\n readonly expanded: Signal<boolean>;\n readonly disabled: Signal<boolean>;\n readonly value: Signal<string>;\n readonly triggerId: Signal<string>;\n readonly contentId: Signal<string>;\n /** Adopts a consumer-set static `id` on the trigger host into `triggerId`. */\n adoptTriggerId(el: HTMLElement): void;\n /** Adopts a consumer-set static `id` on the content host into `contentId`. */\n adoptContentId(el: HTMLElement): void;\n toggle(): void;\n}\n\nexport const FOR_ACCORDION_CONTEXT = new InjectionToken<ForAccordionContext>(\n 'FOR_ACCORDION_CONTEXT',\n);\n\nexport const FOR_ACCORDION_ITEM_CONTEXT = new InjectionToken<ForAccordionItemContext>(\n 'FOR_ACCORDION_ITEM_CONTEXT',\n);\n\nexport function injectAccordionContext(piece: string): ForAccordionContext {\n const ctx = inject(FOR_ACCORDION_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(`[forty-cdk/accordion] ${piece} must be used inside a [forAccordion] element.`);\n }\n return ctx;\n}\n\nexport function injectAccordionItemContext(piece: string): ForAccordionItemContext {\n const ctx = inject(FOR_ACCORDION_ITEM_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(\n `[forty-cdk/accordion] ${piece} must be used inside a [forAccordionItem] element.`,\n );\n }\n return ctx;\n}\n","import { booleanAttribute, Directive, input, model } from '@angular/core';\n\nimport {\n Collection,\n type ListNavigationAction,\n moveIndex,\n type WritingDirection,\n injectTextDirection,\n} from 'forty-cdk/core';\nimport {\n FOR_ACCORDION_CONTEXT,\n type ForAccordionContext,\n type ForAccordionTriggerHandle,\n} from './accordion-context';\n\n/**\n * Root of the Accordion primitive. Holds the open value(s) and orchestrates\n * single/multiple expansion, collapse rules, and keyboard navigation between\n * triggers.\n *\n * Implements the [WAI-ARIA Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).\n *\n * State is modeled as `value: readonly string[]` regardless of mode:\n * - In single mode (`multiple=false`, default), the array has 0 or 1 element.\n * - In multiple mode, any number of items can be open.\n *\n * @example\n * ```html\n * <div forAccordion [(value)]=\"open\" collapsible>\n * <div forAccordionItem value=\"a\">\n * <h3><button type=\"button\" forAccordionTrigger>A</button></h3>\n * <section forAccordionContent>...</section>\n * </div>\n * <div forAccordionItem value=\"b\">...</div>\n * </div>\n * ```\n */\n@Directive({\n selector: '[forAccordion]',\n exportAs: 'forAccordion',\n host: {\n '[attr.data-orientation]': 'orientation()',\n '[attr.data-disabled]': 'disabled() ? \"\" : null',\n '[attr.dir]': 'dir()',\n },\n providers: [{ provide: FOR_ACCORDION_CONTEXT, useExisting: ForAccordion }],\n})\nexport class ForAccordion implements ForAccordionContext {\n readonly #triggers = new Collection<ForAccordionTriggerHandle>();\n\n /** When true, multiple items can be expanded simultaneously. */\n readonly multiple = input(false, { transform: booleanAttribute });\n\n /**\n * When true, the whole accordion is disabled: every item's trigger reflects\n * the native `disabled` attribute (dropped from the Tab order and skipped by\n * arrow-key navigation) and cannot toggle. Composes with a per-item\n * `[disabled]` — an item is effectively disabled when either is set. Mirrors\n * the root `disabled` on `ForTabs` / `ForStepper` / `ForDisclosure`.\n */\n readonly disabled = input(false, { transform: booleanAttribute });\n\n /**\n * Single mode only: when true, the open item can be collapsed by clicking\n * its trigger. When false, exactly one item stays open at all times once\n * any has been opened.\n */\n readonly collapsible = input(false, { transform: booleanAttribute });\n\n /**\n * Layout direction of the trigger list. `'vertical'` (default) maps\n * ArrowUp/Down to prev/next; `'horizontal'` maps ArrowLeft/Right (with RTL\n * swap when `dir='rtl'`).\n */\n readonly orientation = input<'horizontal' | 'vertical'>('vertical');\n\n /**\n * Writing direction. Only relevant when `orientation='horizontal'`. When\n * unset (default `null`), the inherited ambient direction is resolved from\n * the nearest ancestor carrying a `dir` attribute (or `<html dir>`),\n * defaulting to `'ltr'`. An explicit `[dir]` always wins. The resolved\n * value is reflected to the host `dir` attribute and drives arrow-key\n * semantics.\n */\n readonly _dirInput = input<WritingDirection | null>(null, { alias: 'dir' });\n readonly dir = injectTextDirection(this._dirInput);\n\n /**\n * When true (default), arrow navigation between triggers wraps at the ends —\n * moving past the last trigger focuses the first and vice versa. Set `false`\n * to stop at the boundaries. Mirrors the `loop` input on `ForTabs` and\n * `ForListbox`.\n */\n readonly loop = input(true, { transform: booleanAttribute });\n\n /**\n * Two-way bindable. List of currently expanded item values. In single\n * mode the array has 0 or 1 element. The `model()` change emitter\n * (`(valueChange)`) fires only on internal toggles, never on consumer\n * writes via `[(value)]` — observe transitions without binding back.\n */\n readonly value = model<readonly string[]>([]);\n\n isExpanded(itemValue: string): boolean {\n return this.value().includes(itemValue);\n }\n\n toggle(itemValue: string): void {\n const current = this.value();\n const isOpen = current.includes(itemValue);\n\n if (isOpen) {\n if (!this.canCollapse(itemValue)) {\n return;\n }\n this.value.set(current.filter((v) => v !== itemValue));\n return;\n }\n\n if (this.multiple()) {\n this.value.set([...current, itemValue]);\n } else {\n this.value.set([itemValue]);\n }\n }\n\n canCollapse(_itemValue: string): boolean {\n return this.multiple() || this.collapsible();\n }\n\n focusByOffset(currentTrigger: HTMLElement, action: ListNavigationAction): void {\n const triggers = this.#triggers.items();\n if (triggers.length === 0) {\n return;\n }\n const currentIndex = triggers.findIndex((t) => t.host === currentTrigger);\n if (currentIndex === -1 && action !== 'first' && action !== 'last') {\n return;\n }\n const next = moveIndex(currentIndex < 0 ? 0 : currentIndex, triggers.length, action, {\n loop: this.loop(),\n isDisabled: (i) => triggers[i]?.disabled() ?? false,\n });\n if (next !== null) {\n triggers[next]?.host.focus();\n }\n }\n\n registerTrigger(handle: ForAccordionTriggerHandle): void {\n this.#triggers.register(handle);\n }\n\n unregisterTrigger(handle: ForAccordionTriggerHandle): void {\n this.#triggers.unregister(handle);\n }\n}\n","import { booleanAttribute, computed, Directive, inject, input, signal } from '@angular/core';\n\nimport { adoptHostId, IdGenerator } from 'forty-cdk/core';\nimport {\n FOR_ACCORDION_ITEM_CONTEXT,\n type ForAccordionItemContext,\n injectAccordionContext,\n} from './accordion-context';\n\n/**\n * One section of a `ForAccordion`. Owns the unique `value` identifying this\n * item to the root and exposes the per-item context that the trigger and\n * content read.\n */\n@Directive({\n selector: '[forAccordionItem]',\n exportAs: 'forAccordionItem',\n host: {\n '[attr.data-state]': 'expanded() ? \"open\" : \"closed\"',\n '[attr.data-disabled]': 'disabled() ? \"\" : null',\n '[attr.data-orientation]': 'parent.orientation()',\n },\n providers: [{ provide: FOR_ACCORDION_ITEM_CONTEXT, useExisting: ForAccordionItem }],\n})\nexport class ForAccordionItem implements ForAccordionItemContext {\n protected readonly parent = injectAccordionContext('ForAccordionItem');\n readonly #idGen = inject(IdGenerator);\n\n /** Unique identifier of this item within the accordion. Required. */\n readonly value = input.required<string>();\n\n /**\n * When true, this item's trigger ignores clicks and reflects the native\n * `disabled` attribute (not `aria-disabled`): dropped from the Tab order and\n * skipped by arrow-key navigation, but kept in the accessibility tree so\n * screen readers still announce it. See `ForAccordionTrigger` for the\n * rationale (rule #561 D2). Bind via `[disabled]`; read the composed\n * {@link disabled} for state.\n */\n readonly disabledInput = input(false, { transform: booleanAttribute, alias: 'disabled' });\n\n /**\n * Effective disabled: this item's own `[disabled]` OR'd with the root\n * `[forAccordion]`'s `disabled`. Everything that gates on the item's disabled\n * state — native attribute reflection, trigger click, arrow-navigation skip,\n * `data-disabled` — reads this, so disabling the whole accordion disables\n * every item.\n */\n readonly disabled = computed(() => this.disabledInput() || this.parent.disabled());\n\n readonly #triggerId = signal(this.#idGen.next('for-accordion-trigger'));\n readonly #contentId = signal(this.#idGen.next('for-accordion-content'));\n\n readonly triggerId = this.#triggerId.asReadonly();\n readonly contentId = this.#contentId.asReadonly();\n\n readonly expanded = computed(() => this.parent.isExpanded(this.value()));\n\n /**\n * Adopts a consumer-set static `id` on the `[forAccordionTrigger]` host into\n * `triggerId` (preserving anchors / external `aria-labelledby` references /\n * label `for`) instead of letting the `[id]` host binding clobber it.\n */\n adoptTriggerId(el: HTMLElement): void {\n adoptHostId(el, this.#triggerId);\n }\n\n /** Adopts a consumer-set static `id` on the content host into `contentId`. */\n adoptContentId(el: HTMLElement): void {\n adoptHostId(el, this.#contentId);\n }\n\n toggle(): void {\n if (this.disabled()) {\n return;\n }\n this.parent.toggle(this.value());\n }\n}\n","import { computed, Directive, ElementRef, inject } from '@angular/core';\n\nimport { registerHandle, reflectDisabled, resolveListNavigation } from 'forty-cdk/core';\nimport { injectAccordionContext, injectAccordionItemContext } from './accordion-context';\n\n/**\n * Header button for a `ForAccordionItem`. Apply on a `<button>` wrapped in a\n * heading element (`<h2>`–`<h6>`) so APG landmark navigation works. The\n * directive host-binds `type=\"button\"` so a trigger inside a `<form>` never\n * submits it on toggle.\n *\n * Handles ARIA wiring, click-to-toggle, and the recommended keyboard\n * navigation (ArrowDown / ArrowUp / Home / End).\n *\n * `aria-controls` is emitted only while the item is expanded — mirroring the\n * overlay triggers' open-only gating — so the reference never dangles at an\n * unmounted panel under the recommended `@if (item.expanded())` mount pattern.\n */\n@Directive({\n selector: '[forAccordionTrigger]',\n exportAs: 'forAccordionTrigger',\n host: {\n type: 'button',\n '[id]': 'item.triggerId()',\n '[attr.aria-expanded]': 'item.expanded() ? \"true\" : \"false\"',\n '[attr.aria-controls]': 'item.expanded() ? item.contentId() : null',\n '[attr.aria-disabled]': 'ariaDisabled() ? \"true\" : null',\n '[attr.data-state]': 'item.expanded() ? \"open\" : \"closed\"',\n '[attr.data-disabled]': 'item.disabled() ? \"\" : null',\n '[attr.data-orientation]': 'parent.orientation()',\n '(click)': 'item.toggle()',\n '(keydown)': 'onKeyDown($event)',\n },\n})\nexport class ForAccordionTrigger {\n readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);\n protected readonly parent = injectAccordionContext('ForAccordionTrigger');\n protected readonly item = injectAccordionItemContext('ForAccordionTrigger');\n\n /**\n * APG: aria-disabled is true only when the panel is open AND the accordion\n * disallows collapse. A real `disabled` item is reflected via the native\n * `disabled` attribute instead — the sanctioned exception in rule #561 (D2):\n * the trigger is a real single-purpose `<button>`, not a roving collection\n * item (every trigger stays independently in the Tab order; the arrow-key\n * navigation is an APG-optional enhancement layered on top, not a\n * roving-tabindex collection), so native `disabled` is correct here. The\n * button stays in the accessibility tree — screen readers still announce it\n * as unavailable in browse mode — while being dropped from the Tab order and\n * the arrow-key navigation (which `focusByOffset` already skips), keeping\n * disabled triggers uniformly unreachable. The APG Accordion pattern does not\n * require disabled headers to remain focusable.\n */\n protected readonly ariaDisabled = computed(() => {\n if (this.item.disabled()) {\n return false;\n }\n return this.item.expanded() && !this.parent.canCollapse(this.item.value());\n });\n\n constructor() {\n this.item.adoptTriggerId(this.#host.nativeElement);\n const handle = {\n host: this.#host.nativeElement,\n disabled: this.item.disabled,\n };\n registerHandle(\n handle,\n (h) => this.parent.registerTrigger(h),\n (h) => this.parent.unregisterTrigger(h),\n );\n reflectDisabled(this.item.disabled);\n }\n\n protected onKeyDown(event: KeyboardEvent): void {\n const action = resolveListNavigation(event, {\n orientation: this.parent.orientation(),\n dir: this.parent.dir(),\n });\n if (!action) {\n return;\n }\n event.preventDefault();\n this.parent.focusByOffset(this.#host.nativeElement, action);\n }\n}\n","import { Directive, ElementRef, inject } from '@angular/core';\n\nimport { hostLabelledBy } from 'forty-cdk/core';\nimport { injectAccordionContext, injectAccordionItemContext } from './accordion-context';\n\n/**\n * Panel revealed by a `ForAccordionTrigger`. The directive does not manage\n * DOM presence — wrap with `@if (item.expanded())` so panels mount and\n * unmount with the expanded state and `animate.enter` / `animate.leave`\n * work natively. If the consumer prefers to keep the panel mounted (for\n * CSS-only transitions or to preserve internal state), the directive\n * reflects `aria-hidden=\"true\"` and `inert` while closed so the panel is\n * removed from the accessibility tree and focus order automatically.\n *\n * APG note: `role=\"region\"` adds the panel to the landmark navigation tree.\n * If the accordion has 6+ simultaneously expandable panels, consider not\n * using region — the directive will gain an opt-out input when this comes up\n * in real usage.\n */\n@Directive({\n selector: '[forAccordionContent]',\n exportAs: 'forAccordionContent',\n host: {\n '[id]': 'item.contentId()',\n '[attr.role]': '\"region\"',\n '[attr.aria-labelledby]': 'labelledBy()',\n '[attr.aria-hidden]': 'item.expanded() ? null : \"true\"',\n '[attr.inert]': 'item.expanded() ? null : \"\"',\n '[attr.data-state]': 'item.expanded() ? \"open\" : \"closed\"',\n '[attr.data-disabled]': 'item.disabled() ? \"\" : null',\n '[attr.data-orientation]': 'parent.orientation()',\n },\n})\nexport class ForAccordionContent {\n protected readonly parent = injectAccordionContext('ForAccordionContent');\n protected readonly item = injectAccordionItemContext('ForAccordionContent');\n\n protected readonly labelledBy = hostLabelledBy(() => this.item.triggerId());\n\n constructor() {\n this.item.adoptContentId(inject<ElementRef<HTMLElement>>(ElementRef).nativeElement);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, input, Directive, booleanAttribute } from '@angular/core';
|
|
3
|
-
import { createDefaults } from 'forty-cdk/core';
|
|
3
|
+
import { createDefaults, hostAriaLabel } from 'forty-cdk/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Library fallback for breadcrumbs defaults, read at the root injector when no
|
|
@@ -65,8 +65,9 @@ class ForBreadcrumbs {
|
|
|
65
65
|
*/
|
|
66
66
|
ariaLabel = input(this.#defaults.label, /* @ts-ignore */
|
|
67
67
|
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
68
|
+
resolvedAriaLabel = hostAriaLabel(() => this.ariaLabel() || null);
|
|
68
69
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
69
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForBreadcrumbs, isStandalone: true, selector: "[forBreadcrumbs]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "
|
|
70
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForBreadcrumbs, isStandalone: true, selector: "[forBreadcrumbs]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "resolvedAriaLabel()" } }, exportAs: ["forBreadcrumbs"], ngImport: i0 });
|
|
70
71
|
}
|
|
71
72
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbs, decorators: [{
|
|
72
73
|
type: Directive,
|
|
@@ -75,7 +76,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
75
76
|
exportAs: 'forBreadcrumbs',
|
|
76
77
|
host: {
|
|
77
78
|
role: 'navigation',
|
|
78
|
-
'[attr.aria-label]': '
|
|
79
|
+
'[attr.aria-label]': 'resolvedAriaLabel()',
|
|
79
80
|
},
|
|
80
81
|
}]
|
|
81
82
|
}], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forty-cdk-breadcrumbs.mjs","sources":["../../../projects/forty-cdk/breadcrumbs/src/breadcrumbs-defaults.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumbs.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-item.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-separator.ts","../../../projects/forty-cdk/breadcrumbs/src/forty-cdk-breadcrumbs.ts"],"sourcesContent":["import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant breadcrumb trails in the surrounding\n * injector scope. Configure with `provideForBreadcrumbsDefaults` either at the\n * application root or in any component's `providers` array; partial overrides\n * merge with the parent scope.\n */\nexport interface ForBreadcrumbsDefaults {\n /**\n * Accessible name for the breadcrumb `navigation` landmark, for trails that\n * don't set `[ariaLabel]` locally. Localize it here to translate every\n * breadcrumb landmark in the scope.\n */\n label: string;\n}\n\n/**\n * Library fallback for breadcrumbs defaults, read at the root injector when no\n * consumer has called `provideForBreadcrumbsDefaults`. Exported for the shared\n * defaults contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_BREADCRUMBS_FALLBACK_DEFAULTS: ForBreadcrumbsDefaults = {\n label: 'Breadcrumb',\n};\n\nconst { token, provideDefaults } = createDefaults<ForBreadcrumbsDefaults>(\n 'FOR_BREADCRUMBS_DEFAULTS',\n FOR_BREADCRUMBS_FALLBACK_DEFAULTS,\n);\n\n/** Token holding the resolved breadcrumbs defaults for the current scope. */\nexport const FOR_BREADCRUMBS_DEFAULTS = token;\n\n/**\n * Configures forty-cdk breadcrumbs defaults for this injector scope. Partial\n * overrides inherit unspecified keys from the parent scope (or library\n * defaults at the root).\n */\nexport function provideForBreadcrumbsDefaults(\n defaults: Partial<ForBreadcrumbsDefaults> = {},\n): Provider[] {\n return provideDefaults(defaults);\n}\n","import { Directive, inject, input } from '@angular/core';\n\nimport { FOR_BREADCRUMBS_DEFAULTS } from './breadcrumbs-defaults';\n\n/**\n * Headless breadcrumb trail implementing the\n * [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/):\n * a `navigation` landmark wrapping a set of links that reveal the page's\n * position within the site hierarchy.\n *\n * The directive contributes only the landmark role + accessible name; the\n * consumer owns the markup structure (an `<ol>` of `<li>` links is the APG\n * recommendation) and applies their own styles. Each link decorated with\n * `[forBreadcrumbItem]` can mark itself the current page via `current`, and\n * visual dividers decorated with `[forBreadcrumbSeparator]` are hidden from\n * assistive technology.\n *\n * There is no keyboard interaction or focus management beyond what the native\n * links provide — breadcrumb links are ordinary tab stops.\n *\n * @example\n * ```html\n * <nav forBreadcrumbs>\n * <ol>\n * <li><a forBreadcrumbItem href=\"/\">Home</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library\">Library</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library/data\" current>Data</a></li>\n * </ol>\n * </nav>\n * ```\n */\n@Directive({\n selector: '[forBreadcrumbs]',\n exportAs: 'forBreadcrumbs',\n host: {\n role: 'navigation',\n '[attr.aria-label]': '
|
|
1
|
+
{"version":3,"file":"forty-cdk-breadcrumbs.mjs","sources":["../../../projects/forty-cdk/breadcrumbs/src/breadcrumbs-defaults.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumbs.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-item.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-separator.ts","../../../projects/forty-cdk/breadcrumbs/src/forty-cdk-breadcrumbs.ts"],"sourcesContent":["import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant breadcrumb trails in the surrounding\n * injector scope. Configure with `provideForBreadcrumbsDefaults` either at the\n * application root or in any component's `providers` array; partial overrides\n * merge with the parent scope.\n */\nexport interface ForBreadcrumbsDefaults {\n /**\n * Accessible name for the breadcrumb `navigation` landmark, for trails that\n * don't set `[ariaLabel]` locally. Localize it here to translate every\n * breadcrumb landmark in the scope.\n */\n label: string;\n}\n\n/**\n * Library fallback for breadcrumbs defaults, read at the root injector when no\n * consumer has called `provideForBreadcrumbsDefaults`. Exported for the shared\n * defaults contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_BREADCRUMBS_FALLBACK_DEFAULTS: ForBreadcrumbsDefaults = {\n label: 'Breadcrumb',\n};\n\nconst { token, provideDefaults } = createDefaults<ForBreadcrumbsDefaults>(\n 'FOR_BREADCRUMBS_DEFAULTS',\n FOR_BREADCRUMBS_FALLBACK_DEFAULTS,\n);\n\n/** Token holding the resolved breadcrumbs defaults for the current scope. */\nexport const FOR_BREADCRUMBS_DEFAULTS = token;\n\n/**\n * Configures forty-cdk breadcrumbs defaults for this injector scope. Partial\n * overrides inherit unspecified keys from the parent scope (or library\n * defaults at the root).\n */\nexport function provideForBreadcrumbsDefaults(\n defaults: Partial<ForBreadcrumbsDefaults> = {},\n): Provider[] {\n return provideDefaults(defaults);\n}\n","import { Directive, inject, input } from '@angular/core';\n\nimport { hostAriaLabel } from 'forty-cdk/core';\nimport { FOR_BREADCRUMBS_DEFAULTS } from './breadcrumbs-defaults';\n\n/**\n * Headless breadcrumb trail implementing the\n * [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/):\n * a `navigation` landmark wrapping a set of links that reveal the page's\n * position within the site hierarchy.\n *\n * The directive contributes only the landmark role + accessible name; the\n * consumer owns the markup structure (an `<ol>` of `<li>` links is the APG\n * recommendation) and applies their own styles. Each link decorated with\n * `[forBreadcrumbItem]` can mark itself the current page via `current`, and\n * visual dividers decorated with `[forBreadcrumbSeparator]` are hidden from\n * assistive technology.\n *\n * There is no keyboard interaction or focus management beyond what the native\n * links provide — breadcrumb links are ordinary tab stops.\n *\n * @example\n * ```html\n * <nav forBreadcrumbs>\n * <ol>\n * <li><a forBreadcrumbItem href=\"/\">Home</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library\">Library</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library/data\" current>Data</a></li>\n * </ol>\n * </nav>\n * ```\n */\n@Directive({\n selector: '[forBreadcrumbs]',\n exportAs: 'forBreadcrumbs',\n host: {\n role: 'navigation',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n },\n})\nexport class ForBreadcrumbs {\n readonly #defaults = inject(FOR_BREADCRUMBS_DEFAULTS);\n\n /**\n * Accessible label for the navigation landmark. Defaults to the scope's\n * `label` (`'Breadcrumb'` unless overridden via\n * `provideForBreadcrumbsDefaults`), so a bare `<nav forBreadcrumbs>` is\n * already a correctly labelled landmark — the conventional name for this\n * pattern. Override it per-instance via `[ariaLabel]` (or point a native\n * `aria-labelledby` at a visible heading) when the page hosts more than one\n * breadcrumb trail and they need to be told apart; set it to `null` to drop\n * the attribute and rely on `aria-labelledby`.\n */\n readonly ariaLabel = input<string | null>(this.#defaults.label);\n\n protected readonly resolvedAriaLabel = hostAriaLabel(() => this.ariaLabel() || null);\n}\n","import { booleanAttribute, Directive, input } from '@angular/core';\n\n/**\n * A single link within a breadcrumb trail. Apply on an `<a>` so navigation is\n * native. Set `current` on the link that points to the page the user is on —\n * it reflects [`aria-current=\"page\"`](https://www.w3.org/TR/wai-aria-1.2/#aria-current)\n * so assistive technology announces it as the current location.\n */\n@Directive({\n selector: '[forBreadcrumbItem]',\n exportAs: 'forBreadcrumbItem',\n host: {\n '[attr.aria-current]': 'current() ? \"page\" : null',\n },\n})\nexport class ForBreadcrumbItem {\n /**\n * Whether this item represents the page the user is currently on. The\n * current item reflects `aria-current=\"page\"`; all others emit no\n * `aria-current` attribute.\n */\n readonly current = input(false, { transform: booleanAttribute });\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Decorative divider rendered between breadcrumb items (a `/`, `>`, chevron,\n * etc.). Reflects `aria-hidden=\"true\"` so assistive technology skips it — the\n * trail's structure is already conveyed by the list and the links, making the\n * visual separator redundant in the accessibility tree.\n */\n@Directive({\n selector: '[forBreadcrumbSeparator]',\n exportAs: 'forBreadcrumbSeparator',\n host: {\n 'aria-hidden': 'true',\n },\n})\nexport class ForBreadcrumbSeparator {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, DOCUMENT, PLATFORM_ID, signal, DestroyRef, Injectable, ElementRef, computed, input, booleanAttribute, output, Directive } from '@angular/core';
|
|
3
|
+
import { isNonTouchPointer, FOR_FIELDSET_CONTEXT } from 'forty-cdk/core';
|
|
3
4
|
import { isPlatformBrowser } from '@angular/common';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -99,10 +100,12 @@ function injectFocusVisible() {
|
|
|
99
100
|
* hovering the host element. State is set on `pointerenter` and cleared on
|
|
100
101
|
* `pointerleave`.
|
|
101
102
|
*
|
|
102
|
-
* Touch is suppressed
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* Touch is suppressed through the shared `isNonTouchPointer` predicate: a
|
|
104
|
+
* `pointerenter` whose `pointerType` is `'touch'` does not set the hovered
|
|
105
|
+
* state, so the emulated mouse-enter a tap produces on a touchscreen never
|
|
106
|
+
* leaves the element stuck in a hovered state after the finger lifts. Only
|
|
107
|
+
* `'mouse'` / `'pen'` pointers report hover — this is the pen-inclusive hover
|
|
108
|
+
* vocabulary, not the menu family's mouse-only `isHoverCapablePointer`.
|
|
106
109
|
*
|
|
107
110
|
* Attaches `pointerenter` / `pointerleave` listeners to the host element. The
|
|
108
111
|
* `disabled` option short-circuits the result reactively (a pure `computed`,
|
|
@@ -125,7 +128,7 @@ function injectHovered(opts) {
|
|
|
125
128
|
const hovered = signal(false, /* @ts-ignore */
|
|
126
129
|
...(ngDevMode ? [{ debugName: "hovered" }] : /* istanbul ignore next */ []));
|
|
127
130
|
const onPointerEnter = (event) => {
|
|
128
|
-
if (event
|
|
131
|
+
if (!isNonTouchPointer(event)) {
|
|
129
132
|
return;
|
|
130
133
|
}
|
|
131
134
|
if (disabled?.()) {
|
|
@@ -232,7 +235,10 @@ function injectPressed(opts) {
|
|
|
232
235
|
*
|
|
233
236
|
* Disabled stays focusable: per the APG a disabled button must remain reachable by assistive
|
|
234
237
|
* technology, so the native `disabled` attribute is never set. Instead `aria-disabled="true"` and
|
|
235
|
-
* `data-disabled=""` are reflected and the activation handler becomes a no-op.
|
|
238
|
+
* `data-disabled=""` are reflected and the activation handler becomes a no-op. `disabled` composes
|
|
239
|
+
* with a surrounding `[forFieldset]`: a disabled group disables the button too (`aria-disabled` +
|
|
240
|
+
* `data-disabled`, activation suppressed), which is what makes a non-native host (`<div forButton>`)
|
|
241
|
+
* behave like a native button inside a native `<fieldset disabled>`.
|
|
236
242
|
*
|
|
237
243
|
* Interaction state is reflected as `data-pressed`, `data-hovered`, and `data-focus-visible`
|
|
238
244
|
* (present/absent boolean attributes). There is no `data-state` — this primitive has no
|
|
@@ -254,12 +260,25 @@ class ForButton {
|
|
|
254
260
|
#host = inject(ElementRef).nativeElement;
|
|
255
261
|
#isNativeButton = this.#host.tagName === 'BUTTON';
|
|
256
262
|
#initialType = this.#host.getAttribute('type');
|
|
263
|
+
#fieldset = inject(FOR_FIELDSET_CONTEXT, { optional: true });
|
|
257
264
|
/**
|
|
258
265
|
* When `true`, activation (click, Enter, Space) is suppressed and the element
|
|
259
266
|
* reflects `aria-disabled="true"` + `data-disabled=""`. The element remains
|
|
260
|
-
* focusable so assistive technology can announce it.
|
|
267
|
+
* focusable so assistive technology can announce it. Read
|
|
268
|
+
* {@link effectiveDisabled} for the value that actually gates behavior — it
|
|
269
|
+
* also folds in a surrounding disabled `[forFieldset]`.
|
|
261
270
|
*/
|
|
262
271
|
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
272
|
+
/**
|
|
273
|
+
* The button's own {@link disabled} OR'd with a surrounding disabled
|
|
274
|
+
* `[forFieldset]`. This is what gates activation and drives `aria-disabled` /
|
|
275
|
+
* `data-disabled`: a native `<fieldset disabled>` never reaches a non-native
|
|
276
|
+
* host (`<div forButton>`), so the group's disabled state has to compose in
|
|
277
|
+
* here. Mirrors `FormUiControlBase.effectiveDisabled`, so a `[forButton]` and
|
|
278
|
+
* a `[forSwitch]` inside the same disabled group behave identically.
|
|
279
|
+
*/
|
|
280
|
+
effectiveDisabled = computed(() => this.disabled() || (this.#fieldset?.disabled() ?? false), /* @ts-ignore */
|
|
281
|
+
...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
|
|
263
282
|
/**
|
|
264
283
|
* Emitted once per user activation: a pointer click on any host, or Enter / Space
|
|
265
284
|
* on a non-native-button host (native buttons synthesize click from keyboard). The
|
|
@@ -271,8 +290,8 @@ class ForButton {
|
|
|
271
290
|
...(ngDevMode ? [{ debugName: "#focused" }] : /* istanbul ignore next */ []));
|
|
272
291
|
#spaceHeld = false;
|
|
273
292
|
#keyboardModality = injectFocusVisible();
|
|
274
|
-
hovered = injectHovered({ disabled: this.
|
|
275
|
-
pressed = injectPressed({ disabled: this.
|
|
293
|
+
hovered = injectHovered({ disabled: this.effectiveDisabled });
|
|
294
|
+
pressed = injectPressed({ disabled: this.effectiveDisabled });
|
|
276
295
|
focusVisible = computed(() => this.#focused() && this.#keyboardModality(), /* @ts-ignore */
|
|
277
296
|
...(ngDevMode ? [{ debugName: "focusVisible" }] : /* istanbul ignore next */ []));
|
|
278
297
|
resolvedType = computed(() => this.#initialType ?? (this.#isNativeButton ? 'button' : null), /* @ts-ignore */
|
|
@@ -282,7 +301,7 @@ class ForButton {
|
|
|
282
301
|
resolvedTabindex = computed(() => (this.#isNativeButton ? null : '0'), /* @ts-ignore */
|
|
283
302
|
...(ngDevMode ? [{ debugName: "resolvedTabindex" }] : /* istanbul ignore next */ []));
|
|
284
303
|
onClick(event) {
|
|
285
|
-
if (this.
|
|
304
|
+
if (this.effectiveDisabled()) {
|
|
286
305
|
event.preventDefault();
|
|
287
306
|
event.stopImmediatePropagation();
|
|
288
307
|
return;
|
|
@@ -294,7 +313,7 @@ class ForButton {
|
|
|
294
313
|
return;
|
|
295
314
|
}
|
|
296
315
|
if (event.key === 'Enter') {
|
|
297
|
-
if (this.
|
|
316
|
+
if (this.effectiveDisabled()) {
|
|
298
317
|
return;
|
|
299
318
|
}
|
|
300
319
|
event.preventDefault();
|
|
@@ -303,7 +322,7 @@ class ForButton {
|
|
|
303
322
|
}
|
|
304
323
|
if (event.key === ' ') {
|
|
305
324
|
event.preventDefault();
|
|
306
|
-
if (this.
|
|
325
|
+
if (this.effectiveDisabled()) {
|
|
307
326
|
return;
|
|
308
327
|
}
|
|
309
328
|
this.#spaceHeld = true;
|
|
@@ -320,7 +339,7 @@ class ForButton {
|
|
|
320
339
|
return;
|
|
321
340
|
}
|
|
322
341
|
this.#spaceHeld = false;
|
|
323
|
-
if (this.
|
|
342
|
+
if (this.effectiveDisabled()) {
|
|
324
343
|
return;
|
|
325
344
|
}
|
|
326
345
|
event.preventDefault();
|
|
@@ -334,7 +353,7 @@ class ForButton {
|
|
|
334
353
|
this.#spaceHeld = false;
|
|
335
354
|
}
|
|
336
355
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForButton, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
337
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForButton, isStandalone: true, selector: "[forButton]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activate: "activate" }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)", "keyup": "onKeyup($event)", "focusin": "onFocusIn()", "focusout": "onFocusOut()" }, properties: { "attr.type": "resolvedType()", "attr.role": "resolvedRole()", "attr.tabindex": "resolvedTabindex()", "attr.aria-disabled": "
|
|
356
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForButton, isStandalone: true, selector: "[forButton]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activate: "activate" }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)", "keyup": "onKeyup($event)", "focusin": "onFocusIn()", "focusout": "onFocusOut()" }, properties: { "attr.type": "resolvedType()", "attr.role": "resolvedRole()", "attr.tabindex": "resolvedTabindex()", "attr.aria-disabled": "effectiveDisabled() ? 'true' : null", "attr.data-disabled": "effectiveDisabled() ? '' : null", "attr.data-pressed": "pressed() ? '' : null", "attr.data-hovered": "hovered() ? '' : null", "attr.data-focus-visible": "focusVisible() ? '' : null" } }, exportAs: ["forButton"], ngImport: i0 });
|
|
338
357
|
}
|
|
339
358
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForButton, decorators: [{
|
|
340
359
|
type: Directive,
|
|
@@ -345,8 +364,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
345
364
|
'[attr.type]': 'resolvedType()',
|
|
346
365
|
'[attr.role]': 'resolvedRole()',
|
|
347
366
|
'[attr.tabindex]': 'resolvedTabindex()',
|
|
348
|
-
'[attr.aria-disabled]': "
|
|
349
|
-
'[attr.data-disabled]': "
|
|
367
|
+
'[attr.aria-disabled]': "effectiveDisabled() ? 'true' : null",
|
|
368
|
+
'[attr.data-disabled]': "effectiveDisabled() ? '' : null",
|
|
350
369
|
'[attr.data-pressed]': "pressed() ? '' : null",
|
|
351
370
|
'[attr.data-hovered]': "hovered() ? '' : null",
|
|
352
371
|
'[attr.data-focus-visible]': "focusVisible() ? '' : null",
|