forty-cdk 0.19.0 → 0.20.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.
Files changed (80) hide show
  1. package/combobox/README.md +1 -1
  2. package/context-menu/README.md +20 -20
  3. package/dropdown-menu/README.md +20 -20
  4. package/fesm2022/forty-cdk-avatar.mjs +4 -1
  5. package/fesm2022/forty-cdk-avatar.mjs.map +1 -1
  6. package/fesm2022/forty-cdk-breakpoints.mjs +7 -2
  7. package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -1
  8. package/fesm2022/forty-cdk-button.mjs +21 -27
  9. package/fesm2022/forty-cdk-button.mjs.map +1 -1
  10. package/fesm2022/forty-cdk-calendar.mjs +14 -10
  11. package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
  12. package/fesm2022/forty-cdk-carousel.mjs +9 -16
  13. package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
  14. package/fesm2022/forty-cdk-combobox.mjs +22 -10
  15. package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
  16. package/fesm2022/forty-cdk-context-menu.mjs +6 -2
  17. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
  18. package/fesm2022/forty-cdk-core.mjs +192 -104
  19. package/fesm2022/forty-cdk-core.mjs.map +1 -1
  20. package/fesm2022/forty-cdk-date-field.mjs +7 -2
  21. package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
  22. package/fesm2022/forty-cdk-date-range-field.mjs +8 -2
  23. package/fesm2022/forty-cdk-date-range-field.mjs.map +1 -1
  24. package/fesm2022/forty-cdk-dialog.mjs +7 -2
  25. package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
  26. package/fesm2022/forty-cdk-drag-drop.mjs +3 -2
  27. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
  28. package/fesm2022/forty-cdk-drawer.mjs +7 -2
  29. package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
  30. package/fesm2022/forty-cdk-dropdown-menu.mjs +6 -2
  31. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
  32. package/fesm2022/forty-cdk-fieldset.mjs +54 -7
  33. package/fesm2022/forty-cdk-fieldset.mjs.map +1 -1
  34. package/fesm2022/forty-cdk-menu.mjs +15 -12
  35. package/fesm2022/forty-cdk-menu.mjs.map +1 -1
  36. package/fesm2022/forty-cdk-menubar.mjs +7 -3
  37. package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
  38. package/fesm2022/forty-cdk-navigation-menu.mjs +107 -36
  39. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
  40. package/fesm2022/forty-cdk-otp-input.mjs +15 -10
  41. package/fesm2022/forty-cdk-otp-input.mjs.map +1 -1
  42. package/fesm2022/forty-cdk-radio-group.mjs +16 -13
  43. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -1
  44. package/fesm2022/forty-cdk-scroll-area.mjs +16 -14
  45. package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -1
  46. package/fesm2022/forty-cdk-select.mjs.map +1 -1
  47. package/fesm2022/forty-cdk-table.mjs +109 -10
  48. package/fesm2022/forty-cdk-table.mjs.map +1 -1
  49. package/fesm2022/forty-cdk-time-field.mjs +7 -2
  50. package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
  51. package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
  52. package/fesm2022/forty-cdk-time-range-field.mjs +8 -2
  53. package/fesm2022/forty-cdk-time-range-field.mjs.map +1 -1
  54. package/fesm2022/forty-cdk-toast.mjs +41 -14
  55. package/fesm2022/forty-cdk-toast.mjs.map +1 -1
  56. package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
  57. package/fesm2022/forty-cdk-tree.mjs +63 -15
  58. package/fesm2022/forty-cdk-tree.mjs.map +1 -1
  59. package/fieldset/README.md +15 -3
  60. package/menu/README.md +13 -1
  61. package/menubar/README.md +7 -7
  62. package/navigation-menu/README.md +1 -0
  63. package/package.json +1 -1
  64. package/table/README.md +14 -14
  65. package/toast/README.md +3 -1
  66. package/types/forty-cdk-avatar.d.ts +5 -0
  67. package/types/forty-cdk-combobox.d.ts +35 -5
  68. package/types/forty-cdk-context-menu.d.ts +12 -1
  69. package/types/forty-cdk-core.d.ts +177 -53
  70. package/types/forty-cdk-dropdown-menu.d.ts +12 -1
  71. package/types/forty-cdk-fieldset.d.ts +35 -4
  72. package/types/forty-cdk-menu.d.ts +19 -7
  73. package/types/forty-cdk-menubar.d.ts +19 -2
  74. package/types/forty-cdk-navigation-menu.d.ts +61 -3
  75. package/types/forty-cdk-radio-group.d.ts +9 -0
  76. package/types/forty-cdk-table.d.ts +20 -3
  77. package/types/forty-cdk-time-picker.d.ts +5 -0
  78. package/types/forty-cdk-toast.d.ts +46 -16
  79. package/types/forty-cdk-toggle.d.ts +5 -0
  80. package/types/forty-cdk-tree.d.ts +42 -5
@@ -621,7 +621,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
621
621
  - Disabled options keep the host `aria-disabled="true"`. Click and hover (activedescendant pinning) are no-ops on disabled options.
622
622
  - `[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).
623
623
  - `[forComboboxGroup]` is purely advisory grouping — options inside still register flatly with the root, so navigation flows through groups without interruption.
624
- - `[forComboboxEmpty]` carries `role="status"` + `aria-live="polite"` so the empty-state message is announced when filtering removes all matches.
624
+ - `[forComboboxEmpty]` and `[forComboboxStatus]` carry `role="status"` and nothing else — the role already implies `aria-live="polite"` and `aria-atomic="true"`, so the empty-state message is announced, whole, when filtering removes all matches. The role is the channel they keep (rather than the attribute pair) because `[forComboboxEmpty]` self-hides and comes back with its message already in the DOM: a live _role_ is what screen readers read reliably on insertion.
625
625
  - 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).
626
626
  - 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.
627
627
 
@@ -117,26 +117,26 @@ Both triggers carry `[menuPositioning]`, a partial `{ side, align, sideOffset, a
117
117
 
118
118
  ### `ForContextMenu`
119
119
 
120
- | Property | Type | Description |
121
- | --------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
122
- | `open` | `model<boolean>` | Two-way bindable. Whether the menu is shown.<br>**Default:** `false` |
123
- | `side` | `input<string>` | Anchor side relative to the pointer.<br>**Default:** `'bottom'` |
124
- | `align` | `input<string>` | Alignment along `side` (`'start'` / `'center'` / `'end'`).<br>**Default:** `'start'` |
125
- | `sideOffset` | `input<number>` | Gap (px) between the pointer and the menu along the main axis.<br>**Default:** `0` |
126
- | `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0` |
127
- | `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'` |
128
- | `loop` | `input<boolean>` | Whether arrow navigation wraps.<br>**Default:** `true` |
129
- | `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'` |
130
- | `disabled` | `input<boolean>` | When `true`, the contextmenu event falls through to the native browser menu.<br>**Default:** `false` |
131
- | `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true` |
132
- | `returnFocus` | `input<boolean>` | When `true`, focus returns to the trigger element on close.<br>**Default:** `true` |
133
- | `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` |
134
- | `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissible layer.<br>**Default:** — |
135
- | `pointerDownOutside` | `output<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down on a target outside content + trigger.<br>**Default:** — |
136
- | `focusOutside` | `output<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside content + trigger.<br>**Default:** — |
137
- | `interactOutside` | `output<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite — fires alongside the two above (and shares their veto state).<br>**Default:** — |
138
- | `autoFocusOnOpen` | `output<VetoableEvent>` | Output. Just before the imperative focus move on mount.<br>**Default:** — |
139
- | `autoFocusOnClose` | `output<VetoableEvent>` | Output. Just before the imperative focus move on unmount.<br>**Default:** — |
120
+ | Property | Type | Description |
121
+ | --------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
122
+ | `open` | `model<boolean>` | Two-way bindable. Whether the menu is shown.<br>**Default:** `false` |
123
+ | `side` | `input<string>` | Anchor side relative to the pointer.<br>**Default:** `'bottom'` |
124
+ | `align` | `input<string>` | Alignment along `side` (`'start'` / `'center'` / `'end'`).<br>**Default:** `'start'` |
125
+ | `sideOffset` | `input<number>` | Gap (px) between the pointer and the menu along the main axis.<br>**Default:** `0` |
126
+ | `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0` |
127
+ | `fallbackAxisSideDirection` | `input<'none' \| 'start' \| 'end'>` | When both sides of the preferred axis overflow, lets `flip` drop the menu to a perpendicular side instead of clipping. `'none'` keeps only the opposite same-axis placement. The default is read from `provideForContextMenuDefaults` for the surrounding scope — set it once for the whole app rather than per call site.<br>**Default:** `'none'` |
128
+ | `loop` | `input<boolean>` | Whether arrow navigation wraps.<br>**Default:** `true` |
129
+ | `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'` |
130
+ | `disabled` | `input<boolean>` | When `true`, the contextmenu event falls through to the native browser menu.<br>**Default:** `false` |
131
+ | `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true` |
132
+ | `returnFocus` | `input<boolean>` | When `true`, focus returns to the trigger element on close.<br>**Default:** `true` |
133
+ | `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` |
134
+ | `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissible layer.<br>**Default:** — |
135
+ | `pointerDownOutside` | `output<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down on a target outside content + trigger.<br>**Default:** — |
136
+ | `focusOutside` | `output<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside content + trigger.<br>**Default:** — |
137
+ | `interactOutside` | `output<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite — fires alongside the two above (and shares their veto state).<br>**Default:** — |
138
+ | `autoFocusOnOpen` | `output<VetoableEvent>` | Output. Just before the imperative focus move on mount.<br>**Default:** — |
139
+ | `autoFocusOnClose` | `output<VetoableEvent>` | Output. Just before the imperative focus move on unmount.<br>**Default:** — |
140
140
 
141
141
  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`.
142
142
 
@@ -133,26 +133,26 @@ Angular resolves `ng-template` DI at the template's **declaration** site, not wh
133
133
 
134
134
  ### `ForDropdownMenu`
135
135
 
136
- | Property | Type | Description |
137
- | --------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
138
- | `open` | `model<boolean>` | Two-way bindable. Whether the menu is shown.<br>**Default:** `false` |
139
- | `side` | `input<string>` | Anchor side of `[forMenuContent]` against the trigger.<br>**Default:** `'bottom'` |
140
- | `align` | `input<string>` | Alignment along `side` (`'start'` / `'center'` / `'end'`).<br>**Default:** `'start'` |
141
- | `sideOffset` | `input<number>` | Gap (px) between the trigger and the content along the main axis.<br>**Default:** `4` |
142
- | `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0` |
143
- | `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'` |
144
- | `loop` | `input<boolean>` | Whether arrow navigation wraps at the ends.<br>**Default:** `true` |
145
- | `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'` |
146
- | `disabled` | `input<boolean>` | When `true`, trigger interactions are ignored.<br>**Default:** `false` |
147
- | `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true` |
148
- | `returnFocus` | `input<boolean>` | When `true`, focus returns to the trigger on close.<br>**Default:** `true` |
149
- | `ariaLabel` | `input<string \| null>` | Manual `aria-label` on `[forMenuContent]` if the trigger isn't a meaningful name.<br>**Default:** `null` |
150
- | `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissible layer.<br>**Default:** — |
151
- | `pointerDownOutside` | `output<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down on a target outside content + trigger.<br>**Default:** — |
152
- | `focusOutside` | `output<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside content + trigger.<br>**Default:** — |
153
- | `interactOutside` | `output<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite — fires alongside the two above (and shares their veto state).<br>**Default:** — |
154
- | `autoFocusOnOpen` | `output<VetoableEvent>` | Output. Just before focus moves to the first / last enabled item on mount.<br>**Default:** — |
155
- | `autoFocusOnClose` | `output<VetoableEvent>` | Output. Just before focus returns to the trigger on unmount.<br>**Default:** — |
136
+ | Property | Type | Description |
137
+ | --------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
138
+ | `open` | `model<boolean>` | Two-way bindable. Whether the menu is shown.<br>**Default:** `false` |
139
+ | `side` | `input<string>` | Anchor side of `[forMenuContent]` against the trigger.<br>**Default:** `'bottom'` |
140
+ | `align` | `input<string>` | Alignment along `side` (`'start'` / `'center'` / `'end'`).<br>**Default:** `'start'` |
141
+ | `sideOffset` | `input<number>` | Gap (px) between the trigger and the content along the main axis.<br>**Default:** `4` |
142
+ | `alignOffset` | `input<number>` | Gap (px) along the cross axis (parallel to `side`).<br>**Default:** `0` |
143
+ | `fallbackAxisSideDirection` | `input<'none' \| 'start' \| 'end'>` | When both sides of the preferred axis overflow, lets `flip` drop the menu to a perpendicular side instead of clipping. `'none'` keeps only the opposite same-axis placement. The default is read from `provideForDropdownMenuDefaults` for the surrounding scope — set it once for the whole app rather than per call site.<br>**Default:** `'none'` |
144
+ | `loop` | `input<boolean>` | Whether arrow navigation wraps at the ends.<br>**Default:** `true` |
145
+ | `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'` |
146
+ | `disabled` | `input<boolean>` | When `true`, trigger interactions are ignored.<br>**Default:** `false` |
147
+ | `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true` |
148
+ | `returnFocus` | `input<boolean>` | When `true`, focus returns to the trigger on close.<br>**Default:** `true` |
149
+ | `ariaLabel` | `input<string \| null>` | Manual `aria-label` on `[forMenuContent]` if the trigger isn't a meaningful name.<br>**Default:** `null` |
150
+ | `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissible layer.<br>**Default:** — |
151
+ | `pointerDownOutside` | `output<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down on a target outside content + trigger.<br>**Default:** — |
152
+ | `focusOutside` | `output<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside content + trigger.<br>**Default:** — |
153
+ | `interactOutside` | `output<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite — fires alongside the two above (and shares their veto state).<br>**Default:** — |
154
+ | `autoFocusOnOpen` | `output<VetoableEvent>` | Output. Just before focus moves to the first / last enabled item on mount.<br>**Default:** — |
155
+ | `autoFocusOnClose` | `output<VetoableEvent>` | Output. Just before focus returns to the trigger on unmount.<br>**Default:** — |
156
156
 
157
157
  Every output above is vetoable — each handler receives a `VetoableEvent` (or `VetoableNativeEvent<E>` when there is a native DOM event). Call `preventDefault()` on the emitted veto to suppress the directive's default action; the original DOM event, when present, is on `.event`.
158
158
 
@@ -97,7 +97,10 @@ class ForAvatar {
97
97
  return this.#timerElapsed();
98
98
  }, /* @ts-ignore */
99
99
  ...(ngDevMode ? [{ debugName: "shouldShowFallback" }] : /* istanbul ignore next */ []));
