forty-cdk 0.11.1 → 0.13.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/breakpoints/README.md +5 -3
- package/combobox/README.md +33 -6
- package/date-field/README.md +9 -8
- package/date-range-field/README.md +9 -8
- package/drawer/README.md +14 -0
- package/fesm2022/forty-cdk-accordion.mjs +4 -2
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -1
- package/fesm2022/forty-cdk-avatar.mjs +14 -4
- package/fesm2022/forty-cdk-avatar.mjs.map +1 -1
- package/fesm2022/forty-cdk-breakpoints.mjs +27 -21
- package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -1
- package/fesm2022/forty-cdk-calendar.mjs +2 -11
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
- package/fesm2022/forty-cdk-carousel.mjs +7 -18
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
- package/fesm2022/forty-cdk-combobox.mjs +152 -56
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +2219 -1373
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-field.mjs +18 -58
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-range-field.mjs +26 -13
- package/fesm2022/forty-cdk-date-range-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-dialog.mjs +98 -233
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
- package/fesm2022/forty-cdk-drag-drop.mjs +2 -3
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
- package/fesm2022/forty-cdk-drawer.mjs +180 -225
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
- package/fesm2022/forty-cdk-hover-card.mjs +116 -105
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs +3 -21
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +48 -165
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-menu.mjs +2 -2
- package/fesm2022/forty-cdk-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-menubar.mjs +2 -3
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs +1 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-pagination.mjs +33 -10
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs +11 -103
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +117 -183
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-stepper.mjs +61 -58
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +228 -55
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-tabs.mjs +26 -44
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-field.mjs +19 -60
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs +13 -15
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-range-field.mjs +39 -14
- package/fesm2022/forty-cdk-time-range-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-toast.mjs +43 -21
- package/fesm2022/forty-cdk-toast.mjs.map +1 -1
- package/fesm2022/forty-cdk-toggle.mjs +2 -3
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
- package/fesm2022/forty-cdk-toolbar.mjs +3 -6
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -1
- package/fesm2022/forty-cdk-tooltip.mjs +13 -96
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
- package/fesm2022/forty-cdk-tree.mjs +2 -3
- package/fesm2022/forty-cdk-tree.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs +62 -49
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/hover-card/README.md +56 -8
- package/package.json +1 -1
- package/select/README.md +9 -7
- package/stepper/README.md +27 -1
- package/table/README.md +39 -31
- package/time-field/README.md +9 -8
- package/time-range-field/README.md +12 -11
- package/toast/README.md +4 -1
- package/tooltip/README.md +15 -15
- package/types/forty-cdk-breakpoints.d.ts +20 -18
- package/types/forty-cdk-combobox.d.ts +63 -16
- package/types/forty-cdk-core.d.ts +1460 -635
- package/types/forty-cdk-date-field.d.ts +16 -24
- package/types/forty-cdk-date-picker.d.ts +1 -1
- package/types/forty-cdk-date-range-field.d.ts +18 -11
- package/types/forty-cdk-dialog.d.ts +33 -124
- package/types/forty-cdk-drawer.d.ts +36 -79
- package/types/forty-cdk-hover-card.d.ts +55 -72
- package/types/forty-cdk-listbox.d.ts +1 -1
- package/types/forty-cdk-menu.d.ts +1 -1
- package/types/forty-cdk-pagination.d.ts +9 -1
- package/types/forty-cdk-popover.d.ts +8 -92
- package/types/forty-cdk-select.d.ts +30 -8
- package/types/forty-cdk-stepper.d.ts +60 -22
- package/types/forty-cdk-table.d.ts +133 -25
- package/types/forty-cdk-tabs.d.ts +8 -5
- package/types/forty-cdk-time-field.d.ts +17 -26
- package/types/forty-cdk-time-picker.d.ts +12 -5
- package/types/forty-cdk-time-range-field.d.ts +31 -12
- package/types/forty-cdk-toast.d.ts +8 -1
- package/types/forty-cdk-toolbar.d.ts +0 -1
- package/types/forty-cdk-tooltip.d.ts +17 -86
- package/types/forty-cdk-virtualization.d.ts +21 -6
package/breakpoints/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Breakpoints
|
|
2
2
|
|
|
3
|
-
A signal-first, zoneless, SSR-safe viewport breakpoint observer (injectBreakpoints). Configure the breakpoint map once via
|
|
3
|
+
A signal-first, zoneless, SSR-safe viewport breakpoint observer (injectBreakpoints). Configure the breakpoint map once via provideForBreakpointsDefaults — or use the Tailwind scale by default — then read up / down / between / only / active or any arbitrary media query, each as a live Signal<boolean>.
|
|
4
4
|
|
|
5
5
|
It is a headless reactive utility, not a UI primitive: no DOM, no ARIA, no template. Configure the breakpoint map **once** via a provider; read it anywhere with `injectBreakpoints()` — no need to repeat the breakpoint set at every call site.
|
|
6
6
|
|
|
@@ -9,10 +9,12 @@ It is a headless reactive utility, not a UI primitive: no DOM, no ARIA, no templ
|
|
|
9
9
|
Configuring is optional — without a provider the Tailwind scale (`sm` 640, `md` 768, `lg` 1024, `xl` 1280, `2xl` 1536) is used. To define your own:
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
|
-
import {
|
|
12
|
+
import { provideForBreakpointsDefaults } from 'forty-cdk/breakpoints';
|
|
13
13
|
|
|
14
14
|
export const appConfig: ApplicationConfig = {
|
|
15
|
-
providers: [
|
|
15
|
+
providers: [
|
|
16
|
+
provideForBreakpointsDefaults({ mobile: 0, tablet: 640, laptop: 1024, desktop: 1280 }),
|
|
17
|
+
],
|
|
16
18
|
};
|
|
17
19
|
```
|
|
18
20
|
|
package/combobox/README.md
CHANGED
|
@@ -36,6 +36,21 @@ The editable (default) anatomy — an `<input>` that filters a portaled listbox
|
|
|
36
36
|
</div>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
**Editable + list (no trigger).** Wrapping the options in a `[forComboboxList]` without adding a `[forComboboxTrigger]` is a supported shape, and the a11y-clean way to add non-option pieces (`[forComboboxEmpty]`, `[forComboboxStatus]`, `[forComboboxAction]`) to the editable anatomy. Because content carries `role="listbox"` (which may only own `option` / `group` children), moving the options into `[forComboboxList]` makes those pieces siblings of the listbox instead of invalid listbox children — content becomes role-less and the list owns the listbox role. The role split keys off `hasList`, the focus model off `trigger()`, so focus still stays on the input the whole time:
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<div forComboboxContent>
|
|
43
|
+
<div forComboboxList>
|
|
44
|
+
<div forComboboxOption [value]="item.id" [label]="item.label">{{ item.label }}</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div forComboboxEmpty>No matches.</div>
|
|
47
|
+
</div>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A `[forComboboxAction]` **requires** this shape — see [Action items](#action-items).
|
|
51
|
+
|
|
52
|
+
> **Editable-anatomy caveat.** For the common case of options plus only a `[forComboboxEmpty]` / `[forComboboxStatus]` message (the bare anatomy above, no `[forComboboxList]`), the message sits directly inside `[forComboboxContent]`. This is still supported and does not throw, but it leaves the `role="status"` message as an owned child of `role="listbox"`, a minor `aria-required-owned` compromise; wrap the options in a `[forComboboxList]` (the "editable + list" shape) when you want the strictly-clean tree.
|
|
53
|
+
|
|
39
54
|
The picker anatomy adds a `[forComboboxTrigger]` `<button>` showing the committed selection, with the search input and a `[forComboboxList]` (`role="listbox"`) nested inside the popup — see [Picker anatomy](#picker-anatomy). Multi mode wraps the chips + input in `[forComboboxChips]` — see [Multi mode](#multi-mode). Optional `[forComboboxAnchor]`, `[forComboboxStatus]`, `[forComboboxGroup]` / `[forComboboxGroupLabel]`, and `[forComboboxSeparator]` pieces are covered in their own sections below.
|
|
40
55
|
|
|
41
56
|
## Examples
|
|
@@ -204,15 +219,24 @@ A combobox popup often needs an entry that is an **action**, not a value —
|
|
|
204
219
|
`role="button"` actions, not `role="option"` selections, so `[forComboboxAction]`
|
|
205
220
|
renders one that stays out of the option/value collection entirely.
|
|
206
221
|
|
|
222
|
+
`[forComboboxAction]` **requires a `[forComboboxList]`**: content carries
|
|
223
|
+
`role="listbox"` in the editable anatomy, so a `role="button"` placed directly
|
|
224
|
+
inside it would be an invalid listbox child (`aria-required-owned`). Wrap the
|
|
225
|
+
options in a `[forComboboxList]` so the action becomes a sibling of the listbox.
|
|
226
|
+
An action rendered without a `[forComboboxList]` throws `[forty-cdk/combobox]` at
|
|
227
|
+
runtime. This is the "editable + list" shape (no `[forComboboxTrigger]` needed).
|
|
228
|
+
|
|
207
229
|
```html
|
|
208
230
|
<div forCombobox #combobox="forCombobox" [(query)]="query" [(value)]="value">
|
|
209
231
|
<input forComboboxInput placeholder="Search…" />
|
|
210
232
|
@if (combobox.open()) {
|
|
211
233
|
<div forComboboxContent>
|
|
212
234
|
<button forComboboxAction (action)="createNew(query())">Create "{{ query() }}"</button>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
235
|
+
<div forComboboxList>
|
|
236
|
+
@for (it of filtered; track it.id) {
|
|
237
|
+
<div forComboboxOption [value]="it.id" [label]="it.label">{{ it.label }}</div>
|
|
238
|
+
}
|
|
239
|
+
</div>
|
|
216
240
|
</div>
|
|
217
241
|
}
|
|
218
242
|
</div>
|
|
@@ -249,9 +273,9 @@ returns focus to the input (editable anatomy) or the `[forComboboxTrigger]`
|
|
|
249
273
|
only. With no action registered, Tab keeps its default "close and let Tab flow on"
|
|
250
274
|
behaviour, so existing comboboxes are unchanged.
|
|
251
275
|
|
|
252
|
-
Actions live inside `[forComboboxContent]`
|
|
253
|
-
|
|
254
|
-
dismissal checks, exactly like the input.
|
|
276
|
+
Actions live inside `[forComboboxContent]` and beside `[forComboboxList]` (never
|
|
277
|
+
inside it, in either anatomy), so they are naturally "inside" the outside-pointer /
|
|
278
|
+
outside-focus dismissal checks, exactly like the input.
|
|
255
279
|
|
|
256
280
|
### API
|
|
257
281
|
|
|
@@ -382,6 +406,8 @@ The `autocompleteMode` input mirrors the WAI-ARIA `aria-autocomplete` property:
|
|
|
382
406
|
|
|
383
407
|
Inline completion preserves the user's typed prefix as unselected and selects the appended remainder, so the next keystroke replaces the selection (matching native browser autofill behavior). Backspace deletes the selection without re-completing, so the user can always shorten the query.
|
|
384
408
|
|
|
409
|
+
> **Pure `'inline'` needs a warm cache.** `'inline'` never opens the popup (per APG — `aria-autocomplete="inline"` has no listbox), so in the default `@if (open())` anatomy no `[forComboboxOption]` ever renders and the label cache starts cold. A first keystroke into a combobox that has never been opened completes against nothing; inline completion only works once the options have rendered at least once (the user opened the popup via ArrowDown or `[openOnFocus]`, warming the cache). If completion must work from the very first keystroke, use `'both'` (which opens the popup) or keep the options mounted rather than gating them behind `@if (open())`.
|
|
410
|
+
|
|
385
411
|
## Dismiss events
|
|
386
412
|
|
|
387
413
|
Each dismiss reason emits a vetoable event from `[forCombobox]` — call `preventDefault()` on the event to keep the listbox open.
|
|
@@ -585,6 +611,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
585
611
|
- `[forComboboxSeparator]` is decorative and never registers with the listbox's option collection — keyboard navigation skips it automatically.
|
|
586
612
|
- `[forComboboxGroup]` is purely advisory grouping — options inside still register flatly with the root, so navigation flows through groups without interruption.
|
|
587
613
|
- `[forComboboxEmpty]` carries `role="status"` + `aria-live="polite"` so the empty-state message is announced when filtering removes all matches.
|
|
614
|
+
- 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).
|
|
588
615
|
- The input element is exempt from the listbox's outside-pointer dismissal layer, so a click on the input while the listbox is open routes through `(click)` (toggle / focus open) instead of double-firing as an outside dismissal.
|
|
589
616
|
|
|
590
617
|
## Styling
|
package/date-field/README.md
CHANGED
|
@@ -146,7 +146,7 @@ Plus the shared `FormUiControl` members from `@angular/forms/signals`: `disabled
|
|
|
146
146
|
| `[forDateFieldSegment]` | `data-disabled` | present \| absent |
|
|
147
147
|
| `[forDateFieldSegment]` | `data-readonly` | present \| absent |
|
|
148
148
|
|
|
149
|
-
`data-empty` marks the
|
|
149
|
+
`data-empty` marks the field only while **every** editable segment is empty (nothing has been entered); a partially-typed field is **not** empty. `data-placeholder` marks each individual segment that is still empty. `data-highlighted` is the current roving-tabindex segment — the only focus hook the consumer gets, shared with the other roving primitives. `[forDateFieldLiteral]` carries no data-\* attributes (it is `aria-hidden` and out of the tab order).
|
|
150
150
|
|
|
151
151
|
## Date-time field
|
|
152
152
|
|
|
@@ -192,13 +192,14 @@ providers: [
|
|
|
192
192
|
|
|
193
193
|
Key behavior applies per segment. Horizontal arrows mirror under `dir="rtl"`.
|
|
194
194
|
|
|
195
|
-
| Key | Behavior
|
|
196
|
-
| -------------------------- |
|
|
197
|
-
| **0–9** | Type the value; auto-advances to the next segment when full.
|
|
198
|
-
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today.
|
|
199
|
-
| **ArrowLeft / ArrowRight** | Move to the previous / next segment (no wrap).
|
|
200
|
-
| **Home / End** | Jump to the segment minimum / maximum.
|
|
201
|
-
| **Backspace
|
|
195
|
+
| Key | Behavior |
|
|
196
|
+
| -------------------------- | --------------------------------------------------------------------------------------- |
|
|
197
|
+
| **0–9** | Type the value; auto-advances to the next segment when full. |
|
|
198
|
+
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today. |
|
|
199
|
+
| **ArrowLeft / ArrowRight** | Move to the previous / next segment (no wrap). |
|
|
200
|
+
| **Home / End** | Jump to the segment minimum / maximum. |
|
|
201
|
+
| **Backspace** | Delete the last entered digit; the value becomes `null` when the last digit is removed. |
|
|
202
|
+
| **Delete** | Clear the whole segment (the value becomes `null` until refilled). |
|
|
202
203
|
|
|
203
204
|
The day clamps to the current month's length (e.g. 31 → 28 in February), and a composed value is clamped into `[minDate, maxDate]`.
|
|
204
205
|
|
|
@@ -189,7 +189,7 @@ Plus the shared `FormUiControl` members from `@angular/forms/signals`: `disabled
|
|
|
189
189
|
| `[forDateRangeFieldSegment]` | `data-disabled` | present \| absent |
|
|
190
190
|
| `[forDateRangeFieldSegment]` | `data-readonly` | present \| absent |
|
|
191
191
|
|
|
192
|
-
`data-empty` marks the
|
|
192
|
+
`data-empty` marks the field only while **both** endpoints are entirely empty; a partially-filled or complete-but-disordered range is **not** empty (`data-range-error` covers the disorder). `data-range-error` marks the specific case of two complete but out-of-order endpoints; `data-placeholder` marks each individual segment that is still empty. `data-highlighted` is the current roving-tabindex segment — the only focus hook the consumer gets, shared with the other roving primitives.
|
|
193
193
|
|
|
194
194
|
## Ordering
|
|
195
195
|
|
|
@@ -222,13 +222,14 @@ providers: [
|
|
|
222
222
|
|
|
223
223
|
Key behavior applies per segment. Each endpoint is its own tab stop, so `Tab` moves from the start group to the end group to the next control; arrows move between segments **within** an endpoint. Horizontal arrows mirror under `dir="rtl"`.
|
|
224
224
|
|
|
225
|
-
| Key | Behavior
|
|
226
|
-
| -------------------------- |
|
|
227
|
-
| **0–9** | Type the value; auto-advances to the next segment when full.
|
|
228
|
-
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today.
|
|
229
|
-
| **ArrowLeft / ArrowRight** | Move to the previous / next segment in the same endpoint (no wrap).
|
|
230
|
-
| **Home / End** | Jump to the segment minimum / maximum.
|
|
231
|
-
| **Backspace
|
|
225
|
+
| Key | Behavior |
|
|
226
|
+
| -------------------------- | --------------------------------------------------------------------------------------- |
|
|
227
|
+
| **0–9** | Type the value; auto-advances to the next segment when full. |
|
|
228
|
+
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today. |
|
|
229
|
+
| **ArrowLeft / ArrowRight** | Move to the previous / next segment in the same endpoint (no wrap). |
|
|
230
|
+
| **Home / End** | Jump to the segment minimum / maximum. |
|
|
231
|
+
| **Backspace** | Delete the last entered digit; the range becomes `null` when the last digit is removed. |
|
|
232
|
+
| **Delete** | Clear the whole segment (the range becomes `null` until refilled). |
|
|
232
233
|
|
|
233
234
|
The day clamps to the current month's length (e.g. 31 → 28 in February), and each composed endpoint is clamped into `[minDate, maxDate]`.
|
|
234
235
|
|
package/drawer/README.md
CHANGED
|
@@ -205,6 +205,20 @@ this.#drawers.open(ConfirmDrawer, {
|
|
|
205
205
|
|
|
206
206
|
`activeSnapPointChange` fires with the landed snap on the mount-time default and every drag release — the read-back the declarative API exposes through `[(activeSnapPoint)]`. All three subscriptions are released automatically when the drawer closes.
|
|
207
207
|
|
|
208
|
+
**Driving the active snap point.** `ForDrawerRef.setActiveSnapPoint(snap)` moves a snap-point drawer to a new snap after open — the programmatic equivalent of _writing_ `[(activeSnapPoint)]` on the declarative `[forDrawer]`. `ref.activeSnapPoint()` is the matching reactive read (it also reflects the drawer's own internal transitions — the mount-time default and every drag release):
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const ref = this.#drawers.open(ConfirmDrawer, {
|
|
212
|
+
data,
|
|
213
|
+
snapPoints: ['148px', '50%', 1],
|
|
214
|
+
defaultSnapPoint: '148px',
|
|
215
|
+
});
|
|
216
|
+
ref.setActiveSnapPoint('50%'); // slide to the mid snap
|
|
217
|
+
ref.activeSnapPoint(); // => '50%'
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Like the declarative model it does not validate the argument against `snapPoints` (the drag engine tolerates a non-member), it is a no-op once the drawer has closed, and it is meaningful only when `snapPoints` are configured. The surface movement is the consumer's CSS keyed off the reflected `data-active-snap-point` attribute (see [Positioning the snaps](#positioning-the-snaps-css-contract)); `setActiveSnapPoint` only sets the state. As with `[(activeSnapPoint)]`, driving the snap this way does **not** re-fire the `activeSnapPointChange` callback (that fires only on the drawer's own internal transitions).
|
|
221
|
+
|
|
208
222
|
### Per-channel dismissal (Escape-only drawers)
|
|
209
223
|
|
|
210
224
|
`dismissible` is **not** all-or-nothing. The four dismiss channels — Escape, pointer-down-outside, focus-outside, and the composite outside-interaction — are independently vetoable on both APIs, so you can keep some live and suppress others (e.g. a non-modal floater that closes on Escape but stays put on an outside click). Programmatically the channels are callbacks on the open config, mirroring the `autoFocusOn*` shape:
|
|
@@ -285,7 +285,7 @@ class ForAccordionTrigger {
|
|
|
285
285
|
this.parent.focusByOffset(this.#host.nativeElement, action);
|
|
286
286
|
}
|
|
287
287
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAccordionTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
288
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForAccordionTrigger, isStandalone: true, selector: "[forAccordionTrigger]", host: { attributes: { "type": "button" }, listeners: { "click": "item.toggle()", "keydown": "onKeyDown($event)" }, properties: { "id": "item.triggerId()", "attr.aria-expanded": "item.expanded() ? \"true\" : \"false\"", "attr.aria-controls": "item.expanded() ? item.contentId() : null", "attr.aria-disabled": "ariaDisabled() ? \"true\" : null", "attr.data-state": "item.expanded() ? \"open\" : \"closed\"", "attr.data-orientation": "parent.orientation()" } }, exportAs: ["forAccordionTrigger"], ngImport: i0 });
|
|
288
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForAccordionTrigger, isStandalone: true, selector: "[forAccordionTrigger]", host: { attributes: { "type": "button" }, listeners: { "click": "item.toggle()", "keydown": "onKeyDown($event)" }, properties: { "id": "item.triggerId()", "attr.aria-expanded": "item.expanded() ? \"true\" : \"false\"", "attr.aria-controls": "item.expanded() ? item.contentId() : null", "attr.aria-disabled": "ariaDisabled() ? \"true\" : null", "attr.data-state": "item.expanded() ? \"open\" : \"closed\"", "attr.data-disabled": "item.disabled() ? \"\" : null", "attr.data-orientation": "parent.orientation()" } }, exportAs: ["forAccordionTrigger"], ngImport: i0 });
|
|
289
289
|
}
|
|
290
290
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAccordionTrigger, decorators: [{
|
|
291
291
|
type: Directive,
|
|
@@ -299,6 +299,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
299
299
|
'[attr.aria-controls]': 'item.expanded() ? item.contentId() : null',
|
|
300
300
|
'[attr.aria-disabled]': 'ariaDisabled() ? "true" : null',
|
|
301
301
|
'[attr.data-state]': 'item.expanded() ? "open" : "closed"',
|
|
302
|
+
'[attr.data-disabled]': 'item.disabled() ? "" : null',
|
|
302
303
|
'[attr.data-orientation]': 'parent.orientation()',
|
|
303
304
|
'(click)': 'item.toggle()',
|
|
304
305
|
'(keydown)': 'onKeyDown($event)',
|
|
@@ -327,7 +328,7 @@ class ForAccordionContent {
|
|
|
327
328
|
this.item.adoptContentId(inject(ElementRef).nativeElement);
|
|
328
329
|
}
|
|
329
330
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAccordionContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
330
|
-
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": "item.triggerId()", "attr.aria-hidden": "item.expanded() ? null : \"true\"", "attr.inert": "item.expanded() ? null : \"\"", "attr.data-state": "item.expanded() ? \"open\" : \"closed\"", "attr.data-orientation": "parent.orientation()" } }, exportAs: ["forAccordionContent"], ngImport: i0 });
|
|
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": "item.triggerId()", "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 });
|
|
331
332
|
}
|
|
332
333
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAccordionContent, decorators: [{
|
|
333
334
|
type: Directive,
|
|
@@ -341,6 +342,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
341
342
|
'[attr.aria-hidden]': 'item.expanded() ? null : "true"',
|
|
342
343
|
'[attr.inert]': 'item.expanded() ? null : ""',
|
|
343
344
|
'[attr.data-state]': 'item.expanded() ? "open" : "closed"',
|
|
345
|
+
'[attr.data-disabled]': 'item.disabled() ? "" : null',
|
|
344
346
|
'[attr.data-orientation]': 'parent.orientation()',
|
|
345
347
|
},
|
|
346
348
|
}]
|
|
@@ -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-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-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 { 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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"}
|
|
@@ -69,7 +69,10 @@ class ForAvatar {
|
|
|
69
69
|
* The default is read from `provideForAvatarDefaults` for the
|
|
70
70
|
* surrounding scope.
|
|
71
71
|
*/
|
|
72
|
-
fallbackDelayMs = input(this.#defaults.fallbackDelayMs, { ...(ngDevMode ? { debugName: "fallbackDelayMs" } : /* istanbul ignore next */ {}), transform:
|
|
72
|
+
fallbackDelayMs = input(this.#defaults.fallbackDelayMs, { ...(ngDevMode ? { debugName: "fallbackDelayMs" } : /* istanbul ignore next */ {}), transform: (value) => {
|
|
73
|
+
const parsed = numberAttribute(value);
|
|
74
|
+
return Number.isFinite(parsed) ? parsed : this.#defaults.fallbackDelayMs;
|
|
75
|
+
} });
|
|
73
76
|
#status = signal('idle', /* @ts-ignore */
|
|
74
77
|
...(ngDevMode ? [{ debugName: "#status" }] : /* istanbul ignore next */ []));
|
|
75
78
|
status = this.#status.asReadonly();
|
|
@@ -202,17 +205,24 @@ class ForAvatarImage {
|
|
|
202
205
|
// (the native event can arrive after `src` was reassigned).
|
|
203
206
|
if (!this.#isCurrentRequest())
|
|
204
207
|
return;
|
|
208
|
+
if (!this.#host.complete)
|
|
209
|
+
return;
|
|
205
210
|
this.#emit('loaded');
|
|
206
211
|
}
|
|
207
212
|
onError() {
|
|
208
213
|
if (!this.#isCurrentRequest())
|
|
209
214
|
return;
|
|
215
|
+
if (!this.#host.complete)
|
|
216
|
+
return;
|
|
210
217
|
this.#emit('error');
|
|
211
218
|
}
|
|
212
219
|
/**
|
|
213
|
-
*
|
|
214
|
-
* `#syncFromAttr` observed, so a native
|
|
215
|
-
*
|
|
220
|
+
* Cheap string-identity fast-path: true when the `src` the host currently
|
|
221
|
+
* carries is the one the latest `#syncFromAttr` observed, so a native
|
|
222
|
+
* load/error event has not drifted away from the in-flight request. This
|
|
223
|
+
* proves src-attribute stability only; request *completeness* (a late event
|
|
224
|
+
* from a superseded resource that happens to share the current src) is
|
|
225
|
+
* verified separately in onLoad/onError via `this.#host.complete`.
|
|
216
226
|
*/
|
|
217
227
|
#isCurrentRequest() {
|
|
218
228
|
return this.#host.getAttribute('src') === this.#requestSrc;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forty-cdk-avatar.mjs","sources":["../../../projects/forty-cdk/avatar/src/avatar-context.ts","../../../projects/forty-cdk/avatar/src/avatar-defaults.ts","../../../projects/forty-cdk/avatar/src/avatar.ts","../../../projects/forty-cdk/avatar/src/avatar-image.ts","../../../projects/forty-cdk/avatar/src/avatar-fallback.ts","../../../projects/forty-cdk/avatar/src/forty-cdk-avatar.ts"],"sourcesContent":["import { inject, InjectionToken, type Signal } from '@angular/core';\n\n/**\n * Lifecycle stages of the avatar's image:\n * - `idle`: no `src` set yet.\n * - `loading`: `src` set, waiting for the network.\n * - `loaded`: image rendered successfully.\n * - `error`: image failed to load.\n */\nexport type ForAvatarStatus = 'idle' | 'loading' | 'loaded' | 'error';\n\nexport interface ForAvatarContext {\n /** Current status of the avatar's image. */\n readonly status: Signal<ForAvatarStatus>;\n /**\n * `true` when the consumer should render the fallback. Drives `@if` in\n * the consumer's template. Honors `fallbackDelayMs` for `idle` / `loading`\n * states; `error` flips it to `true` immediately.\n */\n readonly shouldShowFallback: Signal<boolean>;\n /**\n * Internal — reported by `[forAvatarImage]` so the root can transition\n * its state machine. Consumers should not call this.\n */\n reportStatus(status: ForAvatarStatus): void;\n}\n\nexport const FOR_AVATAR_CONTEXT = new InjectionToken<ForAvatarContext>('FOR_AVATAR_CONTEXT');\n\n/**\n * Injects the nearest {@link ForAvatarContext}, throwing a descriptive error\n * when used outside a `[forAvatar]` element.\n *\n * @param piece Name of the calling directive, used in the error message.\n */\nexport function injectAvatarContext(piece: string): ForAvatarContext {\n const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(`[forty-cdk/avatar] ${piece} must be used inside a [forAvatar] element.`);\n }\n return ctx;\n}\n","import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant avatars in the surrounding injector\n * scope. Configure with `provideForAvatarDefaults` either at the\n * application root or in any component's `providers` array; partial\n * overrides merge with the parent scope.\n */\nexport interface ForAvatarDefaults {\n /**\n * Milliseconds to defer the fallback for fast loads, avoiding a brief\n * \"initials → image\" flicker. Set to `0` to show immediately during\n * `idle` / `loading`, or to e.g. `500` to skip rendering during quick\n * cached loads. `error` always shows the fallback immediately.\n */\n fallbackDelayMs: number;\n}\n\n/**\n * Library fallback for avatar defaults, read at the root injector when no\n * consumer has called `provideForAvatarDefaults`. Exported for the shared defaults\n * contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_AVATAR_FALLBACK_DEFAULTS: ForAvatarDefaults = {\n fallbackDelayMs: 0,\n};\n\nconst { token, provideDefaults } = createDefaults<ForAvatarDefaults>(\n 'FOR_AVATAR_DEFAULTS',\n FOR_AVATAR_FALLBACK_DEFAULTS,\n);\n\n/** Token holding the resolved avatar defaults for the current scope. */\nexport const FOR_AVATAR_DEFAULTS = token;\n\n/**\n * Configures forty-cdk avatar 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 provideForAvatarDefaults(defaults: Partial<ForAvatarDefaults> = {}): Provider[] {\n return provideDefaults(defaults);\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n input,\n linkedSignal,\n numberAttribute,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\n\nimport { FOR_AVATAR_CONTEXT, type ForAvatarContext, type ForAvatarStatus } from './avatar-context';\nimport { FOR_AVATAR_DEFAULTS } from './avatar-defaults';\n\n/**\n * Headless avatar root. Tracks the load lifecycle of an inner\n * `[forAvatarImage]` and exposes `status` + `shouldShowFallback` so the\n * consumer can swap the image for fallback content (initials, generic icon)\n * via `@if`.\n *\n * No ARIA role is imposed — avatars are typically presentational. If the\n * surrounding component conveys identity (a person's name in the header,\n * a chat bubble), the consumer should provide an `aria-label` on the\n * relevant ancestor or pair the avatar with visible text.\n *\n * @example\n * ```html\n * <span forAvatar #a=\"forAvatar\" fallbackDelayMs=\"500\">\n * <img forAvatarImage [src]=\"user.avatarUrl\" [alt]=\"user.name\" />\n * @if (a.shouldShowFallback()) {\n * <span forAvatarFallback>{{ user.initials }}</span>\n * }\n * </span>\n * ```\n */\n@Directive({\n selector: '[forAvatar]',\n exportAs: 'forAvatar',\n host: {\n '[attr.data-status]': 'status()',\n },\n providers: [{ provide: FOR_AVATAR_CONTEXT, useExisting: ForAvatar }],\n})\nexport class ForAvatar implements ForAvatarContext {\n readonly #defaults = inject(FOR_AVATAR_DEFAULTS);\n\n /**\n * Milliseconds to defer the fallback for fast loads, avoiding a brief\n * \"initials → image\" flicker. Set to `0` (default) to show immediately\n * during `idle` / `loading`, or to e.g. `500` to skip rendering during\n * quick cached loads. `error` always shows the fallback immediately.\n * The default is read from `provideForAvatarDefaults` for the\n * surrounding scope.\n */\n readonly fallbackDelayMs = input(this.#defaults.fallbackDelayMs, {\n transform: numberAttribute,\n });\n\n readonly #status = signal<ForAvatarStatus>('idle');\n readonly status = this.#status.asReadonly();\n\n // Whether the deferred-fallback timer has fired for the current status/delay.\n // `linkedSignal` resets it to `false` whenever the source changes (the\n // canonical replacement for resetting state inside an effect); the timer\n // callback in the constructor flips it to `true`.\n readonly #timerElapsed = linkedSignal<{ status: ForAvatarStatus; delay: number }, boolean>({\n source: () => ({ status: this.#status(), delay: this.fallbackDelayMs() }),\n computation: () => false,\n });\n #timer: ReturnType<typeof setTimeout> | null = null;\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n readonly shouldShowFallback = computed(() => {\n const status = this.#status();\n if (status === 'loaded') return false;\n if (status === 'error') return true;\n // idle | loading: an immediate (delay <= 0) gate shows the fallback right\n // away; a deferred gate waits for the timer armed in the effect below.\n if (this.fallbackDelayMs() <= 0) return true;\n return this.#timerElapsed();\n });\n\n /** @internal */\n reportStatus(status: ForAvatarStatus): void {\n this.#status.set(status);\n }\n\n constructor() {\n // Imperative timer integration: the effect's only job is to (re)arm a\n // setTimeout for the deferred-fallback case (idle | loading with a positive\n // delay). The synchronous branches (loaded / error / delay <= 0) are pure\n // derivations and live in `shouldShowFallback`. The signal write happens\n // inside the timer callback, which runs outside the effect's reactive\n // scope, so this does NOT create a self-cycle in the reactive graph.\n effect(() => {\n const status = this.#status();\n const delay = this.fallbackDelayMs();\n this.#cancelTimer();\n if (this.#isBrowser && (status === 'idle' || status === 'loading') && delay > 0) {\n this.#timer = setTimeout(() => {\n this.#timer = null;\n this.#timerElapsed.set(true);\n }, delay);\n }\n });\n\n inject(DestroyRef).onDestroy(() => this.#cancelTimer());\n }\n\n #cancelTimer(): void {\n if (this.#timer !== null) {\n clearTimeout(this.#timer);\n this.#timer = null;\n }\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n output,\n PLATFORM_ID,\n} from '@angular/core';\n\nimport { FOR_AVATAR_CONTEXT, type ForAvatarStatus } from './avatar-context';\n\n/**\n * Wraps an `<img>` and reports its load lifecycle to the surrounding\n * `[forAvatar]`. The native `src` / `alt` bindings are owned by the\n * consumer — this directive only observes.\n *\n * Reflects `data-status` on the image element so the consumer can hide it\n * via CSS while loading or after an error (e.g. `img[data-status=\"error\"]\n * { display: none }`).\n */\n@Directive({\n selector: 'img[forAvatarImage]',\n exportAs: 'forAvatarImage',\n host: {\n '[attr.data-status]': 'status()',\n '(load)': 'onLoad()',\n '(error)': 'onError()',\n },\n})\nexport class ForAvatarImage {\n readonly #host = inject<ElementRef<HTMLImageElement>>(ElementRef).nativeElement;\n readonly #parent = inject(FOR_AVATAR_CONTEXT, { optional: true });\n\n /**\n * Emits whenever the image lifecycle transitions to a new state. Useful\n * if the consumer wants to log analytics, swap to a different src on\n * error, etc.\n */\n readonly loadStatusChange = output<ForAvatarStatus>();\n\n constructor() {\n if (!this.#parent) {\n throw new Error(\n '[forty-cdk/avatar] ForAvatarImage must be used inside a [forAvatar] element.',\n );\n }\n const parent = this.#parent;\n const host = this.#host;\n\n let destroyed = false;\n inject(DestroyRef).onDestroy(() => (destroyed = true));\n\n afterNextRender(() => {\n this.#syncFromAttr();\n });\n\n if (isPlatformBrowser(inject(PLATFORM_ID)) && typeof MutationObserver !== 'undefined') {\n const observer = new MutationObserver(() => {\n this.#syncFromAttr();\n });\n observer.observe(host, { attributes: true, attributeFilter: ['src'] });\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n }\n\n // Re-emit upstream whenever the parent's status changes due to our writes.\n // De-dupe on the (request, status) pair rather than status alone: a new\n // `src` opens a fresh request token, so an identical status (e.g. a second\n // image that also `loaded`) still re-reports for the new request, while a\n // repeated status within the same request is suppressed.\n let last: ForAvatarStatus | null = null;\n let lastReportedToken = -1;\n const emit = (status: ForAvatarStatus): void => {\n if (destroyed) return;\n if (last === status && lastReportedToken === this.#requestToken) return;\n last = status;\n lastReportedToken = this.#requestToken;\n parent.reportStatus(status);\n this.loadStatusChange.emit(status);\n };\n this.#emit = emit;\n }\n\n protected status(): ForAvatarStatus {\n return this.#parent!.status();\n }\n\n protected onLoad(): void {\n // Ignore a `load` that fires for a request the host has already moved past\n // (the native event can arrive after `src` was reassigned).\n if (!this.#isCurrentRequest()) return;\n this.#emit('loaded');\n }\n\n protected onError(): void {\n if (!this.#isCurrentRequest()) return;\n this.#emit('error');\n }\n\n /**\n * True when the `src` the host currently carries is the one the latest\n * `#syncFromAttr` observed, so a native load/error event belongs to the\n * in-flight request rather than a superseded one.\n */\n #isCurrentRequest(): boolean {\n return this.#host.getAttribute('src') === this.#requestSrc;\n }\n\n #syncFromAttr(): void {\n const src = this.#host.getAttribute('src');\n this.#requestToken++;\n this.#requestSrc = src;\n if (!src) {\n this.#emit('idle');\n return;\n }\n if (this.#host.complete) {\n // Cached image: load/error events may not fire — derive from naturalWidth.\n if (this.#host.naturalWidth > 0) {\n this.#emit('loaded');\n return;\n }\n // A complete image with zero intrinsic width is ambiguous: it may be a\n // broken image OR a valid SVG with no intrinsic dimensions. Don't\n // pessimistically flag `error` and hide a valid image — verify with\n // `decode()`, which resolves for a decodable (even zero-size) image and\n // rejects for a broken one. Stay `loading` until it settles.\n this.#emit('loading');\n this.#verifyCachedZeroSize(src);\n return;\n }\n this.#emit('loading');\n }\n\n #verifyCachedZeroSize(src: string): void {\n const decode = this.#host.decode?.bind(this.#host);\n if (!decode) {\n // No `decode()` (older engines / jsdom): keep the legacy heuristic.\n this.#emit('error');\n return;\n }\n decode().then(\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('loaded');\n }\n },\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('error');\n }\n },\n );\n }\n\n // Initialized in constructor.\n #emit!: (status: ForAvatarStatus) => void;\n\n /** Monotonic id bumped on every `src` change so stale events are ignored. */\n #requestToken = 0;\n /** The `src` the latest `#syncFromAttr` observed, used to match native events. */\n #requestSrc: string | null = null;\n}\n","import { Directive } from '@angular/core';\n\nimport { injectAvatarContext } from './avatar-context';\n\n/**\n * Marker for the avatar's fallback content (initials, generic icon, …).\n * Reflects `data-status` from the parent `[forAvatar]` so the consumer can\n * style for the `error` state separately from `idle` / `loading` if needed.\n *\n * The decision of *whether* to render the fallback lives on the root —\n * drive `@if` with `forAvatar.shouldShowFallback()`.\n */\n@Directive({\n selector: '[forAvatarFallback]',\n exportAs: 'forAvatarFallback',\n host: {\n '[attr.data-status]': 'parent.status()',\n },\n})\nexport class ForAvatarFallback {\n protected readonly parent = injectAvatarContext('ForAvatarFallback');\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-avatar.mjs","sources":["../../../projects/forty-cdk/avatar/src/avatar-context.ts","../../../projects/forty-cdk/avatar/src/avatar-defaults.ts","../../../projects/forty-cdk/avatar/src/avatar.ts","../../../projects/forty-cdk/avatar/src/avatar-image.ts","../../../projects/forty-cdk/avatar/src/avatar-fallback.ts","../../../projects/forty-cdk/avatar/src/forty-cdk-avatar.ts"],"sourcesContent":["import { inject, InjectionToken, type Signal } from '@angular/core';\n\n/**\n * Lifecycle stages of the avatar's image:\n * - `idle`: no `src` set yet.\n * - `loading`: `src` set, waiting for the network.\n * - `loaded`: image rendered successfully.\n * - `error`: image failed to load.\n */\nexport type ForAvatarStatus = 'idle' | 'loading' | 'loaded' | 'error';\n\nexport interface ForAvatarContext {\n /** Current status of the avatar's image. */\n readonly status: Signal<ForAvatarStatus>;\n /**\n * `true` when the consumer should render the fallback. Drives `@if` in\n * the consumer's template. Honors `fallbackDelayMs` for `idle` / `loading`\n * states; `error` flips it to `true` immediately.\n */\n readonly shouldShowFallback: Signal<boolean>;\n /**\n * Internal — reported by `[forAvatarImage]` so the root can transition\n * its state machine. Consumers should not call this.\n */\n reportStatus(status: ForAvatarStatus): void;\n}\n\nexport const FOR_AVATAR_CONTEXT = new InjectionToken<ForAvatarContext>('FOR_AVATAR_CONTEXT');\n\n/**\n * Injects the nearest {@link ForAvatarContext}, throwing a descriptive error\n * when used outside a `[forAvatar]` element.\n *\n * @param piece Name of the calling directive, used in the error message.\n */\nexport function injectAvatarContext(piece: string): ForAvatarContext {\n const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(`[forty-cdk/avatar] ${piece} must be used inside a [forAvatar] element.`);\n }\n return ctx;\n}\n","import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant avatars in the surrounding injector\n * scope. Configure with `provideForAvatarDefaults` either at the\n * application root or in any component's `providers` array; partial\n * overrides merge with the parent scope.\n */\nexport interface ForAvatarDefaults {\n /**\n * Milliseconds to defer the fallback for fast loads, avoiding a brief\n * \"initials → image\" flicker. Set to `0` to show immediately during\n * `idle` / `loading`, or to e.g. `500` to skip rendering during quick\n * cached loads. `error` always shows the fallback immediately.\n */\n fallbackDelayMs: number;\n}\n\n/**\n * Library fallback for avatar defaults, read at the root injector when no\n * consumer has called `provideForAvatarDefaults`. Exported for the shared defaults\n * contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_AVATAR_FALLBACK_DEFAULTS: ForAvatarDefaults = {\n fallbackDelayMs: 0,\n};\n\nconst { token, provideDefaults } = createDefaults<ForAvatarDefaults>(\n 'FOR_AVATAR_DEFAULTS',\n FOR_AVATAR_FALLBACK_DEFAULTS,\n);\n\n/** Token holding the resolved avatar defaults for the current scope. */\nexport const FOR_AVATAR_DEFAULTS = token;\n\n/**\n * Configures forty-cdk avatar 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 provideForAvatarDefaults(defaults: Partial<ForAvatarDefaults> = {}): Provider[] {\n return provideDefaults(defaults);\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n input,\n linkedSignal,\n numberAttribute,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\n\nimport { FOR_AVATAR_CONTEXT, type ForAvatarContext, type ForAvatarStatus } from './avatar-context';\nimport { FOR_AVATAR_DEFAULTS } from './avatar-defaults';\n\n/**\n * Headless avatar root. Tracks the load lifecycle of an inner\n * `[forAvatarImage]` and exposes `status` + `shouldShowFallback` so the\n * consumer can swap the image for fallback content (initials, generic icon)\n * via `@if`.\n *\n * No ARIA role is imposed — avatars are typically presentational. If the\n * surrounding component conveys identity (a person's name in the header,\n * a chat bubble), the consumer should provide an `aria-label` on the\n * relevant ancestor or pair the avatar with visible text.\n *\n * @example\n * ```html\n * <span forAvatar #a=\"forAvatar\" fallbackDelayMs=\"500\">\n * <img forAvatarImage [src]=\"user.avatarUrl\" [alt]=\"user.name\" />\n * @if (a.shouldShowFallback()) {\n * <span forAvatarFallback>{{ user.initials }}</span>\n * }\n * </span>\n * ```\n */\n@Directive({\n selector: '[forAvatar]',\n exportAs: 'forAvatar',\n host: {\n '[attr.data-status]': 'status()',\n },\n providers: [{ provide: FOR_AVATAR_CONTEXT, useExisting: ForAvatar }],\n})\nexport class ForAvatar implements ForAvatarContext {\n readonly #defaults = inject(FOR_AVATAR_DEFAULTS);\n\n /**\n * Milliseconds to defer the fallback for fast loads, avoiding a brief\n * \"initials → image\" flicker. Set to `0` (default) to show immediately\n * during `idle` / `loading`, or to e.g. `500` to skip rendering during\n * quick cached loads. `error` always shows the fallback immediately.\n * The default is read from `provideForAvatarDefaults` for the\n * surrounding scope.\n */\n readonly fallbackDelayMs = input(this.#defaults.fallbackDelayMs, {\n transform: (value: unknown) => {\n const parsed = numberAttribute(value);\n return Number.isFinite(parsed) ? parsed : this.#defaults.fallbackDelayMs;\n },\n });\n\n readonly #status = signal<ForAvatarStatus>('idle');\n readonly status = this.#status.asReadonly();\n\n // Whether the deferred-fallback timer has fired for the current status/delay.\n // `linkedSignal` resets it to `false` whenever the source changes (the\n // canonical replacement for resetting state inside an effect); the timer\n // callback in the constructor flips it to `true`.\n readonly #timerElapsed = linkedSignal<{ status: ForAvatarStatus; delay: number }, boolean>({\n source: () => ({ status: this.#status(), delay: this.fallbackDelayMs() }),\n computation: () => false,\n });\n #timer: ReturnType<typeof setTimeout> | null = null;\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n readonly shouldShowFallback = computed(() => {\n const status = this.#status();\n if (status === 'loaded') return false;\n if (status === 'error') return true;\n // idle | loading: an immediate (delay <= 0) gate shows the fallback right\n // away; a deferred gate waits for the timer armed in the effect below.\n if (this.fallbackDelayMs() <= 0) return true;\n return this.#timerElapsed();\n });\n\n /** @internal */\n reportStatus(status: ForAvatarStatus): void {\n this.#status.set(status);\n }\n\n constructor() {\n // Imperative timer integration: the effect's only job is to (re)arm a\n // setTimeout for the deferred-fallback case (idle | loading with a positive\n // delay). The synchronous branches (loaded / error / delay <= 0) are pure\n // derivations and live in `shouldShowFallback`. The signal write happens\n // inside the timer callback, which runs outside the effect's reactive\n // scope, so this does NOT create a self-cycle in the reactive graph.\n effect(() => {\n const status = this.#status();\n const delay = this.fallbackDelayMs();\n this.#cancelTimer();\n if (this.#isBrowser && (status === 'idle' || status === 'loading') && delay > 0) {\n this.#timer = setTimeout(() => {\n this.#timer = null;\n this.#timerElapsed.set(true);\n }, delay);\n }\n });\n\n inject(DestroyRef).onDestroy(() => this.#cancelTimer());\n }\n\n #cancelTimer(): void {\n if (this.#timer !== null) {\n clearTimeout(this.#timer);\n this.#timer = null;\n }\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n output,\n PLATFORM_ID,\n} from '@angular/core';\n\nimport { FOR_AVATAR_CONTEXT, type ForAvatarStatus } from './avatar-context';\n\n/**\n * Wraps an `<img>` and reports its load lifecycle to the surrounding\n * `[forAvatar]`. The native `src` / `alt` bindings are owned by the\n * consumer — this directive only observes.\n *\n * Reflects `data-status` on the image element so the consumer can hide it\n * via CSS while loading or after an error (e.g. `img[data-status=\"error\"]\n * { display: none }`).\n */\n@Directive({\n selector: 'img[forAvatarImage]',\n exportAs: 'forAvatarImage',\n host: {\n '[attr.data-status]': 'status()',\n '(load)': 'onLoad()',\n '(error)': 'onError()',\n },\n})\nexport class ForAvatarImage {\n readonly #host = inject<ElementRef<HTMLImageElement>>(ElementRef).nativeElement;\n readonly #parent = inject(FOR_AVATAR_CONTEXT, { optional: true });\n\n /**\n * Emits whenever the image lifecycle transitions to a new state. Useful\n * if the consumer wants to log analytics, swap to a different src on\n * error, etc.\n */\n readonly loadStatusChange = output<ForAvatarStatus>();\n\n constructor() {\n if (!this.#parent) {\n throw new Error(\n '[forty-cdk/avatar] ForAvatarImage must be used inside a [forAvatar] element.',\n );\n }\n const parent = this.#parent;\n const host = this.#host;\n\n let destroyed = false;\n inject(DestroyRef).onDestroy(() => (destroyed = true));\n\n afterNextRender(() => {\n this.#syncFromAttr();\n });\n\n if (isPlatformBrowser(inject(PLATFORM_ID)) && typeof MutationObserver !== 'undefined') {\n const observer = new MutationObserver(() => {\n this.#syncFromAttr();\n });\n observer.observe(host, { attributes: true, attributeFilter: ['src'] });\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n }\n\n // Re-emit upstream whenever the parent's status changes due to our writes.\n // De-dupe on the (request, status) pair rather than status alone: a new\n // `src` opens a fresh request token, so an identical status (e.g. a second\n // image that also `loaded`) still re-reports for the new request, while a\n // repeated status within the same request is suppressed.\n let last: ForAvatarStatus | null = null;\n let lastReportedToken = -1;\n const emit = (status: ForAvatarStatus): void => {\n if (destroyed) return;\n if (last === status && lastReportedToken === this.#requestToken) return;\n last = status;\n lastReportedToken = this.#requestToken;\n parent.reportStatus(status);\n this.loadStatusChange.emit(status);\n };\n this.#emit = emit;\n }\n\n protected status(): ForAvatarStatus {\n return this.#parent!.status();\n }\n\n protected onLoad(): void {\n // Ignore a `load` that fires for a request the host has already moved past\n // (the native event can arrive after `src` was reassigned).\n if (!this.#isCurrentRequest()) return;\n if (!this.#host.complete) return;\n this.#emit('loaded');\n }\n\n protected onError(): void {\n if (!this.#isCurrentRequest()) return;\n if (!this.#host.complete) return;\n this.#emit('error');\n }\n\n /**\n * Cheap string-identity fast-path: true when the `src` the host currently\n * carries is the one the latest `#syncFromAttr` observed, so a native\n * load/error event has not drifted away from the in-flight request. This\n * proves src-attribute stability only; request *completeness* (a late event\n * from a superseded resource that happens to share the current src) is\n * verified separately in onLoad/onError via `this.#host.complete`.\n */\n #isCurrentRequest(): boolean {\n return this.#host.getAttribute('src') === this.#requestSrc;\n }\n\n #syncFromAttr(): void {\n const src = this.#host.getAttribute('src');\n this.#requestToken++;\n this.#requestSrc = src;\n if (!src) {\n this.#emit('idle');\n return;\n }\n if (this.#host.complete) {\n // Cached image: load/error events may not fire — derive from naturalWidth.\n if (this.#host.naturalWidth > 0) {\n this.#emit('loaded');\n return;\n }\n // A complete image with zero intrinsic width is ambiguous: it may be a\n // broken image OR a valid SVG with no intrinsic dimensions. Don't\n // pessimistically flag `error` and hide a valid image — verify with\n // `decode()`, which resolves for a decodable (even zero-size) image and\n // rejects for a broken one. Stay `loading` until it settles.\n this.#emit('loading');\n this.#verifyCachedZeroSize(src);\n return;\n }\n this.#emit('loading');\n }\n\n #verifyCachedZeroSize(src: string): void {\n const decode = this.#host.decode?.bind(this.#host);\n if (!decode) {\n // No `decode()` (older engines / jsdom): keep the legacy heuristic.\n this.#emit('error');\n return;\n }\n decode().then(\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('loaded');\n }\n },\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('error');\n }\n },\n );\n }\n\n // Initialized in constructor.\n #emit!: (status: ForAvatarStatus) => void;\n\n /** Monotonic id bumped on every `src` change so stale events are ignored. */\n #requestToken = 0;\n /** The `src` the latest `#syncFromAttr` observed, used to match native events. */\n #requestSrc: string | null = null;\n}\n","import { Directive } from '@angular/core';\n\nimport { injectAvatarContext } from './avatar-context';\n\n/**\n * Marker for the avatar's fallback content (initials, generic icon, …).\n * Reflects `data-status` from the parent `[forAvatar]` so the consumer can\n * style for the `error` state separately from `idle` / `loading` if needed.\n *\n * The decision of *whether* to render the fallback lives on the root —\n * drive `@if` with `forAvatar.shouldShowFallback()`.\n */\n@Directive({\n selector: '[forAvatarFallback]',\n exportAs: 'forAvatarFallback',\n host: {\n '[attr.data-status]': 'parent.status()',\n },\n})\nexport class ForAvatarFallback {\n protected readonly parent = injectAvatarContext('ForAvatarFallback');\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|