100
- /** @internal */
100
+ /**
101
+ * Reported by `[forAvatarImage]` so the root can transition its state
102
+ * machine. Consumers should not call this.
103
+ */
101
104
  reportStatus(status) {
102
105
  this.#status.set(status);
103
106
  }
@@ -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: (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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"}
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 /**\n * Reported by `[forAvatarImage]` so the root can transition its state\n * machine. Consumers should not call this.\n */\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":";;;;;MA2Ba,kBAAkB,GAAG,IAAI,cAAc,CAAmB,oBAAoB;AAE3F;;;;;AAKG;AACG,SAAU,mBAAmB,CAAC,KAAa,EAAA;AAC/C,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1D,IAAI,CAAC,GAAG,EAAE;AACR,QAAA,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,CAAA,2CAAA,CAA6C,CAAC;IAC3F;AACA,IAAA,OAAO,GAAG;AACZ;;ACrBA;;;;AAIG;AACI,MAAM,4BAA4B,GAAsB;AAC7D,IAAA,eAAe,EAAE,CAAC;CACnB;AAED,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,CAC/C,qBAAqB,EACrB,4BAA4B,CAC7B;AAED;AACO,MAAM,mBAAmB,GAAG;AAEnC;;;;AAIG;AACG,SAAU,wBAAwB,CAAC,QAAA,GAAuC,EAAE,EAAA;AAChF,IAAA,OAAO,eAAe,CAAC,QAAQ,CAAC;AAClC;;AC3BA;;;;;;;;;;;;;;;;;;;;AAoBG;MASU,SAAS,CAAA;AACX,IAAA,SAAS,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAEhD;;;;;;;AAOG;AACM,IAAA,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,8BAAA,EAAA,CAAA,EAC7D,SAAS,EAAE,CAAC,KAAc,KAAI;AAC5B,YAAA,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC;AACrC,YAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe;AAC1E,QAAA,CAAC,GACD;IAEO,OAAO,GAAG,MAAM,CAAkB,MAAM;gFAAC;AACzC,IAAA,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;;;;;IAMlC,aAAa,GAAG,YAAY,CAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,eAAA,EAAA,8BAAA,EAAA,CAAA,EACnC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;AACzE,QAAA,WAAW,EAAE,MAAM,KAAK,GACxB;IACF,MAAM,GAAyC,IAAI;IAC1C,UAAU,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAEnD,IAAA,kBAAkB,GAAG,QAAQ,CAAC,MAAK;AAC1C,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE;QAC7B,IAAI,MAAM,KAAK,QAAQ;AAAE,YAAA,OAAO,KAAK;QACrC,IAAI,MAAM,KAAK,OAAO;AAAE,YAAA,OAAO,IAAI;;;AAGnC,QAAA,IAAI,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC;AAAE,YAAA,OAAO,IAAI;AAC5C,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE;IAC7B,CAAC;2FAAC;AAEF;;;AAGG;AACH,IAAA,YAAY,CAAC,MAAuB,EAAA;AAClC,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;IAC1B;AAEA,IAAA,WAAA,GAAA;;;;;;;QAOE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE;AAC7B,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,EAAE;YACpC,IAAI,CAAC,YAAY,EAAE;AACnB,YAAA,IAAI,IAAI,CAAC,UAAU,KAAK,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,SAAS,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE;AAC/E,gBAAA,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,MAAK;AAC5B,oBAAA,IAAI,CAAC,MAAM,GAAG,IAAI;AAClB,oBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;gBAC9B,CAAC,EAAE,KAAK,CAAC;YACX;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;IACzD;IAEA,YAAY,GAAA;AACV,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE;AACxB,YAAA,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;AACzB,YAAA,IAAI,CAAC,MAAM,GAAG,IAAI;QACpB;IACF;uGA7EW,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAT,SAAS,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,EAAA,EAAA,SAAA,EAFT,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAEzD,SAAS,EAAA,UAAA,EAAA,CAAA;kBARrB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,aAAa;AACvB,oBAAA,QAAQ,EAAE,WAAW;AACrB,oBAAA,IAAI,EAAE;AACJ,wBAAA,oBAAoB,EAAE,UAAU;AACjC,qBAAA;oBACD,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAA,SAAW,EAAE,CAAC;AACrE,iBAAA;;;AChCD;;;;;;;;AAQG;MAUU,cAAc,CAAA;AAChB,IAAA,KAAK,GAAG,MAAM,CAA+B,UAAU,CAAC,CAAC,aAAa;IACtE,OAAO,GAAG,MAAM,CAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAEjE;;;;AAIG;IACM,gBAAgB,GAAG,MAAM,EAAmB;AAErD,IAAA,WAAA,GAAA;AACE,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;AACjB,YAAA,MAAM,IAAI,KAAK,CACb,8EAA8E,CAC/E;QACH;AACA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO;AAC3B,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK;QAEvB,IAAI,SAAS,GAAG,KAAK;AACrB,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,OAAO,SAAS,GAAG,IAAI,CAAC,CAAC;QAEtD,eAAe,CAAC,MAAK;YACnB,IAAI,CAAC,aAAa,EAAE;AACtB,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,OAAO,gBAAgB,KAAK,WAAW,EAAE;AACrF,YAAA,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,MAAK;gBACzC,IAAI,CAAC,aAAa,EAAE;AACtB,YAAA,CAAC,CAAC;AACF,YAAA,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;AACtE,YAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC3D;;;;;;QAOA,IAAI,IAAI,GAA2B,IAAI;AACvC,QAAA,IAAI,iBAAiB,GAAG,CAAC,CAAC;AAC1B,QAAA,MAAM,IAAI,GAAG,CAAC,MAAuB,KAAU;AAC7C,YAAA,IAAI,SAAS;gBAAE;YACf,IAAI,IAAI,KAAK,MAAM,IAAI,iBAAiB,KAAK,IAAI,CAAC,aAAa;gBAAE;YACjE,IAAI,GAAG,MAAM;AACb,YAAA,iBAAiB,GAAG,IAAI,CAAC,aAAa;AACtC,YAAA,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;AAC3B,YAAA,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;AACpC,QAAA,CAAC;AACD,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;IACnB;IAEU,MAAM,GAAA;AACd,QAAA,OAAO,IAAI,CAAC,OAAQ,CAAC,MAAM,EAAE;IAC/B;IAEU,MAAM,GAAA;;;AAGd,QAAA,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAAE;AAC/B,QAAA,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ;YAAE;AAC1B,QAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IACtB;IAEU,OAAO,GAAA;AACf,QAAA,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAAE;AAC/B,QAAA,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ;YAAE;AAC1B,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;IACrB;AAEA;;;;;;;AAOG;IACH,iBAAiB,GAAA;AACf,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,WAAW;IAC5D;IAEA,aAAa,GAAA;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC;QAC1C,IAAI,CAAC,aAAa,EAAE;AACpB,QAAA,IAAI,CAAC,WAAW,GAAG,GAAG;QACtB,IAAI,CAAC,GAAG,EAAE;AACR,YAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YAClB;QACF;AACA,QAAA,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE;;YAEvB,IAAI,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,EAAE;AAC/B,gBAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBACpB;YACF;;;;;;AAMA,YAAA,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;AACrB,YAAA,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC;YAC/B;QACF;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;IACvB;AAEA,IAAA,qBAAqB,CAAC,GAAW,EAAA;AAC/B,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAClD,IAAI,CAAC,MAAM,EAAE;;AAEX,YAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;YACnB;QACF;AACA,QAAA,MAAM,EAAE,CAAC,IAAI,CACX,MAAK;YACH,IAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE;AAC1C,gBAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;YACtB;QACF,CAAC,EACD,MAAK;YACH,IAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE;AAC1C,gBAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;YACrB;AACF,QAAA,CAAC,CACF;IACH;;AAGA,IAAA,KAAK;;IAGL,aAAa,GAAG,CAAC;;IAEjB,WAAW,GAAkB,IAAI;uGAxItB,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAd,cAAc,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,MAAA,EAAA,UAAA,EAAA,OAAA,EAAA,WAAA,EAAA,EAAA,UAAA,EAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAd,cAAc,EAAA,UAAA,EAAA,CAAA;kBAT1B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,qBAAqB;AAC/B,oBAAA,QAAQ,EAAE,gBAAgB;AAC1B,oBAAA,IAAI,EAAE;AACJ,wBAAA,oBAAoB,EAAE,UAAU;AAChC,wBAAA,QAAQ,EAAE,UAAU;AACpB,wBAAA,SAAS,EAAE,WAAW;AACvB,qBAAA;AACF,iBAAA;;;AC1BD;;;;;;;AAOG;MAQU,iBAAiB,CAAA;AACT,IAAA,MAAM,GAAG,mBAAmB,CAAC,mBAAmB,CAAC;uGADzD,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAjB,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,kBAAA,EAAA,iBAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAjB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAP7B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,qBAAqB;AAC/B,oBAAA,QAAQ,EAAE,mBAAmB;AAC7B,oBAAA,IAAI,EAAE;AACJ,wBAAA,oBAAoB,EAAE,iBAAiB;AACxC,qBAAA;AACF,iBAAA;;;AClBD;;AAEG;;;;"}
@@ -14,10 +14,15 @@ const forBreakpointsTailwind = {
14
14
  xl: 1280,
15
15
  '2xl': 1536,
16
16
  };
17
- const FALLBACK = {
17
+ /**
18
+ * Library fallback for breakpoints defaults, read at the root injector when no
19
+ * consumer has called `provideForBreakpointsDefaults`. Exported for the shared
20
+ * defaults contract spec; not re-exported from the primitive's public entry.
21
+ */
22
+ const FOR_BREAKPOINTS_FALLBACK_DEFAULTS = {
18
23
  breakpoints: forBreakpointsTailwind,
19
24
  };
20
- const { token, provideDefaults } = createDefaults('FOR_BREAKPOINTS_DEFAULTS', FALLBACK);
25
+ const { token, provideDefaults } = createDefaults('FOR_BREAKPOINTS_DEFAULTS', FOR_BREAKPOINTS_FALLBACK_DEFAULTS);
21
26
  /**
22
27
  * Token holding the resolved breakpoint map for the current injector scope.
23
28
  * The library always provides a fully-populated value (the Tailwind fallback
@@ -1 +1 @@
1
- {"version":3,"file":"forty-cdk-breakpoints.mjs","sources":["../../../projects/forty-cdk/breakpoints/src/breakpoints-defaults.ts","../../../projects/forty-cdk/breakpoints/src/breakpoints.ts","../../../projects/forty-cdk/breakpoints/src/forty-cdk-breakpoints.ts"],"sourcesContent":["import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * A breakpoint map: each named breakpoint paired with its `min-width`\n * threshold in CSS pixels. Names are arbitrary — `injectBreakpoints` derives\n * its query methods (`up`, `down`, `between`, `only`) from whatever keys this\n * map declares, sorted ascending by threshold.\n */\nexport type BreakpointMap = Record<string, number>;\n\n/**\n * The default breakpoint map, mirroring Tailwind CSS's `sm`–`2xl` scale. Used\n * as the library fallback when no `provideForBreakpointsDefaults` is in scope,\n * and the source of the typed breakpoint names when the consumer has not\n * augmented {@link BreakpointRegistry}.\n */\nexport const forBreakpointsTailwind = {\n sm: 640,\n md: 768,\n lg: 1024,\n xl: 1280,\n '2xl': 1536,\n} as const satisfies BreakpointMap;\n\n/** The breakpoint names from the default Tailwind scale. */\nexport type TailwindBreakpointName = keyof typeof forBreakpointsTailwind;\n\n/** Resolved shape stored against {@link FOR_BREAKPOINTS_DEFAULTS}. */\nexport interface ForBreakpointsDefaults {\n /** The active breakpoint map for the current injector scope. */\n breakpoints: BreakpointMap;\n}\n\nconst FALLBACK: ForBreakpointsDefaults = {\n breakpoints: forBreakpointsTailwind,\n};\n\nconst { token, provideDefaults } = createDefaults<ForBreakpointsDefaults>(\n 'FOR_BREAKPOINTS_DEFAULTS',\n FALLBACK,\n);\n\n/**\n * Token holding the resolved breakpoint map for the current injector scope.\n * The library always provides a fully-populated value (the Tailwind fallback\n * at the root, or the map from the nearest `provideForBreakpointsDefaults`).\n */\nexport const FOR_BREAKPOINTS_DEFAULTS = token;\n\n/**\n * Configures the breakpoint map for this injector scope so `injectBreakpoints`\n * can be called with no arguments. Provide it once in your application config:\n *\n * ```ts\n * providers: [\n * provideForBreakpointsDefaults({ mobile: 0, tablet: 640, laptop: 1024, desktop: 1280 }),\n * ]\n * ```\n *\n * Providing it again on a component injector replaces the map for that subtree\n * only (the nearest scope wins; the map is replaced wholesale, never merged\n * key-by-key). Omit it entirely to use {@link forBreakpointsTailwind}.\n */\nexport function provideForBreakpointsDefaults(breakpoints: BreakpointMap): Provider[] {\n return provideDefaults({ breakpoints });\n}\n","import { Injector, computed, inject, runInInjectionContext, type Signal } from '@angular/core';\n\nimport { injectMediaQuery } from 'forty-cdk/core';\nimport { FOR_BREAKPOINTS_DEFAULTS, type TailwindBreakpointName } from './breakpoints-defaults';\n\n/**\n * Extension point for typing custom breakpoint names. Augment it via module\n * declaration merging so `injectBreakpoints` autocompletes your own names\n * instead of the default Tailwind scale:\n *\n * ```ts\n * import type { appBreakpoints } from './breakpoints';\n *\n * declare module 'forty-cdk' {\n * interface BreakpointRegistry extends Record<keyof typeof appBreakpoints, true> {}\n * }\n * ```\n *\n * When left empty (no augmentation), {@link BreakpointName} falls back to the\n * keys of {@link forBreakpointsTailwind}.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface BreakpointRegistry {}\n\n/**\n * The set of valid breakpoint names. Resolves to the augmented\n * {@link BreakpointRegistry} keys when present, otherwise the Tailwind scale\n * (`'sm' | 'md' | 'lg' | 'xl' | '2xl'`).\n */\nexport type BreakpointName = [keyof BreakpointRegistry] extends [never]\n ? TailwindBreakpointName\n : keyof BreakpointRegistry & string;\n\n/** Reactive handle returned by {@link injectBreakpoints}. */\nexport interface ForBreakpoints<K extends string = BreakpointName> {\n /**\n * Matches the named breakpoint and wider — `(min-width: <threshold>px)`.\n * @param name A breakpoint declared in the active map.\n */\n up(name: K): Signal<boolean>;\n /**\n * Matches narrower than the named breakpoint —\n * `(max-width: <threshold - 0.02>px)`. The 0.02px back-off keeps `up(name)`\n * and `down(name)` from both matching at the exact threshold.\n * @param name A breakpoint declared in the active map.\n */\n down(name: K): Signal<boolean>;\n /**\n * Matches from `min` (inclusive) up to but not including `max`.\n * @param min Lower breakpoint, inclusive.\n * @param max Upper breakpoint, exclusive.\n */\n between(min: K, max: K): Signal<boolean>;\n /**\n * Matches only the named breakpoint's own band — from its threshold up to\n * but not including the next-larger breakpoint (open-ended for the largest).\n * @param name A breakpoint declared in the active map.\n */\n only(name: K): Signal<boolean>;\n /**\n * The largest breakpoint whose `min-width` currently matches, or `null` when\n * the viewport is narrower than the smallest breakpoint (and on the server).\n */\n readonly active: Signal<K | null>;\n /**\n * Escape hatch for an arbitrary media query string (orientation, pointer,\n * `prefers-color-scheme`, …) that the named helpers don't cover.\n * @param query Any valid media query.\n */\n matches(query: string): Signal<boolean>;\n}\n\n/**\n * A signal-first, zoneless, SSR-safe viewport breakpoint observer. Reads the\n * breakpoint map from the ambient {@link FOR_BREAKPOINTS_DEFAULTS} token\n * (configured with `provideForBreakpointsDefaults`, or the Tailwind fallback),\n * so call sites never repeat the breakpoint set:\n *\n * ```ts\n * private bp = injectBreakpoints();\n * protected isDesktop = this.bp.up('desktop');\n * protected active = this.bp.active;\n * ```\n *\n * Each query method returns a `Signal<boolean>` backed by a cached\n * `MediaQueryList`. The returned handle captures the calling injection\n * context, so its methods can be invoked lazily from `computed()` or a\n * template — not only during construction. Listeners are torn down with the\n * injector. `active`'s per-breakpoint queries are materialized eagerly at call\n * time, so reading `active` never attaches a listener from inside a reactive\n * computation or after teardown. On the server (or where `matchMedia` is\n * unavailable) every signal reads `false` and `active` reads `null`.\n *\n * Must be called from an injection context.\n *\n * @returns A {@link ForBreakpoints} handle of reactive query methods.\n */\nexport function injectBreakpoints(): ForBreakpoints {\n const injector = inject(Injector);\n const map = inject(FOR_BREAKPOINTS_DEFAULTS).breakpoints;\n const names = Object.keys(map).sort((a, b) => map[a]! - map[b]!);\n const cache = new Map<string, Signal<boolean>>();\n\n const observe = (query: string): Signal<boolean> => {\n let result = cache.get(query);\n if (result === undefined) {\n result = runInInjectionContext(injector, () => injectMediaQuery(query));\n cache.set(query, result);\n }\n return result;\n };\n\n const thresholdOf = (name: string): number => {\n const value = map[name];\n if (value === undefined) {\n throw new Error(\n `[forty-cdk/breakpoints] Unknown breakpoint \"${name}\". Defined breakpoints: ${\n names.join(', ') || '(none)'\n }.`,\n );\n }\n return value;\n };\n\n const up = (name: string): Signal<boolean> => observe(`(min-width: ${thresholdOf(name)}px)`);\n\n const down = (name: string): Signal<boolean> =>\n observe(`(max-width: ${thresholdOf(name) - 0.02}px)`);\n\n const between = (min: string, max: string): Signal<boolean> =>\n observe(`(min-width: ${thresholdOf(min)}px) and (max-width: ${thresholdOf(max) - 0.02}px)`);\n\n const only = (name: string): Signal<boolean> => {\n const lower = thresholdOf(name);\n const index = names.indexOf(name);\n const next = index < names.length - 1 ? thresholdOf(names[index + 1]!) : null;\n return next === null\n ? observe(`(min-width: ${lower}px)`)\n : observe(`(min-width: ${lower}px) and (max-width: ${next - 0.02}px)`);\n };\n\n const activeCandidates = names.map((name) => ({\n name,\n matches: observe(`(min-width: ${map[name]}px)`),\n }));\n\n const active = computed<BreakpointName | null>(() => {\n let current: string | null = null;\n for (const candidate of activeCandidates) {\n if (candidate.matches()) {\n current = candidate.name;\n }\n }\n return current as BreakpointName | null;\n });\n\n return { up, down, between, only, active, matches: (query) => observe(query) };\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-breakpoints.mjs","sources":["../../../projects/forty-cdk/breakpoints/src/breakpoints-defaults.ts","../../../projects/forty-cdk/breakpoints/src/breakpoints.ts","../../../projects/forty-cdk/breakpoints/src/forty-cdk-breakpoints.ts"],"sourcesContent":["import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * A breakpoint map: each named breakpoint paired with its `min-width`\n * threshold in CSS pixels. Names are arbitrary — `injectBreakpoints` derives\n * its query methods (`up`, `down`, `between`, `only`) from whatever keys this\n * map declares, sorted ascending by threshold.\n */\nexport type BreakpointMap = Record<string, number>;\n\n/**\n * The default breakpoint map, mirroring Tailwind CSS's `sm`–`2xl` scale. Used\n * as the library fallback when no `provideForBreakpointsDefaults` is in scope,\n * and the source of the typed breakpoint names when the consumer has not\n * augmented {@link BreakpointRegistry}.\n */\nexport const forBreakpointsTailwind = {\n sm: 640,\n md: 768,\n lg: 1024,\n xl: 1280,\n '2xl': 1536,\n} as const satisfies BreakpointMap;\n\n/** The breakpoint names from the default Tailwind scale. */\nexport type TailwindBreakpointName = keyof typeof forBreakpointsTailwind;\n\n/** Resolved shape stored against {@link FOR_BREAKPOINTS_DEFAULTS}. */\nexport interface ForBreakpointsDefaults {\n /** The active breakpoint map for the current injector scope. */\n breakpoints: BreakpointMap;\n}\n\n/**\n * Library fallback for breakpoints defaults, read at the root injector when no\n * consumer has called `provideForBreakpointsDefaults`. Exported for the shared\n * defaults contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_BREAKPOINTS_FALLBACK_DEFAULTS: ForBreakpointsDefaults = {\n breakpoints: forBreakpointsTailwind,\n};\n\nconst { token, provideDefaults } = createDefaults<ForBreakpointsDefaults>(\n 'FOR_BREAKPOINTS_DEFAULTS',\n FOR_BREAKPOINTS_FALLBACK_DEFAULTS,\n);\n\n/**\n * Token holding the resolved breakpoint map for the current injector scope.\n * The library always provides a fully-populated value (the Tailwind fallback\n * at the root, or the map from the nearest `provideForBreakpointsDefaults`).\n */\nexport const FOR_BREAKPOINTS_DEFAULTS = token;\n\n/**\n * Configures the breakpoint map for this injector scope so `injectBreakpoints`\n * can be called with no arguments. Provide it once in your application config:\n *\n * ```ts\n * providers: [\n * provideForBreakpointsDefaults({ mobile: 0, tablet: 640, laptop: 1024, desktop: 1280 }),\n * ]\n * ```\n *\n * Providing it again on a component injector replaces the map for that subtree\n * only (the nearest scope wins; the map is replaced wholesale, never merged\n * key-by-key). Omit it entirely to use {@link forBreakpointsTailwind}.\n */\nexport function provideForBreakpointsDefaults(breakpoints: BreakpointMap): Provider[] {\n return provideDefaults({ breakpoints });\n}\n","import { Injector, computed, inject, runInInjectionContext, type Signal } from '@angular/core';\n\nimport { injectMediaQuery } from 'forty-cdk/core';\nimport { FOR_BREAKPOINTS_DEFAULTS, type TailwindBreakpointName } from './breakpoints-defaults';\n\n/**\n * Extension point for typing custom breakpoint names. Augment it via module\n * declaration merging so `injectBreakpoints` autocompletes your own names\n * instead of the default Tailwind scale:\n *\n * ```ts\n * import type { appBreakpoints } from './breakpoints';\n *\n * declare module 'forty-cdk' {\n * interface BreakpointRegistry extends Record<keyof typeof appBreakpoints, true> {}\n * }\n * ```\n *\n * When left empty (no augmentation), {@link BreakpointName} falls back to the\n * keys of {@link forBreakpointsTailwind}.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface BreakpointRegistry {}\n\n/**\n * The set of valid breakpoint names. Resolves to the augmented\n * {@link BreakpointRegistry} keys when present, otherwise the Tailwind scale\n * (`'sm' | 'md' | 'lg' | 'xl' | '2xl'`).\n */\nexport type BreakpointName = [keyof BreakpointRegistry] extends [never]\n ? TailwindBreakpointName\n : keyof BreakpointRegistry & string;\n\n/** Reactive handle returned by {@link injectBreakpoints}. */\nexport interface ForBreakpoints<K extends string = BreakpointName> {\n /**\n * Matches the named breakpoint and wider — `(min-width: <threshold>px)`.\n * @param name A breakpoint declared in the active map.\n */\n up(name: K): Signal<boolean>;\n /**\n * Matches narrower than the named breakpoint —\n * `(max-width: <threshold - 0.02>px)`. The 0.02px back-off keeps `up(name)`\n * and `down(name)` from both matching at the exact threshold.\n * @param name A breakpoint declared in the active map.\n */\n down(name: K): Signal<boolean>;\n /**\n * Matches from `min` (inclusive) up to but not including `max`.\n * @param min Lower breakpoint, inclusive.\n * @param max Upper breakpoint, exclusive.\n */\n between(min: K, max: K): Signal<boolean>;\n /**\n * Matches only the named breakpoint's own band — from its threshold up to\n * but not including the next-larger breakpoint (open-ended for the largest).\n * @param name A breakpoint declared in the active map.\n */\n only(name: K): Signal<boolean>;\n /**\n * The largest breakpoint whose `min-width` currently matches, or `null` when\n * the viewport is narrower than the smallest breakpoint (and on the server).\n */\n readonly active: Signal<K | null>;\n /**\n * Escape hatch for an arbitrary media query string (orientation, pointer,\n * `prefers-color-scheme`, …) that the named helpers don't cover.\n * @param query Any valid media query.\n */\n matches(query: string): Signal<boolean>;\n}\n\n/**\n * A signal-first, zoneless, SSR-safe viewport breakpoint observer. Reads the\n * breakpoint map from the ambient {@link FOR_BREAKPOINTS_DEFAULTS} token\n * (configured with `provideForBreakpointsDefaults`, or the Tailwind fallback),\n * so call sites never repeat the breakpoint set:\n *\n * ```ts\n * private bp = injectBreakpoints();\n * protected isDesktop = this.bp.up('desktop');\n * protected active = this.bp.active;\n * ```\n *\n * Each query method returns a `Signal<boolean>` backed by a cached\n * `MediaQueryList`. The returned handle captures the calling injection\n * context, so its methods can be invoked lazily from `computed()` or a\n * template — not only during construction. Listeners are torn down with the\n * injector. `active`'s per-breakpoint queries are materialized eagerly at call\n * time, so reading `active` never attaches a listener from inside a reactive\n * computation or after teardown. On the server (or where `matchMedia` is\n * unavailable) every signal reads `false` and `active` reads `null`.\n *\n * Must be called from an injection context.\n *\n * @returns A {@link ForBreakpoints} handle of reactive query methods.\n */\nexport function injectBreakpoints(): ForBreakpoints {\n const injector = inject(Injector);\n const map = inject(FOR_BREAKPOINTS_DEFAULTS).breakpoints;\n const names = Object.keys(map).sort((a, b) => map[a]! - map[b]!);\n const cache = new Map<string, Signal<boolean>>();\n\n const observe = (query: string): Signal<boolean> => {\n let result = cache.get(query);\n if (result === undefined) {\n result = runInInjectionContext(injector, () => injectMediaQuery(query));\n cache.set(query, result);\n }\n return result;\n };\n\n const thresholdOf = (name: string): number => {\n const value = map[name];\n if (value === undefined) {\n throw new Error(\n `[forty-cdk/breakpoints] Unknown breakpoint \"${name}\". Defined breakpoints: ${\n names.join(', ') || '(none)'\n }.`,\n );\n }\n return value;\n };\n\n const up = (name: string): Signal<boolean> => observe(`(min-width: ${thresholdOf(name)}px)`);\n\n const down = (name: string): Signal<boolean> =>\n observe(`(max-width: ${thresholdOf(name) - 0.02}px)`);\n\n const between = (min: string, max: string): Signal<boolean> =>\n observe(`(min-width: ${thresholdOf(min)}px) and (max-width: ${thresholdOf(max) - 0.02}px)`);\n\n const only = (name: string): Signal<boolean> => {\n const lower = thresholdOf(name);\n const index = names.indexOf(name);\n const next = index < names.length - 1 ? thresholdOf(names[index + 1]!) : null;\n return next === null\n ? observe(`(min-width: ${lower}px)`)\n : observe(`(min-width: ${lower}px) and (max-width: ${next - 0.02}px)`);\n };\n\n const activeCandidates = names.map((name) => ({\n name,\n matches: observe(`(min-width: ${map[name]}px)`),\n }));\n\n const active = computed<BreakpointName | null>(() => {\n let current: string | null = null;\n for (const candidate of activeCandidates) {\n if (candidate.matches()) {\n current = candidate.name;\n }\n }\n return current as BreakpointName | null;\n });\n\n return { up, down, between, only, active, matches: (query) => observe(query) };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;AAYA;;;;;AAKG;AACI,MAAM,sBAAsB,GAAG;AACpC,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,EAAE,EAAE,IAAI;AACR,IAAA,EAAE,EAAE,IAAI;AACR,IAAA,KAAK,EAAE,IAAI;;AAYb;;;;AAIG;AACI,MAAM,iCAAiC,GAA2B;AACvE,IAAA,WAAW,EAAE,sBAAsB;CACpC;AAED,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,CAC/C,0BAA0B,EAC1B,iCAAiC,CAClC;AAED;;;;AAIG;AACI,MAAM,wBAAwB,GAAG;AAExC;;;;;;;;;;;;;AAaG;AACG,SAAU,6BAA6B,CAAC,WAA0B,EAAA;AACtE,IAAA,OAAO,eAAe,CAAC,EAAE,WAAW,EAAE,CAAC;AACzC;;ACAA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;SACa,iBAAiB,GAAA;AAC/B,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,MAAM,GAAG,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC,WAAW;AACxD,IAAA,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAE,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC;AAChE,IAAA,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B;AAEhD,IAAA,MAAM,OAAO,GAAG,CAAC,KAAa,KAAqB;QACjD,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AAC7B,QAAA,IAAI,MAAM,KAAK,SAAS,EAAE;AACxB,YAAA,MAAM,GAAG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACvE,YAAA,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC;QAC1B;AACA,QAAA,OAAO,MAAM;AACf,IAAA,CAAC;AAED,IAAA,MAAM,WAAW,GAAG,CAAC,IAAY,KAAY;AAC3C,QAAA,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC;AACvB,QAAA,IAAI,KAAK,KAAK,SAAS,EAAE;AACvB,YAAA,MAAM,IAAI,KAAK,CACb,CAAA,4CAAA,EAA+C,IAAI,2BACjD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QACtB,CAAA,CAAA,CAAG,CACJ;QACH;AACA,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,EAAE,GAAG,CAAC,IAAY,KAAsB,OAAO,CAAC,CAAA,YAAA,EAAe,WAAW,CAAC,IAAI,CAAC,CAAA,GAAA,CAAK,CAAC;AAE5F,IAAA,MAAM,IAAI,GAAG,CAAC,IAAY,KACxB,OAAO,CAAC,CAAA,YAAA,EAAe,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA,GAAA,CAAK,CAAC;IAEvD,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,GAAW,KACvC,OAAO,CAAC,CAAA,YAAA,EAAe,WAAW,CAAC,GAAG,CAAC,CAAA,oBAAA,EAAuB,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA,GAAA,CAAK,CAAC;AAE7F,IAAA,MAAM,IAAI,GAAG,CAAC,IAAY,KAAqB;AAC7C,QAAA,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC;QAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QACjC,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAE,CAAC,GAAG,IAAI;QAC7E,OAAO,IAAI,KAAK;AACd,cAAE,OAAO,CAAC,CAAA,YAAA,EAAe,KAAK,KAAK;cACjC,OAAO,CAAC,CAAA,YAAA,EAAe,KAAK,CAAA,oBAAA,EAAuB,IAAI,GAAG,IAAI,CAAA,GAAA,CAAK,CAAC;AAC1E,IAAA,CAAC;IAED,MAAM,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;QAC5C,IAAI;QACJ,OAAO,EAAE,OAAO,CAAC,CAAA,YAAA,EAAe,GAAG,CAAC,IAAI,CAAC,CAAA,GAAA,CAAK,CAAC;AAChD,KAAA,CAAC,CAAC;AAEH,IAAA,MAAM,MAAM,GAAG,QAAQ,CAAwB,MAAK;QAClD,IAAI,OAAO,GAAkB,IAAI;AACjC,QAAA,KAAK,MAAM,SAAS,IAAI,gBAAgB,EAAE;AACxC,YAAA,IAAI,SAAS,CAAC,OAAO,EAAE,EAAE;AACvB,gBAAA,OAAO,GAAG,SAAS,CAAC,IAAI;YAC1B;QACF;AACA,QAAA,OAAO,OAAgC;IACzC,CAAC;+EAAC;IAEF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK,CAAC,EAAE;AAChF;;AC7JA;;AAEG;;;;"}
@@ -24,7 +24,8 @@ import { isPlatformBrowser } from '@angular/common';
24
24
  * - Bootstrap-safety: `TestBed.resetTestingModule()`, micro-frontend reloads,
25
25
  * and anything else that destroys `ApplicationRef` must not leave stale
26
26
  * `document` listeners behind. The listeners are registered in the
27
- * constructor and removed via `DestroyRef`.
27
+ * constructor against a single `AbortController` and dropped by one
28
+ * `abort()` from `DestroyRef`.
28
29
  * - SSR safety: `document` is inaccessible on the server. The service is a
29
30
  * no-op when `PLATFORM_ID` is not the browser; `keyboard` stays `false` and
30
31
  * no listener is installed.
@@ -58,12 +59,11 @@ class InputModality {
58
59
  if (!this.#isBrowser) {
59
60
  return;
60
61
  }
61
- this.#document.addEventListener('keydown', this.#onKeyDown, true);
62
- this.#document.addEventListener('pointerdown', this.#onPointerDown, true);
63
- inject(DestroyRef).onDestroy(() => {
64
- this.#document.removeEventListener('keydown', this.#onKeyDown, true);
65
- this.#document.removeEventListener('pointerdown', this.#onPointerDown, true);
66
- });
62
+ const controller = new AbortController();
63
+ const options = { capture: true, signal: controller.signal };
64
+ this.#document.addEventListener('keydown', this.#onKeyDown, options);
65
+ this.#document.addEventListener('pointerdown', this.#onPointerDown, options);
66
+ inject(DestroyRef).onDestroy(() => controller.abort());
67
67
  }
68
68
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: InputModality, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
69
69
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: InputModality, providedIn: 'root' });
@@ -139,12 +139,11 @@ function injectHovered(opts) {
139
139
  const onPointerLeave = () => {
140
140
  hovered.set(false);
141
141
  };
142
- el.addEventListener('pointerenter', onPointerEnter);
143
- el.addEventListener('pointerleave', onPointerLeave);
144
- inject(DestroyRef).onDestroy(() => {
145
- el.removeEventListener('pointerenter', onPointerEnter);
146
- el.removeEventListener('pointerleave', onPointerLeave);
147
- });
142
+ const controller = new AbortController();
143
+ const options = { signal: controller.signal };
144
+ el.addEventListener('pointerenter', onPointerEnter, options);
145
+ el.addEventListener('pointerleave', onPointerLeave, options);
146
+ inject(DestroyRef).onDestroy(() => controller.abort());
148
147
  return computed(() => (disabled?.() ? false : hovered()));
149
148
  }
150
149
 
@@ -205,20 +204,15 @@ function injectPressed(opts) {
205
204
  const onBlur = () => {
206
205
  pressed.set(false);
207
206
  };
208
- el.addEventListener('pointerdown', onPointerDown);
209
- el.addEventListener('pointerup', onPointerEnd);
210
- el.addEventListener('pointerleave', onPointerEnd);
211
- el.addEventListener('keydown', onKeyDown);
212
- el.addEventListener('keyup', onKeyUp);
213
- el.addEventListener('blur', onBlur);
214
- inject(DestroyRef).onDestroy(() => {
215
- el.removeEventListener('pointerdown', onPointerDown);
216
- el.removeEventListener('pointerup', onPointerEnd);
217
- el.removeEventListener('pointerleave', onPointerEnd);
218
- el.removeEventListener('keydown', onKeyDown);
219
- el.removeEventListener('keyup', onKeyUp);
220
- el.removeEventListener('blur', onBlur);
221
- });
207
+ const controller = new AbortController();
208
+ const options = { signal: controller.signal };
209
+ el.addEventListener('pointerdown', onPointerDown, options);
210
+ el.addEventListener('pointerup', onPointerEnd, options);
211
+ el.addEventListener('pointerleave', onPointerEnd, options);
212
+ el.addEventListener('keydown', onKeyDown, options);
213
+ el.addEventListener('keyup', onKeyUp, options);
214
+ el.addEventListener('blur', onBlur, options);
215
+ inject(DestroyRef).onDestroy(() => controller.abort());
222
216
  return computed(() => (disabled?.() ? false : pressed()));
223
217
  }
224
218
 
@@ -1 +1 @@
1
- {"version":3,"file":"forty-cdk-button.mjs","sources":["../../../projects/forty-cdk/button/src/modality.ts","../../../projects/forty-cdk/button/src/focus-visible.ts","../../../projects/forty-cdk/button/src/hovered.ts","../../../projects/forty-cdk/button/src/pressed.ts","../../../projects/forty-cdk/button/src/button.ts","../../../projects/forty-cdk/button/src/forty-cdk-button.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n DOCUMENT,\n DestroyRef,\n Injectable,\n PLATFORM_ID,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\n\n/**\n * Application-scoped tracker for the last input modality the user employed —\n * the in-house implementation of input-modality (pointer vs keyboard)\n * detection. Created once per Angular bootstrap (one per SSR\n * request), tied to the root injector lifetime.\n *\n * It installs exactly **one** capture-phase `keydown` and **one** capture-phase\n * `pointerdown` listener on `document`, regardless of how many primitives read\n * its state — every `injectFocusVisible()` consumer shares this single\n * singleton, so the document is never listened to more than once. Capture phase\n * is used so the modality is settled before any overlay content can stop\n * propagation.\n *\n * Why a service rather than module-level state:\n *\n * - SSR isolation: module-level globals leak between simultaneous server\n * requests in the same Node process. A `providedIn: 'root'` service is\n * instantiated per application injector.\n * - Bootstrap-safety: `TestBed.resetTestingModule()`, micro-frontend reloads,\n * and anything else that destroys `ApplicationRef` must not leave stale\n * `document` listeners behind. The listeners are registered in the\n * constructor and removed via `DestroyRef`.\n * - SSR safety: `document` is inaccessible on the server. The service is a\n * no-op when `PLATFORM_ID` is not the browser; `keyboard` stays `false` and\n * no listener is installed.\n *\n * Internal — not re-exported from `public-api.ts`.\n */\n@Injectable({ providedIn: 'root' })\nexport class InputModality {\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n readonly #keyboard = signal(false);\n\n /**\n * `true` when the most recent input modality was the keyboard, `false` after\n * a pointer interaction (or before any interaction). Stays `false` on the\n * server. A `keydown` carrying a `Meta` / `Control` / `Alt` modifier is\n * treated as a shortcut rather than keyboard navigation and does **not** flip\n * this to `true` (matching the platform `:focus-visible` heuristic); `Shift`\n * alone still counts, since `Shift`+`Tab` is legitimate keyboard navigation.\n */\n readonly keyboard: Signal<boolean> = this.#keyboard.asReadonly();\n\n readonly #onKeyDown = (event: KeyboardEvent): void => {\n if (event.metaKey || event.ctrlKey || event.altKey) {\n return;\n }\n this.#keyboard.set(true);\n };\n readonly #onPointerDown = (): void => {\n this.#keyboard.set(false);\n };\n\n constructor() {\n if (!this.#isBrowser) {\n return;\n }\n this.#document.addEventListener('keydown', this.#onKeyDown, true);\n this.#document.addEventListener('pointerdown', this.#onPointerDown, true);\n\n inject(DestroyRef).onDestroy(() => {\n this.#document.removeEventListener('keydown', this.#onKeyDown, true);\n this.#document.removeEventListener('pointerdown', this.#onPointerDown, true);\n });\n }\n}\n","import { inject, type Signal } from '@angular/core';\n\nimport { InputModality } from './modality';\n\n/**\n * Returns a `Signal<boolean>` that is `true` while the last global input\n * modality was the keyboard, and `false` after a pointer interaction (or\n * before any interaction). This is the modality half of `:focus-visible`\n * styling: a consumer that wants a focus ring only on keyboard focus combines\n * this with the element's own focused state (`:focus`, or a `focusin` /\n * `focusout` listener) — e.g. `host: { '[attr.data-focus-visible]':\n * \"focused() && focusVisible() ? '' : null\" }`.\n *\n * Backed by the application-scoped {@link InputModality} singleton, so every\n * consumer shares a single capture-phase `keydown` / `pointerdown` listener on\n * `document` no matter how many call this.\n *\n * SSR-safe: on the server the backing service installs no listener and the\n * signal stays `false`. Must be called from an injection context.\n *\n * Internal — not re-exported from `public-api.ts`.\n */\nexport function injectFocusVisible(): Signal<boolean> {\n return inject(InputModality).keyboard;\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { isNonTouchPointer } from 'forty-cdk/core';\n\n/** Options for {@link injectHovered}. */\nexport interface HoveredOptions {\n /**\n * When this signal reports `true`, the returned signal is forced to `false`\n * and no interaction can set it — a disabled control is never hovered.\n */\n disabled?: Signal<boolean>;\n}\n\n/**\n * Returns a `Signal<boolean>` reflecting whether a pointing device is currently\n * hovering the host element. State is set on `pointerenter` and cleared on\n * `pointerleave`.\n *\n * Touch is suppressed through the shared `isNonTouchPointer` predicate: a\n * `pointerenter` whose `pointerType` is `'touch'` does not set the hovered\n * state, so the emulated mouse-enter a tap produces on a touchscreen never\n * leaves the element stuck in a hovered state after the finger lifts. Only\n * `'mouse'` / `'pen'` pointers report hover — this is the pen-inclusive hover\n * vocabulary, not the menu family's mouse-only `isHoverCapablePointer`.\n *\n * Attaches `pointerenter` / `pointerleave` listeners to the host element. The\n * `disabled` option short-circuits the result reactively (a pure `computed`,\n * never an `effect`-written signal) and also stops hover from arming while\n * disabled.\n *\n * SSR-safe: on the server no listener is attached and the signal stays\n * `false`. Must be called from an injection context (injects `ElementRef`,\n * `PLATFORM_ID`, `DestroyRef`).\n *\n * Internal — not re-exported from `public-api.ts`.\n */\nexport function injectHovered(opts?: HoveredOptions): Signal<boolean> {\n const disabled = opts?.disabled;\n const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n if (!isBrowser) {\n return signal(false).asReadonly();\n }\n\n const el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n const hovered = signal(false);\n\n const onPointerEnter = (event: PointerEvent): void => {\n if (!isNonTouchPointer(event)) {\n return;\n }\n if (disabled?.()) {\n return;\n }\n hovered.set(true);\n };\n const onPointerLeave = (): void => {\n hovered.set(false);\n };\n\n el.addEventListener('pointerenter', onPointerEnter);\n el.addEventListener('pointerleave', onPointerLeave);\n\n inject(DestroyRef).onDestroy(() => {\n el.removeEventListener('pointerenter', onPointerEnter);\n el.removeEventListener('pointerleave', onPointerLeave);\n });\n\n return computed(() => (disabled?.() ? false : hovered()));\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\n\n/** Options for {@link injectPressed}. */\nexport interface PressedOptions {\n /**\n * When this signal reports `true`, the returned signal is forced to `false`\n * and no interaction can set it — a disabled control is never pressed.\n */\n disabled?: Signal<boolean>;\n}\n\n/**\n * Returns a `Signal<boolean>` reflecting whether the host element is currently\n * being pressed — held down via the primary pointer button, or via the\n * <kbd>Enter</kbd> / <kbd>Space</kbd> key while focused. The state clears on\n * pointer release, the pointer leaving the element mid-press, key release, or\n * `blur` (covers focus leaving the element while a key is held).\n *\n * Attaches `pointerdown` / `pointerup` / `pointerleave` / `keydown` / `keyup` /\n * `blur` listeners to the host element. The `disabled` option short-circuits\n * the result reactively (a pure `computed`, never an `effect`-written signal)\n * and also stops a fresh press from arming while disabled.\n *\n * SSR-safe: on the server no listener is attached and the signal stays\n * `false`. Must be called from an injection context (injects `ElementRef`,\n * `PLATFORM_ID`, `DestroyRef`).\n *\n * Internal — not re-exported from `public-api.ts`.\n */\nexport function injectPressed(opts?: PressedOptions): Signal<boolean> {\n const disabled = opts?.disabled;\n const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n if (!isBrowser) {\n return signal(false).asReadonly();\n }\n\n const el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n const pressed = signal(false);\n\n const onPointerDown = (event: PointerEvent): void => {\n if (event.pointerType === 'mouse' && event.button !== 0) {\n return;\n }\n if (disabled?.()) {\n return;\n }\n pressed.set(true);\n };\n const onPointerEnd = (): void => {\n pressed.set(false);\n };\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Enter' && event.key !== ' ') {\n return;\n }\n if (disabled?.()) {\n return;\n }\n pressed.set(true);\n };\n const onKeyUp = (event: KeyboardEvent): void => {\n if (event.key !== 'Enter' && event.key !== ' ') {\n return;\n }\n pressed.set(false);\n };\n const onBlur = (): void => {\n pressed.set(false);\n };\n\n el.addEventListener('pointerdown', onPointerDown);\n el.addEventListener('pointerup', onPointerEnd);\n el.addEventListener('pointerleave', onPointerEnd);\n el.addEventListener('keydown', onKeyDown);\n el.addEventListener('keyup', onKeyUp);\n el.addEventListener('blur', onBlur);\n\n inject(DestroyRef).onDestroy(() => {\n el.removeEventListener('pointerdown', onPointerDown);\n el.removeEventListener('pointerup', onPointerEnd);\n el.removeEventListener('pointerleave', onPointerEnd);\n el.removeEventListener('keydown', onKeyDown);\n el.removeEventListener('keyup', onKeyUp);\n el.removeEventListener('blur', onBlur);\n });\n\n return computed(() => (disabled?.() ? false : pressed()));\n}\n","import {\n Directive,\n ElementRef,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { FOR_FIELDSET_CONTEXT, injectSyntheticActivation } from 'forty-cdk/core';\n\nimport { injectFocusVisible } from './focus-visible';\nimport { injectHovered } from './hovered';\nimport { injectPressed } from './pressed';\n\n/**\n * Headless implementation of the [WAI-ARIA Button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/).\n *\n * Works on a native `<button>` host and on any arbitrary host element (e.g. `<div>`, `<span>`).\n * On a native button the platform handles Enter/Space → click synthesis and no `role` or\n * `tabindex` is emitted (the platform owns those). On a non-button host, `role=\"button\"` and\n * `tabindex=\"0\"` are applied, and the directive synthesizes a click through its single `onClick`\n * path: Enter activates on `keydown`, while Space activates on `keyup` (its `keydown` always\n * calls `preventDefault()` to stop the page scrolling, even when disabled) — matching native\n * button and APG behavior.\n *\n * Disabled stays focusable: per the APG a disabled button must remain reachable by assistive\n * technology, so the native `disabled` attribute is never set. Instead `aria-disabled=\"true\"` and\n * `data-disabled=\"\"` are reflected and the activation handler becomes a no-op. `disabled` composes\n * with a surrounding `[forFieldset]`: a disabled group disables the button too (`aria-disabled` +\n * `data-disabled`, activation suppressed), which is what makes a non-native host (`<div forButton>`)\n * behave like a native button inside a native `<fieldset disabled>`.\n *\n * Interaction state is reflected as `data-pressed`, `data-hovered`, and `data-focus-visible`\n * (present/absent boolean attributes). There is no `data-state` — this primitive has no\n * open/closed or checked/unchecked logical state.\n *\n * @example\n * ```html\n * <!-- Native button — platform handles Enter/Space -->\n * <button forButton (activate)=\"doSomething()\">Click me</button>\n *\n * <!-- Non-button host — role, tabindex, and keyboard handling added automatically -->\n * <div forButton (activate)=\"doSomething()\">Click me</div>\n *\n * <!-- Disabled: stays focusable, activation is a no-op -->\n * <button forButton [disabled]=\"true\" (activate)=\"doSomething()\">Disabled</button>\n * ```\n */\n@Directive({\n selector: '[forButton]',\n exportAs: 'forButton',\n host: {\n '[attr.type]': 'resolvedType()',\n '[attr.role]': 'resolvedRole()',\n '[attr.tabindex]': 'resolvedTabindex()',\n '[attr.aria-disabled]': \"effectiveDisabled() ? 'true' : null\",\n '[attr.data-disabled]': \"effectiveDisabled() ? '' : null\",\n '[attr.data-pressed]': \"pressed() ? '' : null\",\n '[attr.data-hovered]': \"hovered() ? '' : null\",\n '[attr.data-focus-visible]': \"focusVisible() ? '' : null\",\n '(click)': 'onClick($event)',\n '(keydown)': 'onKeydown($event)',\n '(keyup)': 'onKeyup($event)',\n '(focusin)': 'onFocusIn()',\n '(focusout)': 'onFocusOut()',\n },\n})\nexport class ForButton {\n readonly #initialType =\n inject<ElementRef<HTMLElement>>(ElementRef).nativeElement.getAttribute('type');\n readonly #fieldset = inject(FOR_FIELDSET_CONTEXT, { optional: true });\n\n /**\n * When `true`, activation (click, Enter, Space) is suppressed and the element\n * reflects `aria-disabled=\"true\"` + `data-disabled=\"\"`. The element remains\n * focusable so assistive technology can announce it. Read\n * {@link effectiveDisabled} for the value that actually gates behavior — it\n * also folds in a surrounding disabled `[forFieldset]`.\n */\n readonly disabled = input(false, { transform: booleanAttribute });\n\n /**\n * The button's own {@link disabled} OR'd with a surrounding disabled\n * `[forFieldset]`. This is what gates activation and drives `aria-disabled` /\n * `data-disabled`: a native `<fieldset disabled>` never reaches a non-native\n * host (`<div forButton>`), so the group's disabled state has to compose in\n * here. Mirrors `FormUiControlBase.effectiveDisabled`, so a `[forButton]` and\n * a `[forSwitch]` inside the same disabled group behave identically.\n */\n readonly effectiveDisabled = computed(\n () => this.disabled() || (this.#fieldset?.disabled() ?? false),\n );\n\n /**\n * Emitted once per user activation: a pointer click on any host, or Enter / Space\n * on a non-native-button host (native buttons synthesize click from keyboard). The\n * name `activate` is chosen deliberately to avoid colliding with the native `click`\n * event (which would trip `@angular-eslint/no-output-native` on wrapping consumers).\n */\n readonly activate = output<void>();\n\n readonly #focused = signal(false);\n readonly #activation = injectSyntheticActivation({ disabled: this.effectiveDisabled });\n readonly #keyboardModality = injectFocusVisible();\n protected readonly hovered = injectHovered({ disabled: this.effectiveDisabled });\n protected readonly pressed = injectPressed({ disabled: this.effectiveDisabled });\n protected readonly focusVisible = computed(() => this.#focused() && this.#keyboardModality());\n\n protected readonly resolvedType = computed(\n () => this.#initialType ?? (this.#activation.nativeButton ? 'button' : null),\n );\n protected readonly resolvedRole = computed(() =>\n this.#activation.nativeButton ? null : 'button',\n );\n protected readonly resolvedTabindex = this.#activation.tabindex;\n\n protected onClick(event: MouseEvent): void {\n if (this.effectiveDisabled()) {\n event.preventDefault();\n event.stopImmediatePropagation();\n return;\n }\n this.activate.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n this.#activation.keydown(event);\n }\n\n protected onKeyup(event: KeyboardEvent): void {\n this.#activation.keyup(event);\n }\n\n protected onFocusIn(): void {\n this.#focused.set(true);\n }\n\n protected onFocusOut(): void {\n this.#focused.set(false);\n this.#activation.reset();\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-button.mjs","sources":["../../../projects/forty-cdk/button/src/modality.ts","../../../projects/forty-cdk/button/src/focus-visible.ts","../../../projects/forty-cdk/button/src/hovered.ts","../../../projects/forty-cdk/button/src/pressed.ts","../../../projects/forty-cdk/button/src/button.ts","../../../projects/forty-cdk/button/src/forty-cdk-button.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n DOCUMENT,\n DestroyRef,\n Injectable,\n PLATFORM_ID,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\n\n/**\n * Application-scoped tracker for the last input modality the user employed —\n * the in-house implementation of input-modality (pointer vs keyboard)\n * detection. Created once per Angular bootstrap (one per SSR\n * request), tied to the root injector lifetime.\n *\n * It installs exactly **one** capture-phase `keydown` and **one** capture-phase\n * `pointerdown` listener on `document`, regardless of how many primitives read\n * its state — every `injectFocusVisible()` consumer shares this single\n * singleton, so the document is never listened to more than once. Capture phase\n * is used so the modality is settled before any overlay content can stop\n * propagation.\n *\n * Why a service rather than module-level state:\n *\n * - SSR isolation: module-level globals leak between simultaneous server\n * requests in the same Node process. A `providedIn: 'root'` service is\n * instantiated per application injector.\n * - Bootstrap-safety: `TestBed.resetTestingModule()`, micro-frontend reloads,\n * and anything else that destroys `ApplicationRef` must not leave stale\n * `document` listeners behind. The listeners are registered in the\n * constructor against a single `AbortController` and dropped by one\n * `abort()` from `DestroyRef`.\n * - SSR safety: `document` is inaccessible on the server. The service is a\n * no-op when `PLATFORM_ID` is not the browser; `keyboard` stays `false` and\n * no listener is installed.\n *\n * Internal — not re-exported from `public-api.ts`.\n */\n@Injectable({ providedIn: 'root' })\nexport class InputModality {\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n readonly #keyboard = signal(false);\n\n /**\n * `true` when the most recent input modality was the keyboard, `false` after\n * a pointer interaction (or before any interaction). Stays `false` on the\n * server. A `keydown` carrying a `Meta` / `Control` / `Alt` modifier is\n * treated as a shortcut rather than keyboard navigation and does **not** flip\n * this to `true` (matching the platform `:focus-visible` heuristic); `Shift`\n * alone still counts, since `Shift`+`Tab` is legitimate keyboard navigation.\n */\n readonly keyboard: Signal<boolean> = this.#keyboard.asReadonly();\n\n readonly #onKeyDown = (event: KeyboardEvent): void => {\n if (event.metaKey || event.ctrlKey || event.altKey) {\n return;\n }\n this.#keyboard.set(true);\n };\n readonly #onPointerDown = (): void => {\n this.#keyboard.set(false);\n };\n\n constructor() {\n if (!this.#isBrowser) {\n return;\n }\n const controller = new AbortController();\n const options = { capture: true, signal: controller.signal };\n this.#document.addEventListener('keydown', this.#onKeyDown, options);\n this.#document.addEventListener('pointerdown', this.#onPointerDown, options);\n\n inject(DestroyRef).onDestroy(() => controller.abort());\n }\n}\n","import { inject, type Signal } from '@angular/core';\n\nimport { InputModality } from './modality';\n\n/**\n * Returns a `Signal<boolean>` that is `true` while the last global input\n * modality was the keyboard, and `false` after a pointer interaction (or\n * before any interaction). This is the modality half of `:focus-visible`\n * styling: a consumer that wants a focus ring only on keyboard focus combines\n * this with the element's own focused state (`:focus`, or a `focusin` /\n * `focusout` listener) — e.g. `host: { '[attr.data-focus-visible]':\n * \"focused() && focusVisible() ? '' : null\" }`.\n *\n * Backed by the application-scoped {@link InputModality} singleton, so every\n * consumer shares a single capture-phase `keydown` / `pointerdown` listener on\n * `document` no matter how many call this.\n *\n * SSR-safe: on the server the backing service installs no listener and the\n * signal stays `false`. Must be called from an injection context.\n *\n * Internal — not re-exported from `public-api.ts`.\n */\nexport function injectFocusVisible(): Signal<boolean> {\n return inject(InputModality).keyboard;\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { isNonTouchPointer } from 'forty-cdk/core';\n\n/** Options for {@link injectHovered}. */\nexport interface HoveredOptions {\n /**\n * When this signal reports `true`, the returned signal is forced to `false`\n * and no interaction can set it — a disabled control is never hovered.\n */\n disabled?: Signal<boolean>;\n}\n\n/**\n * Returns a `Signal<boolean>` reflecting whether a pointing device is currently\n * hovering the host element. State is set on `pointerenter` and cleared on\n * `pointerleave`.\n *\n * Touch is suppressed through the shared `isNonTouchPointer` predicate: a\n * `pointerenter` whose `pointerType` is `'touch'` does not set the hovered\n * state, so the emulated mouse-enter a tap produces on a touchscreen never\n * leaves the element stuck in a hovered state after the finger lifts. Only\n * `'mouse'` / `'pen'` pointers report hover — this is the pen-inclusive hover\n * vocabulary, not the menu family's mouse-only `isHoverCapablePointer`.\n *\n * Attaches `pointerenter` / `pointerleave` listeners to the host element. The\n * `disabled` option short-circuits the result reactively (a pure `computed`,\n * never an `effect`-written signal) and also stops hover from arming while\n * disabled.\n *\n * SSR-safe: on the server no listener is attached and the signal stays\n * `false`. Must be called from an injection context (injects `ElementRef`,\n * `PLATFORM_ID`, `DestroyRef`).\n *\n * Internal — not re-exported from `public-api.ts`.\n */\nexport function injectHovered(opts?: HoveredOptions): Signal<boolean> {\n const disabled = opts?.disabled;\n const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n if (!isBrowser) {\n return signal(false).asReadonly();\n }\n\n const el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n const hovered = signal(false);\n\n const onPointerEnter = (event: PointerEvent): void => {\n if (!isNonTouchPointer(event)) {\n return;\n }\n if (disabled?.()) {\n return;\n }\n hovered.set(true);\n };\n const onPointerLeave = (): void => {\n hovered.set(false);\n };\n\n const controller = new AbortController();\n const options = { signal: controller.signal };\n\n el.addEventListener('pointerenter', onPointerEnter, options);\n el.addEventListener('pointerleave', onPointerLeave, options);\n\n inject(DestroyRef).onDestroy(() => controller.abort());\n\n return computed(() => (disabled?.() ? false : hovered()));\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\n\n/** Options for {@link injectPressed}. */\nexport interface PressedOptions {\n /**\n * When this signal reports `true`, the returned signal is forced to `false`\n * and no interaction can set it — a disabled control is never pressed.\n */\n disabled?: Signal<boolean>;\n}\n\n/**\n * Returns a `Signal<boolean>` reflecting whether the host element is currently\n * being pressed — held down via the primary pointer button, or via the\n * <kbd>Enter</kbd> / <kbd>Space</kbd> key while focused. The state clears on\n * pointer release, the pointer leaving the element mid-press, key release, or\n * `blur` (covers focus leaving the element while a key is held).\n *\n * Attaches `pointerdown` / `pointerup` / `pointerleave` / `keydown` / `keyup` /\n * `blur` listeners to the host element. The `disabled` option short-circuits\n * the result reactively (a pure `computed`, never an `effect`-written signal)\n * and also stops a fresh press from arming while disabled.\n *\n * SSR-safe: on the server no listener is attached and the signal stays\n * `false`. Must be called from an injection context (injects `ElementRef`,\n * `PLATFORM_ID`, `DestroyRef`).\n *\n * Internal — not re-exported from `public-api.ts`.\n */\nexport function injectPressed(opts?: PressedOptions): Signal<boolean> {\n const disabled = opts?.disabled;\n const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n if (!isBrowser) {\n return signal(false).asReadonly();\n }\n\n const el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n const pressed = signal(false);\n\n const onPointerDown = (event: PointerEvent): void => {\n if (event.pointerType === 'mouse' && event.button !== 0) {\n return;\n }\n if (disabled?.()) {\n return;\n }\n pressed.set(true);\n };\n const onPointerEnd = (): void => {\n pressed.set(false);\n };\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Enter' && event.key !== ' ') {\n return;\n }\n if (disabled?.()) {\n return;\n }\n pressed.set(true);\n };\n const onKeyUp = (event: KeyboardEvent): void => {\n if (event.key !== 'Enter' && event.key !== ' ') {\n return;\n }\n pressed.set(false);\n };\n const onBlur = (): void => {\n pressed.set(false);\n };\n\n const controller = new AbortController();\n const options = { signal: controller.signal };\n\n el.addEventListener('pointerdown', onPointerDown, options);\n el.addEventListener('pointerup', onPointerEnd, options);\n el.addEventListener('pointerleave', onPointerEnd, options);\n el.addEventListener('keydown', onKeyDown, options);\n el.addEventListener('keyup', onKeyUp, options);\n el.addEventListener('blur', onBlur, options);\n\n inject(DestroyRef).onDestroy(() => controller.abort());\n\n return computed(() => (disabled?.() ? false : pressed()));\n}\n","import {\n Directive,\n ElementRef,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { FOR_FIELDSET_CONTEXT, injectSyntheticActivation } from 'forty-cdk/core';\n\nimport { injectFocusVisible } from './focus-visible';\nimport { injectHovered } from './hovered';\nimport { injectPressed } from './pressed';\n\n/**\n * Headless implementation of the [WAI-ARIA Button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/).\n *\n * Works on a native `<button>` host and on any arbitrary host element (e.g. `<div>`, `<span>`).\n * On a native button the platform handles Enter/Space → click synthesis and no `role` or\n * `tabindex` is emitted (the platform owns those). On a non-button host, `role=\"button\"` and\n * `tabindex=\"0\"` are applied, and the directive synthesizes a click through its single `onClick`\n * path: Enter activates on `keydown`, while Space activates on `keyup` (its `keydown` always\n * calls `preventDefault()` to stop the page scrolling, even when disabled) — matching native\n * button and APG behavior.\n *\n * Disabled stays focusable: per the APG a disabled button must remain reachable by assistive\n * technology, so the native `disabled` attribute is never set. Instead `aria-disabled=\"true\"` and\n * `data-disabled=\"\"` are reflected and the activation handler becomes a no-op. `disabled` composes\n * with a surrounding `[forFieldset]`: a disabled group disables the button too (`aria-disabled` +\n * `data-disabled`, activation suppressed), which is what makes a non-native host (`<div forButton>`)\n * behave like a native button inside a native `<fieldset disabled>`.\n *\n * Interaction state is reflected as `data-pressed`, `data-hovered`, and `data-focus-visible`\n * (present/absent boolean attributes). There is no `data-state` — this primitive has no\n * open/closed or checked/unchecked logical state.\n *\n * @example\n * ```html\n * <!-- Native button — platform handles Enter/Space -->\n * <button forButton (activate)=\"doSomething()\">Click me</button>\n *\n * <!-- Non-button host — role, tabindex, and keyboard handling added automatically -->\n * <div forButton (activate)=\"doSomething()\">Click me</div>\n *\n * <!-- Disabled: stays focusable, activation is a no-op -->\n * <button forButton [disabled]=\"true\" (activate)=\"doSomething()\">Disabled</button>\n * ```\n */\n@Directive({\n selector: '[forButton]',\n exportAs: 'forButton',\n host: {\n '[attr.type]': 'resolvedType()',\n '[attr.role]': 'resolvedRole()',\n '[attr.tabindex]': 'resolvedTabindex()',\n '[attr.aria-disabled]': \"effectiveDisabled() ? 'true' : null\",\n '[attr.data-disabled]': \"effectiveDisabled() ? '' : null\",\n '[attr.data-pressed]': \"pressed() ? '' : null\",\n '[attr.data-hovered]': \"hovered() ? '' : null\",\n '[attr.data-focus-visible]': \"focusVisible() ? '' : null\",\n '(click)': 'onClick($event)',\n '(keydown)': 'onKeydown($event)',\n '(keyup)': 'onKeyup($event)',\n '(focusin)': 'onFocusIn()',\n '(focusout)': 'onFocusOut()',\n },\n})\nexport class ForButton {\n readonly #initialType =\n inject<ElementRef<HTMLElement>>(ElementRef).nativeElement.getAttribute('type');\n readonly #fieldset = inject(FOR_FIELDSET_CONTEXT, { optional: true });\n\n /**\n * When `true`, activation (click, Enter, Space) is suppressed and the element\n * reflects `aria-disabled=\"true\"` + `data-disabled=\"\"`. The element remains\n * focusable so assistive technology can announce it. Read\n * {@link effectiveDisabled} for the value that actually gates behavior — it\n * also folds in a surrounding disabled `[forFieldset]`.\n */\n readonly disabled = input(false, { transform: booleanAttribute });\n\n /**\n * The button's own {@link disabled} OR'd with a surrounding disabled\n * `[forFieldset]`. This is what gates activation and drives `aria-disabled` /\n * `data-disabled`: a native `<fieldset disabled>` never reaches a non-native\n * host (`<div forButton>`), so the group's disabled state has to compose in\n * here. Mirrors `FormUiControlBase.effectiveDisabled`, so a `[forButton]` and\n * a `[forSwitch]` inside the same disabled group behave identically.\n */\n readonly effectiveDisabled = computed(\n () => this.disabled() || (this.#fieldset?.disabled() ?? false),\n );\n\n /**\n * Emitted once per user activation: a pointer click on any host, or Enter / Space\n * on a non-native-button host (native buttons synthesize click from keyboard). The\n * name `activate` is chosen deliberately to avoid colliding with the native `click`\n * event (which would trip `@angular-eslint/no-output-native` on wrapping consumers).\n */\n readonly activate = output<void>();\n\n readonly #focused = signal(false);\n readonly #activation = injectSyntheticActivation({ disabled: this.effectiveDisabled });\n readonly #keyboardModality = injectFocusVisible();\n protected readonly hovered = injectHovered({ disabled: this.effectiveDisabled });\n protected readonly pressed = injectPressed({ disabled: this.effectiveDisabled });\n protected readonly focusVisible = computed(() => this.#focused() && this.#keyboardModality());\n\n protected readonly resolvedType = computed(\n () => this.#initialType ?? (this.#activation.nativeButton ? 'button' : null),\n );\n protected readonly resolvedRole = computed(() =>\n this.#activation.nativeButton ? null : 'button',\n );\n protected readonly resolvedTabindex = this.#activation.tabindex;\n\n protected onClick(event: MouseEvent): void {\n if (this.effectiveDisabled()) {\n event.preventDefault();\n event.stopImmediatePropagation();\n return;\n }\n this.activate.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n this.#activation.keydown(event);\n }\n\n protected onKeyup(event: KeyboardEvent): void {\n this.#activation.keyup(event);\n }\n\n protected onFocusIn(): void {\n this.#focused.set(true);\n }\n\n protected onFocusOut(): void {\n this.#focused.set(false);\n this.#activation.reset();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}