forty-cdk 0.2.0 → 0.3.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 (220) hide show
  1. package/accordion/README.md +122 -0
  2. package/aspect-ratio/README.md +76 -0
  3. package/avatar/README.md +100 -0
  4. package/breadcrumbs/README.md +49 -0
  5. package/breakpoints/README.md +81 -0
  6. package/button/README.md +49 -0
  7. package/calendar/README.md +458 -0
  8. package/carousel/README.md +358 -0
  9. package/checkbox/README.md +146 -0
  10. package/combobox/README.md +535 -0
  11. package/context-menu/README.md +139 -0
  12. package/date-field/README.md +184 -0
  13. package/date-picker/README.md +338 -0
  14. package/dialog/README.md +388 -0
  15. package/disclosure/README.md +114 -0
  16. package/drag-drop/README.md +359 -0
  17. package/drawer/README.md +560 -0
  18. package/dropdown-menu/README.md +176 -0
  19. package/fesm2022/forty-cdk-accordion.mjs +348 -0
  20. package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
  21. package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
  22. package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
  23. package/fesm2022/forty-cdk-avatar.mjs +308 -0
  24. package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
  25. package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
  26. package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
  27. package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
  28. package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
  29. package/fesm2022/forty-cdk-button.mjs +134 -0
  30. package/fesm2022/forty-cdk-button.mjs.map +1 -0
  31. package/fesm2022/forty-cdk-calendar.mjs +2034 -0
  32. package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
  33. package/fesm2022/forty-cdk-carousel.mjs +968 -0
  34. package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
  35. package/fesm2022/forty-cdk-checkbox.mjs +226 -0
  36. package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
  37. package/fesm2022/forty-cdk-combobox.mjs +2596 -0
  38. package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
  39. package/fesm2022/forty-cdk-context-menu.mjs +413 -0
  40. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
  41. package/fesm2022/forty-cdk-core.mjs +9022 -0
  42. package/fesm2022/forty-cdk-core.mjs.map +1 -0
  43. package/fesm2022/forty-cdk-date-field.mjs +744 -0
  44. package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
  45. package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
  46. package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
  47. package/fesm2022/forty-cdk-dialog.mjs +707 -0
  48. package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
  49. package/fesm2022/forty-cdk-disclosure.mjs +190 -0
  50. package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
  51. package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
  52. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
  53. package/fesm2022/forty-cdk-drawer.mjs +1641 -0
  54. package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
  55. package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
  56. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
  57. package/fesm2022/forty-cdk-field.mjs +425 -0
  58. package/fesm2022/forty-cdk-field.mjs.map +1 -0
  59. package/fesm2022/forty-cdk-fieldset.mjs +164 -0
  60. package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
  61. package/fesm2022/forty-cdk-file-upload.mjs +221 -0
  62. package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
  63. package/fesm2022/forty-cdk-hover-card.mjs +496 -0
  64. package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
  65. package/fesm2022/forty-cdk-input.mjs +274 -0
  66. package/fesm2022/forty-cdk-input.mjs.map +1 -0
  67. package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
  68. package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
  69. package/fesm2022/forty-cdk-listbox.mjs +1279 -0
  70. package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
  71. package/fesm2022/forty-cdk-menu.mjs +1439 -0
  72. package/fesm2022/forty-cdk-menu.mjs.map +1 -0
  73. package/fesm2022/forty-cdk-menubar.mjs +787 -0
  74. package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
  75. package/fesm2022/forty-cdk-meter.mjs +211 -0
  76. package/fesm2022/forty-cdk-meter.mjs.map +1 -0
  77. package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
  78. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
  79. package/fesm2022/forty-cdk-number-input.mjs +559 -0
  80. package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
  81. package/fesm2022/forty-cdk-otp-input.mjs +527 -0
  82. package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
  83. package/fesm2022/forty-cdk-pagination.mjs +323 -0
  84. package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
  85. package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
  86. package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
  87. package/fesm2022/forty-cdk-popover.mjs +698 -0
  88. package/fesm2022/forty-cdk-popover.mjs.map +1 -0
  89. package/fesm2022/forty-cdk-progress.mjs +226 -0
  90. package/fesm2022/forty-cdk-progress.mjs.map +1 -0
  91. package/fesm2022/forty-cdk-radio-group.mjs +378 -0
  92. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
  93. package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
  94. package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
  95. package/fesm2022/forty-cdk-search.mjs +205 -0
  96. package/fesm2022/forty-cdk-search.mjs.map +1 -0
  97. package/fesm2022/forty-cdk-select.mjs +1661 -0
  98. package/fesm2022/forty-cdk-select.mjs.map +1 -0
  99. package/fesm2022/forty-cdk-separator.mjs +82 -0
  100. package/fesm2022/forty-cdk-separator.mjs.map +1 -0
  101. package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
  102. package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
  103. package/fesm2022/forty-cdk-slider.mjs +803 -0
  104. package/fesm2022/forty-cdk-slider.mjs.map +1 -0
  105. package/fesm2022/forty-cdk-stepper.mjs +886 -0
  106. package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
  107. package/fesm2022/forty-cdk-switch.mjs +137 -0
  108. package/fesm2022/forty-cdk-switch.mjs.map +1 -0
  109. package/fesm2022/forty-cdk-table.mjs +1518 -0
  110. package/fesm2022/forty-cdk-table.mjs.map +1 -0
  111. package/fesm2022/forty-cdk-tabs.mjs +400 -0
  112. package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
  113. package/fesm2022/forty-cdk-time-field.mjs +593 -0
  114. package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
  115. package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
  116. package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
  117. package/fesm2022/forty-cdk-toast.mjs +1153 -0
  118. package/fesm2022/forty-cdk-toast.mjs.map +1 -0
  119. package/fesm2022/forty-cdk-toggle.mjs +516 -0
  120. package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
  121. package/fesm2022/forty-cdk-toolbar.mjs +374 -0
  122. package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
  123. package/fesm2022/forty-cdk-tooltip.mjs +672 -0
  124. package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
  125. package/fesm2022/forty-cdk-tree.mjs +2007 -0
  126. package/fesm2022/forty-cdk-tree.mjs.map +1 -0
  127. package/fesm2022/forty-cdk-virtualization.mjs +1 -1
  128. package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
  129. package/fesm2022/forty-cdk.mjs +0 -43310
  130. package/fesm2022/forty-cdk.mjs.map +1 -1
  131. package/field/README.md +97 -0
  132. package/fieldset/README.md +86 -0
  133. package/file-upload/README.md +73 -0
  134. package/hover-card/README.md +171 -0
  135. package/input/README.md +156 -0
  136. package/listbox/README.md +424 -0
  137. package/menu/README.md +181 -0
  138. package/menubar/README.md +140 -0
  139. package/meter/README.md +128 -0
  140. package/navigation-menu/README.md +253 -0
  141. package/number-input/README.md +171 -0
  142. package/otp-input/README.md +198 -0
  143. package/package.json +213 -1
  144. package/pagination/README.md +61 -0
  145. package/pane-resizer/README.md +136 -0
  146. package/popover/README.md +262 -0
  147. package/progress/README.md +115 -0
  148. package/radio-group/README.md +129 -0
  149. package/scroll-area/README.md +184 -0
  150. package/search/README.md +42 -0
  151. package/select/README.md +488 -0
  152. package/separator/README.md +84 -0
  153. package/signal-forms/README.md +72 -0
  154. package/slider/README.md +152 -0
  155. package/stepper/README.md +292 -0
  156. package/switch/README.md +116 -0
  157. package/table/README.md +769 -0
  158. package/tabs/README.md +130 -0
  159. package/time-field/README.md +157 -0
  160. package/time-picker/README.md +172 -0
  161. package/toast/README.md +398 -0
  162. package/toggle/README.md +224 -0
  163. package/toolbar/README.md +109 -0
  164. package/tooltip/README.md +274 -0
  165. package/tree/README.md +708 -0
  166. package/types/forty-cdk-accordion.d.ts +242 -0
  167. package/types/forty-cdk-aspect-ratio.d.ts +59 -0
  168. package/types/forty-cdk-avatar.d.ts +133 -0
  169. package/types/forty-cdk-breadcrumbs.d.ts +92 -0
  170. package/types/forty-cdk-breakpoints.d.ts +141 -0
  171. package/types/forty-cdk-button.d.ts +80 -0
  172. package/types/forty-cdk-calendar.d.ts +914 -0
  173. package/types/forty-cdk-carousel.d.ts +530 -0
  174. package/types/forty-cdk-checkbox.d.ts +141 -0
  175. package/types/forty-cdk-combobox.d.ts +1259 -0
  176. package/types/forty-cdk-context-menu.d.ts +313 -0
  177. package/types/forty-cdk-core.d.ts +5774 -0
  178. package/types/forty-cdk-date-field.d.ts +307 -0
  179. package/types/forty-cdk-date-picker.d.ts +622 -0
  180. package/types/forty-cdk-dialog.d.ts +546 -0
  181. package/types/forty-cdk-disclosure.d.ts +127 -0
  182. package/types/forty-cdk-drag-drop.d.ts +456 -0
  183. package/types/forty-cdk-drawer.d.ts +871 -0
  184. package/types/forty-cdk-dropdown-menu.d.ts +242 -0
  185. package/types/forty-cdk-field.d.ts +236 -0
  186. package/types/forty-cdk-fieldset.d.ts +119 -0
  187. package/types/forty-cdk-file-upload.d.ts +124 -0
  188. package/types/forty-cdk-hover-card.d.ts +320 -0
  189. package/types/forty-cdk-input.d.ts +169 -0
  190. package/types/forty-cdk-internationalized-date.d.ts +1 -1
  191. package/types/forty-cdk-listbox.d.ts +513 -0
  192. package/types/forty-cdk-menu.d.ts +629 -0
  193. package/types/forty-cdk-menubar.d.ts +451 -0
  194. package/types/forty-cdk-meter.d.ts +122 -0
  195. package/types/forty-cdk-navigation-menu.d.ts +514 -0
  196. package/types/forty-cdk-number-input.d.ts +319 -0
  197. package/types/forty-cdk-otp-input.d.ts +248 -0
  198. package/types/forty-cdk-pagination.d.ts +214 -0
  199. package/types/forty-cdk-pane-resizer.d.ts +145 -0
  200. package/types/forty-cdk-popover.d.ts +509 -0
  201. package/types/forty-cdk-progress.d.ts +143 -0
  202. package/types/forty-cdk-radio-group.d.ts +222 -0
  203. package/types/forty-cdk-scroll-area.d.ts +258 -0
  204. package/types/forty-cdk-search.d.ts +142 -0
  205. package/types/forty-cdk-select.d.ts +899 -0
  206. package/types/forty-cdk-separator.d.ts +59 -0
  207. package/types/forty-cdk-signal-forms.d.ts +58 -0
  208. package/types/forty-cdk-slider.d.ts +379 -0
  209. package/types/forty-cdk-stepper.d.ts +650 -0
  210. package/types/forty-cdk-switch.d.ts +87 -0
  211. package/types/forty-cdk-table.d.ts +723 -0
  212. package/types/forty-cdk-tabs.d.ts +235 -0
  213. package/types/forty-cdk-time-field.d.ts +307 -0
  214. package/types/forty-cdk-time-picker.d.ts +578 -0
  215. package/types/forty-cdk-toast.d.ts +598 -0
  216. package/types/forty-cdk-toggle.d.ts +310 -0
  217. package/types/forty-cdk-toolbar.d.ts +217 -0
  218. package/types/forty-cdk-tooltip.d.ts +436 -0
  219. package/types/forty-cdk-tree.d.ts +688 -0
  220. package/types/forty-cdk.d.ts +1 -19743
@@ -0,0 +1,698 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, computed, model, input, numberAttribute, booleanAttribute, output, signal, Directive, ElementRef, effect } from '@angular/core';
3
+ import { createDefaults, IdGenerator, ANCHORED_POSITIONING_DEFAULTS, injectPrefersReducedMotion, adoptHostId, emitVetoableNativeEvent, emitVetoableEvent, reflectDisabled, registerHandle, injectOverlayShell, toFloatingPositioner, registerA11yName, registerA11yDescription } from 'forty-cdk/core';
4
+
5
+ const FOR_POPOVER_CONTEXT = new InjectionToken('FOR_POPOVER_CONTEXT');
6
+ function injectPopoverContext(piece) {
7
+ const ctx = inject(FOR_POPOVER_CONTEXT, { optional: true });
8
+ if (!ctx) {
9
+ throw new Error(`[forty-cdk/popover] ${piece} must be used inside a [forPopover] element. ` +
10
+ "If it is declared inside an ng-template, DI resolves at the template's declaration site — " +
11
+ 'not where it is stamped (e.g. via ngTemplateOutlet) — so declare the template inside the ' +
12
+ '[forPopover] root.');
13
+ }
14
+ return ctx;
15
+ }
16
+ /**
17
+ * Resolves the trigger's root context: the explicit reference when the
18
+ * `[forPopoverTrigger]` input carries one, the injected `FOR_POPOVER_CONTEXT`
19
+ * otherwise. The orphan error only fires when neither resolves, on first read
20
+ * of the returned signal. Must be called in an injection context.
21
+ */
22
+ function injectPopoverTriggerContext(explicitRoot) {
23
+ const injected = inject(FOR_POPOVER_CONTEXT, { optional: true });
24
+ return computed(() => {
25
+ const explicit = explicitRoot();
26
+ if (explicit !== '') {
27
+ return explicit;
28
+ }
29
+ if (injected) {
30
+ return injected;
31
+ }
32
+ throw new Error('[forty-cdk/popover] ForPopoverTrigger could not resolve its [forPopover] root: ' +
33
+ 'no FOR_POPOVER_CONTEXT provider is visible and no explicit root reference was passed. ' +
34
+ "If this trigger is declared inside an ng-template, DI resolves at the template's declaration " +
35
+ 'site — not where it is stamped — so either declare the template inside the root or pass the ' +
36
+ 'root explicitly: [forPopoverTrigger]="root" with #root="forPopover".');
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Library fallback for popover defaults, read at the root injector when no
42
+ * consumer has called `provideForPopoverDefaults`. Exported for the shared
43
+ * defaults contract spec; not re-exported from the primitive's public entry.
44
+ */
45
+ const FOR_POPOVER_FALLBACK_DEFAULTS = {
46
+ side: 'bottom',
47
+ align: 'center',
48
+ sideOffset: 8,
49
+ collisionPadding: 8,
50
+ };
51
+ const { token, provideDefaults } = createDefaults('FOR_POPOVER_DEFAULTS', FOR_POPOVER_FALLBACK_DEFAULTS);
52
+ /** Token holding the resolved popover defaults for the current scope. */
53
+ const FOR_POPOVER_DEFAULTS = token;
54
+ /**
55
+ * Configures forty-cdk popover defaults for this injector scope. Partial
56
+ * overrides inherit unspecified keys from the parent scope (or library
57
+ * defaults at the root).
58
+ */
59
+ function provideForPopoverDefaults(defaults = {}) {
60
+ return provideDefaults(defaults);
61
+ }
62
+
63
+ /**
64
+ * Headless implementation of the [WAI-ARIA Modeless Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/),
65
+ * positioned against an internal trigger via `@floating-ui/dom`.
66
+ *
67
+ * Apply `[forPopover]` on a wrapper (the trigger and the content's `@if`
68
+ * live inside it). The directive owns open state, ids, and the registries
69
+ * that wire trigger / content / title / description / arrow together.
70
+ *
71
+ * Mount/unmount of the visible content is the consumer's responsibility —
72
+ * wrap `[forPopoverContent]` with `@if (open())` and let `animate.enter` /
73
+ * `animate.leave` handle transitions:
74
+ *
75
+ * ```html
76
+ * <div forPopover [(open)]="isOpen">
77
+ * <button forPopoverTrigger>Open</button>
78
+ * @if (isOpen()) {
79
+ * <div forPopoverContent animate.leave="fade-out">…</div>
80
+ * }
81
+ * </div>
82
+ * ```
83
+ *
84
+ * Non-modal: focus is sent into the popover on open and returned to the
85
+ * trigger on close, but Tab is allowed to leave the surface (use Dialog
86
+ * if you need a focus trap).
87
+ */
88
+ class ForPopover {
89
+ #idGen = inject(IdGenerator);
90
+ #defaults = inject(FOR_POPOVER_DEFAULTS);
91
+ /**
92
+ * Two-way bindable. Whether the popover is currently shown. The `model()`
93
+ * change emitter (`(openChange)`) fires only on internal transitions
94
+ * (trigger click, Escape, outside dismissal), never on consumer writes
95
+ * via `[(open)]` — observe state changes without binding back.
96
+ */
97
+ open = model(false, /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
99
+ /**
100
+ * Per-popover override for the side the popover is anchored to. Pair with
101
+ * `align` for the full positioning API (`side="bottom" align="start"`).
102
+ * When `undefined` (default), falls back to `ForPopoverDefaults.side` from
103
+ * the surrounding `provideForPopoverDefaults` scope (`'bottom'` unless
104
+ * configured).
105
+ *
106
+ * The input is aliased to `side`; consumers bind `[side]="..."` and read
107
+ * the effective value via the public `side` computed below.
108
+ */
109
+ _sideInput = input(undefined, { ...(ngDevMode ? { debugName: "_sideInput" } : /* istanbul ignore next */ {}), alias: 'side' });
110
+ /** Effective anchor side: the `side` input when set, else the scope default. */
111
+ side = computed(() => this._sideInput() ?? this.#defaults.side, /* @ts-ignore */
112
+ ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
113
+ /**
114
+ * Per-popover override for the alignment along the chosen `side`. When
115
+ * `undefined` (default), falls back to `ForPopoverDefaults.align` from the
116
+ * surrounding `provideForPopoverDefaults` scope (`'center'` unless
117
+ * configured).
118
+ *
119
+ * The input is aliased to `align`; consumers bind `[align]="..."` and read
120
+ * the effective value via the public `align` computed below.
121
+ */
122
+ _alignInput = input(undefined, { ...(ngDevMode ? { debugName: "_alignInput" } : /* istanbul ignore next */ {}), alias: 'align' });
123
+ /** Effective alignment: the `align` input when set, else the scope default. */
124
+ align = computed(() => this._alignInput() ?? this.#defaults.align, /* @ts-ignore */
125
+ ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
126
+ /**
127
+ * Per-popover override for the gap (px) between trigger and content along
128
+ * the *main* axis (perpendicular to `side`). Forwarded to floating-ui's
129
+ * `offset` middleware. When `undefined`
130
+ * (default), falls back to `ForPopoverDefaults.sideOffset` from the
131
+ * surrounding `provideForPopoverDefaults` scope (`8` unless configured).
132
+ *
133
+ * The input is aliased to `sideOffset`; consumers bind `[sideOffset]="..."`
134
+ * and read the effective value via the public `sideOffset` computed below.
135
+ */
136
+ _sideOffsetInput = input(undefined, { ...(ngDevMode ? { debugName: "_sideOffsetInput" } : /* istanbul ignore next */ {}), alias: 'sideOffset',
137
+ transform: (v) => (v == null ? undefined : numberAttribute(v)) });
138
+ /** Effective main-axis gap (px): the `sideOffset` input when set, else the scope default. */
139
+ sideOffset = computed(() => this._sideOffsetInput() ?? this.#defaults.sideOffset, /* @ts-ignore */
140
+ ...(ngDevMode ? [{ debugName: "sideOffset" }] : /* istanbul ignore next */ []));
141
+ /** Gap (px) along the cross axis (parallel to `side`). Default `0`. */
142
+ alignOffset = input(ANCHORED_POSITIONING_DEFAULTS.alignOffset, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
143
+ /**
144
+ * When `true` (default), `flip` and `shift` keep the popover inside the
145
+ * viewport. Disable for strict positioning where overflow is acceptable.
146
+ */
147
+ avoidCollisions = input(ANCHORED_POSITIONING_DEFAULTS.avoidCollisions, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
148
+ /**
149
+ * Per-popover override for the padding (px) applied uniformly to the
150
+ * `flip`, `shift`, and `size` middlewares. When `undefined` (default),
151
+ * falls back to
152
+ * `ForPopoverDefaults.collisionPadding` from the surrounding
153
+ * `provideForPopoverDefaults` scope (`8` unless configured).
154
+ *
155
+ * The input is aliased to `collisionPadding`; consumers bind
156
+ * `[collisionPadding]="..."` and read the effective value via the public
157
+ * `collisionPadding` computed below.
158
+ */
159
+ _collisionPaddingInput = input(undefined, { ...(ngDevMode ? { debugName: "_collisionPaddingInput" } : /* istanbul ignore next */ {}), alias: 'collisionPadding',
160
+ transform: (v) => (v == null ? undefined : numberAttribute(v)) });
161
+ /** Effective collision padding (px): the `collisionPadding` input when set, else the scope default. */
162
+ collisionPadding = computed(() => this._collisionPaddingInput() ?? this.#defaults.collisionPadding, /* @ts-ignore */
163
+ ...(ngDevMode ? [{ debugName: "collisionPadding" }] : /* istanbul ignore next */ []));
164
+ /**
165
+ * Padding (px) for the `arrow` middleware so the arrow stays inside any
166
+ * rounded corners on the popover content. Default `0`.
167
+ */
168
+ arrowPadding = input(ANCHORED_POSITIONING_DEFAULTS.arrowPadding, { ...(ngDevMode ? { debugName: "arrowPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
169
+ /**
170
+ * Stickiness behaviour for `shift`. `'partial'` (default) lets the
171
+ * popover shift to stay visible. `'always'` disables `shift` so the
172
+ * popover keeps its requested placement even off-screen. `false`
173
+ * is treated as `'partial'`.
174
+ */
175
+ sticky = input(ANCHORED_POSITIONING_DEFAULTS.sticky, /* @ts-ignore */
176
+ ...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
177
+ /**
178
+ * When `true`, sets `data-detached=""` on the content while the trigger
179
+ * has scrolled off all clipping ancestors. Use to fade out popovers
180
+ * tied to scrolled-away triggers.
181
+ */
182
+ hideWhenDetached = input(ANCHORED_POSITIONING_DEFAULTS.hideWhenDetached, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
183
+ /**
184
+ * When `true` (default), the content is clipped until floating-ui resolves
185
+ * its first position, preventing a flash at the viewport corner. Set to
186
+ * `false` so a dramatic `animate.enter` plays from its first frame (the
187
+ * surface may flash briefly at the unresolved position while positioning
188
+ * computes).
189
+ */
190
+ clipUntilPositioned = input(ANCHORED_POSITIONING_DEFAULTS.clipUntilPositioned, { ...(ngDevMode ? { debugName: "clipUntilPositioned" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
191
+ /**
192
+ * When true, trigger interaction is ignored and any open popover stays
193
+ * open until the consumer flips `open` themselves. Disable on the trigger
194
+ * side; the content side keeps its dismissable behavior unaffected.
195
+ */
196
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
197
+ /**
198
+ * When true (default), Escape, pointer-down outside, and focus outside
199
+ * close the popover. Disable for confirm flows that must be answered
200
+ * via `[forPopoverClose]` (or by setting `open` programmatically).
201
+ */
202
+ dismissible = input(true, { ...(ngDevMode ? { debugName: "dismissible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
203
+ /** When true (default), focus returns to the trigger on close. */
204
+ returnFocus = input(true, { ...(ngDevMode ? { debugName: "returnFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
205
+ /**
206
+ * Where to send focus when content mounts. `'first'` (default) finds
207
+ * the first focusable descendant; `'container'` focuses the content
208
+ * box itself (useful when there's nothing focusable inside).
209
+ */
210
+ initialFocus = input('first', /* @ts-ignore */
211
+ ...(ngDevMode ? [{ debugName: "initialFocus" }] : /* istanbul ignore next */ []));
212
+ /** Manual `aria-label` on the content. Use this when no visible title element exists. */
213
+ ariaLabel = input(null, /* @ts-ignore */
214
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
215
+ /**
216
+ * Whether the user has requested reduced motion via the OS
217
+ * `prefers-reduced-motion: reduce` media query. Reflected as the boolean
218
+ * `data-reduced-motion` attribute on the root and content so consumers can
219
+ * disable their own `animate.enter` / `animate.leave` and CSS transitions
220
+ * without re-deriving the media query. The popover toggles open / closed
221
+ * synchronously on trigger click, so it has no JS-coordinated timing to skip.
222
+ */
223
+ reducedMotion = injectPrefersReducedMotion();
224
+ /**
225
+ * Fires when the user presses Escape while this popover is the topmost
226
+ * dismissable layer. Call `preventDefault()` on the emitted veto to
227
+ * suppress the automatic close. The native `KeyboardEvent` is
228
+ * available on `.event`.
229
+ */
230
+ escapeKeyDown = output();
231
+ /**
232
+ * Fires when a pointer goes down outside the popover (and outside the
233
+ * trigger). Call `preventDefault()` on the veto to suppress the
234
+ * automatic close. The native `PointerEvent` is on `.event`.
235
+ */
236
+ pointerDownOutside = output();
237
+ /**
238
+ * Fires when focus moves outside the popover and trigger. Call
239
+ * `preventDefault()` on the veto to suppress the automatic close.
240
+ */
241
+ focusOutside = output();
242
+ /**
243
+ * Composite event: fires alongside `pointerDownOutside` and
244
+ * `focusOutside` and shares their veto state — `preventDefault()` on
245
+ * either one suppresses the automatic close.
246
+ */
247
+ interactOutside = output();
248
+ /**
249
+ * Fires just before the popover sends focus into itself on mount.
250
+ * Call `preventDefault()` on the veto to skip the imperative focus
251
+ * move — useful when opening a popover from an input you want to
252
+ * keep focused.
253
+ */
254
+ autoFocusOnOpen = output();
255
+ /**
256
+ * Fires just before focus returns to the trigger on unmount. Call
257
+ * `preventDefault()` on the veto to suppress the return-focus.
258
+ */
259
+ autoFocusOnClose = output();
260
+ triggerId = signal(this.#idGen.next('for-popover-trigger'), /* @ts-ignore */
261
+ ...(ngDevMode ? [{ debugName: "triggerId" }] : /* istanbul ignore next */ []));
262
+ contentId = signal(this.#idGen.next('for-popover-content'), /* @ts-ignore */
263
+ ...(ngDevMode ? [{ debugName: "contentId" }] : /* istanbul ignore next */ []));
264
+ #triggerEl = signal(null, /* @ts-ignore */
265
+ ...(ngDevMode ? [{ debugName: "#triggerEl" }] : /* istanbul ignore next */ []));
266
+ trigger = this.#triggerEl.asReadonly();
267
+ #anchorEl = signal(null, /* @ts-ignore */
268
+ ...(ngDevMode ? [{ debugName: "#anchorEl" }] : /* istanbul ignore next */ []));
269
+ anchor = this.#anchorEl.asReadonly();
270
+ /**
271
+ * The element floating-ui anchors against. Prefers `[forPopoverAnchor]`
272
+ * when registered, otherwise falls back to the trigger so existing
273
+ * popovers without an anchor keep their behavior.
274
+ */
275
+ reference = computed(() => this.#anchorEl() ?? this.#triggerEl(), /* @ts-ignore */
276
+ ...(ngDevMode ? [{ debugName: "reference" }] : /* istanbul ignore next */ []));
277
+ #arrowEl = signal(null, /* @ts-ignore */
278
+ ...(ngDevMode ? [{ debugName: "#arrowEl" }] : /* istanbul ignore next */ []));
279
+ arrow = this.#arrowEl.asReadonly();
280
+ #labelIds = signal([], /* @ts-ignore */
281
+ ...(ngDevMode ? [{ debugName: "#labelIds" }] : /* istanbul ignore next */ []));
282
+ #describedByIds = signal([], /* @ts-ignore */
283
+ ...(ngDevMode ? [{ debugName: "#describedByIds" }] : /* istanbul ignore next */ []));
284
+ labelledBy = computed(() => {
285
+ const ids = this.#labelIds();
286
+ return ids.length === 0 ? null : ids.join(' ');
287
+ }, /* @ts-ignore */
288
+ ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
289
+ describedBy = computed(() => {
290
+ const ids = this.#describedByIds();
291
+ return ids.length === 0 ? null : ids.join(' ');
292
+ }, /* @ts-ignore */
293
+ ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
294
+ registerTrigger(el) {
295
+ adoptHostId(el, this.triggerId);
296
+ this.#triggerEl.set(el);
297
+ }
298
+ unregisterTrigger(el) {
299
+ if (this.#triggerEl() === el) {
300
+ this.#triggerEl.set(null);
301
+ }
302
+ }
303
+ /**
304
+ * Adopts a consumer-set static `id` on the `[forPopoverContent]` host into
305
+ * `contentId` (preserving anchors / external `aria-labelledby` references)
306
+ * instead of letting the `[id]` host binding clobber it.
307
+ */
308
+ adoptContentId(el) {
309
+ adoptHostId(el, this.contentId);
310
+ }
311
+ registerAnchor(el) {
312
+ this.#anchorEl.set(el);
313
+ }
314
+ unregisterAnchor(el) {
315
+ if (this.#anchorEl() === el) {
316
+ this.#anchorEl.set(null);
317
+ }
318
+ }
319
+ registerArrow(el) {
320
+ this.#arrowEl.set(el);
321
+ }
322
+ unregisterArrow(el) {
323
+ if (this.#arrowEl() === el) {
324
+ this.#arrowEl.set(null);
325
+ }
326
+ }
327
+ registerLabel(id) {
328
+ this.#labelIds.update((arr) => (arr.includes(id) ? arr : [...arr, id]));
329
+ }
330
+ unregisterLabel(id) {
331
+ this.#labelIds.update((arr) => arr.filter((x) => x !== id));
332
+ }
333
+ registerDescription(id) {
334
+ this.#describedByIds.update((arr) => (arr.includes(id) ? arr : [...arr, id]));
335
+ }
336
+ unregisterDescription(id) {
337
+ this.#describedByIds.update((arr) => arr.filter((x) => x !== id));
338
+ }
339
+ toggle() {
340
+ if (this.disabled()) {
341
+ return;
342
+ }
343
+ this.open.update((v) => !v);
344
+ }
345
+ emitEscapeKeyDown(event) {
346
+ const vetoed = emitVetoableNativeEvent(this.escapeKeyDown, event);
347
+ if (!vetoed && this.dismissible()) {
348
+ event.stopPropagation();
349
+ this.open.set(false);
350
+ }
351
+ }
352
+ /**
353
+ * Outside-interaction emit forwarders. The shared `#pendingOutsideVeto`
354
+ * reuse between the specific outside channels and the composite
355
+ * `interactOutside` lives in `injectOverlayShell`; these only fire the
356
+ * matching output with the veto the shell built.
357
+ */
358
+ emitPointerDownOutside(veto) {
359
+ this.pointerDownOutside.emit(veto);
360
+ }
361
+ emitFocusOutside(veto) {
362
+ this.focusOutside.emit(veto);
363
+ }
364
+ emitInteractOutside(veto) {
365
+ this.interactOutside.emit(veto);
366
+ }
367
+ /**
368
+ * Implicit close requested by the shell after an un-vetoed outside
369
+ * interaction. The directive only owns the close itself.
370
+ */
371
+ requestClose() {
372
+ this.open.set(false);
373
+ }
374
+ emitAutoFocusOnOpen() {
375
+ return emitVetoableEvent(this.autoFocusOnOpen);
376
+ }
377
+ emitAutoFocusOnClose() {
378
+ return emitVetoableEvent(this.autoFocusOnClose);
379
+ }
380
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopover, deps: [], target: i0.ɵɵFactoryTarget.Directive });
381
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForPopover, isStandalone: true, selector: "[forPopover]", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, _sideInput: { classPropertyName: "_sideInput", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, _alignInput: { classPropertyName: "_alignInput", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, _sideOffsetInput: { classPropertyName: "_sideOffsetInput", publicName: "sideOffset", isSignal: true, isRequired: false, transformFunction: null }, alignOffset: { classPropertyName: "alignOffset", publicName: "alignOffset", isSignal: true, isRequired: false, transformFunction: null }, avoidCollisions: { classPropertyName: "avoidCollisions", publicName: "avoidCollisions", isSignal: true, isRequired: false, transformFunction: null }, _collisionPaddingInput: { classPropertyName: "_collisionPaddingInput", publicName: "collisionPadding", isSignal: true, isRequired: false, transformFunction: null }, arrowPadding: { classPropertyName: "arrowPadding", publicName: "arrowPadding", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, hideWhenDetached: { classPropertyName: "hideWhenDetached", publicName: "hideWhenDetached", isSignal: true, isRequired: false, transformFunction: null }, clipUntilPositioned: { classPropertyName: "clipUntilPositioned", publicName: "clipUntilPositioned", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, returnFocus: { classPropertyName: "returnFocus", publicName: "returnFocus", isSignal: true, isRequired: false, transformFunction: null }, initialFocus: { classPropertyName: "initialFocus", publicName: "initialFocus", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", escapeKeyDown: "escapeKeyDown", pointerDownOutside: "pointerDownOutside", focusOutside: "focusOutside", interactOutside: "interactOutside", autoFocusOnOpen: "autoFocusOnOpen", autoFocusOnClose: "autoFocusOnClose" }, host: { properties: { "attr.data-state": "open() ? \"open\" : \"closed\"", "attr.data-disabled": "disabled() ? \"\" : null", "attr.data-reduced-motion": "reducedMotion() ? \"\" : null" } }, providers: [{ provide: FOR_POPOVER_CONTEXT, useExisting: ForPopover }], exportAs: ["forPopover"], ngImport: i0 });
382
+ }
383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopover, decorators: [{
384
+ type: Directive,
385
+ args: [{
386
+ selector: '[forPopover]',
387
+ exportAs: 'forPopover',
388
+ host: {
389
+ '[attr.data-state]': 'open() ? "open" : "closed"',
390
+ '[attr.data-disabled]': 'disabled() ? "" : null',
391
+ '[attr.data-reduced-motion]': 'reducedMotion() ? "" : null',
392
+ },
393
+ providers: [{ provide: FOR_POPOVER_CONTEXT, useExisting: ForPopover }],
394
+ }]
395
+ }], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], _sideInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], _alignInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], _sideOffsetInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideOffset", required: false }] }], alignOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignOffset", required: false }] }], avoidCollisions: [{ type: i0.Input, args: [{ isSignal: true, alias: "avoidCollisions", required: false }] }], _collisionPaddingInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionPadding", required: false }] }], arrowPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "arrowPadding", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], hideWhenDetached: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideWhenDetached", required: false }] }], clipUntilPositioned: [{ type: i0.Input, args: [{ isSignal: true, alias: "clipUntilPositioned", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], returnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "returnFocus", required: false }] }], initialFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialFocus", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], escapeKeyDown: [{ type: i0.Output, args: ["escapeKeyDown"] }], pointerDownOutside: [{ type: i0.Output, args: ["pointerDownOutside"] }], focusOutside: [{ type: i0.Output, args: ["focusOutside"] }], interactOutside: [{ type: i0.Output, args: ["interactOutside"] }], autoFocusOnOpen: [{ type: i0.Output, args: ["autoFocusOnOpen"] }], autoFocusOnClose: [{ type: i0.Output, args: ["autoFocusOnClose"] }] } });
396
+
397
+ /**
398
+ * Button that toggles the popover when clicked. Apply on a focusable
399
+ * element — preferably a `<button>` — so keyboard users can reach it.
400
+ *
401
+ * Wires `aria-expanded`, `aria-controls`, and `aria-haspopup="dialog"`,
402
+ * registers the host as the floating-ui anchor, and toggles the open
403
+ * state on click. The trigger is exempt from the dismissable layer's
404
+ * outside-pointer / outside-focus checks so its own click never
405
+ * spuriously closes the popover.
406
+ *
407
+ * Disabling: the trigger merges its own `disabled` input OR the root's
408
+ * `disabled` into `effectiveDisabled`, which drives the native `disabled`
409
+ * attribute, `aria-disabled`, `data-disabled`, and the click guard.
410
+ *
411
+ * The root is normally resolved via DI from the enclosing `[forPopover]`.
412
+ * When the trigger is declared inside an `ng-template` stamped into the root
413
+ * (e.g. via `ngTemplateOutlet`), DI resolves at the template's declaration
414
+ * site and misses the root — pass it explicitly through the selector input,
415
+ * `routerLink`-style: `[forPopoverTrigger]="root"` with `#root="forPopover"`.
416
+ */
417
+ class ForPopoverTrigger {
418
+ #host = inject(ElementRef);
419
+ /**
420
+ * Optional explicit reference to the `[forPopover]` root, named after the
421
+ * selector `routerLink`-style. The bare valueless attribute keeps resolving
422
+ * the enclosing root via DI; pass the root explicitly
423
+ * (`[forPopoverTrigger]="root"`, with `#root="forPopover"`) when the trigger
424
+ * is declared in an `ng-template` stamped inside the root — DI resolves at
425
+ * the template's declaration site, so the enclosing root is invisible there.
426
+ * The empty string (what the valueless attribute yields) is treated as unset.
427
+ */
428
+ forPopoverTrigger = input('', /* @ts-ignore */
429
+ ...(ngDevMode ? [{ debugName: "forPopoverTrigger" }] : /* istanbul ignore next */ []));
430
+ ctx = injectPopoverTriggerContext(this.forPopoverTrigger);
431
+ /** Disables this trigger only, in addition to the root's `disabled`. */
432
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
433
+ /** Whether the trigger is disabled — its own `disabled` input OR the root's. */
434
+ effectiveDisabled = computed(() => this.disabled() || this.ctx().disabled(), /* @ts-ignore */
435
+ ...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
436
+ constructor() {
437
+ const el = this.#host.nativeElement;
438
+ // Registration is an imperative call into the resolved root's registry,
439
+ // not state derivation — the effect only re-registers the element when the
440
+ // resolved root changes (explicit reference swapped at runtime).
441
+ effect((onCleanup) => {
442
+ const ctx = this.ctx();
443
+ ctx.registerTrigger(el);
444
+ onCleanup(() => ctx.unregisterTrigger(el));
445
+ });
446
+ reflectDisabled(this.effectiveDisabled);
447
+ }
448
+ onClick() {
449
+ if (this.effectiveDisabled()) {
450
+ return;
451
+ }
452
+ this.ctx().toggle();
453
+ }
454
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
455
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForPopoverTrigger, isStandalone: true, selector: "[forPopoverTrigger]", inputs: { forPopoverTrigger: { classPropertyName: "forPopoverTrigger", publicName: "forPopoverTrigger", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button" }, listeners: { "click": "onClick()" }, properties: { "id": "ctx().triggerId()", "attr.aria-haspopup": "\"dialog\"", "attr.aria-expanded": "ctx().open() ? \"true\" : \"false\"", "attr.aria-controls": "ctx().open() ? ctx().contentId() : null", "attr.data-state": "ctx().open() ? \"open\" : \"closed\"", "attr.data-disabled": "effectiveDisabled() ? \"\" : null", "attr.aria-disabled": "effectiveDisabled() ? \"true\" : null" } }, exportAs: ["forPopoverTrigger"], ngImport: i0 });
456
+ }
457
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverTrigger, decorators: [{
458
+ type: Directive,
459
+ args: [{
460
+ selector: '[forPopoverTrigger]',
461
+ exportAs: 'forPopoverTrigger',
462
+ host: {
463
+ type: 'button',
464
+ '[id]': 'ctx().triggerId()',
465
+ '[attr.aria-haspopup]': '"dialog"',
466
+ '[attr.aria-expanded]': 'ctx().open() ? "true" : "false"',
467
+ '[attr.aria-controls]': 'ctx().open() ? ctx().contentId() : null',
468
+ '[attr.data-state]': 'ctx().open() ? "open" : "closed"',
469
+ '[attr.data-disabled]': 'effectiveDisabled() ? "" : null',
470
+ '[attr.aria-disabled]': 'effectiveDisabled() ? "true" : null',
471
+ '(click)': 'onClick()',
472
+ },
473
+ }]
474
+ }], ctorParameters: () => [], propDecorators: { forPopoverTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "forPopoverTrigger", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
475
+
476
+ /**
477
+ * Optional positioning anchor. When present, the popover is positioned
478
+ * against this element instead of `[forPopoverTrigger]` — useful when
479
+ * "what opens it" and "where it appears" are different elements (a button
480
+ * opens the popover but it's anchored to a row, a text-selection range,
481
+ * a cursor follower, etc.).
482
+ *
483
+ * Only positioning changes: the trigger still owns `aria-controls`,
484
+ * `aria-expanded`, the click toggle, and focus return on close. If no
485
+ * anchor is registered the popover falls back to anchoring against the
486
+ * trigger, so existing usages are unaffected.
487
+ *
488
+ * ```html
489
+ * <div forPopover [(open)]="open">
490
+ * <button forPopoverTrigger>Help</button>
491
+ * <span [forPopoverAnchor]>Anchored phrase</span>
492
+ * @if (open()) {
493
+ * <div forPopoverContent>…</div>
494
+ * }
495
+ * </div>
496
+ * ```
497
+ */
498
+ class ForPopoverAnchor {
499
+ #ctx = injectPopoverContext('ForPopoverAnchor');
500
+ #host = inject(ElementRef);
501
+ constructor() {
502
+ registerHandle(this.#host.nativeElement, (el) => this.#ctx.registerAnchor(el), (el) => this.#ctx.unregisterAnchor(el));
503
+ }
504
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverAnchor, deps: [], target: i0.ɵɵFactoryTarget.Directive });
505
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForPopoverAnchor, isStandalone: true, selector: "[forPopoverAnchor]", exportAs: ["forPopoverAnchor"], ngImport: i0 });
506
+ }
507
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverAnchor, decorators: [{
508
+ type: Directive,
509
+ args: [{
510
+ selector: '[forPopoverAnchor]',
511
+ exportAs: 'forPopoverAnchor',
512
+ }]
513
+ }], ctorParameters: () => [] });
514
+
515
+ /**
516
+ * The popover surface. Carries `role="dialog"` (non-modal — `aria-modal` is absent per APG), is
517
+ * portaled to `document.body`, and is positioned by `@floating-ui/dom`
518
+ * against the registered `[forPopoverTrigger]`.
519
+ *
520
+ * The directive does not manage DOM presence — wrap with
521
+ * `@if (popoverOpen())` so `animate.enter` / `animate.leave` fire on
522
+ * the natural mount / unmount cycle. While mounted the directive
523
+ * activates a `DismissableLayer` (Escape, pointer-down outside, focus
524
+ * outside) and sends focus to the configured `initialFocus` target;
525
+ * on unmount it returns focus to the trigger if `returnFocus` is on.
526
+ *
527
+ * The trigger is exempt from the layer's outside checks, so clicking
528
+ * it again just toggles via the trigger directive — no double-close
529
+ * race.
530
+ *
531
+ * The lifecycle (positioner + dismissable layer + initial focus + return
532
+ * focus) is owned by the shared `injectOverlayShell` helper. The shell's
533
+ * `'first'` mode routes through the same `findFirstFocusable` helper used
534
+ * by `FocusTrap`, dropping the local focusable-selector copy that this
535
+ * directive carried before (see #107).
536
+ */
537
+ class ForPopoverContent {
538
+ ctx = injectPopoverContext('ForPopoverContent');
539
+ constructor() {
540
+ this.ctx.adoptContentId(inject(ElementRef).nativeElement);
541
+ injectOverlayShell({
542
+ positioner: toFloatingPositioner(this.ctx, this.ctx.reference),
543
+ dismiss: {
544
+ dismissible: this.ctx.dismissible,
545
+ requestClose: () => this.ctx.requestClose(),
546
+ emitEscapeKeyDown: (event) => this.ctx.emitEscapeKeyDown(event),
547
+ emitPointerDownOutside: (veto) => this.ctx.emitPointerDownOutside(veto),
548
+ emitFocusOutside: (veto) => this.ctx.emitFocusOutside(veto),
549
+ emitInteractOutside: (veto) => this.ctx.emitInteractOutside(veto),
550
+ // The trigger lives outside the portaled content but is logically
551
+ // "inside" the popover for outside-pointer / outside-focus checks
552
+ // — without this, clicking the trigger to close would race with
553
+ // its own toggle and you'd end up reopening immediately.
554
+ exemptElements: () => {
555
+ const trigger = this.ctx.trigger();
556
+ return trigger ? [trigger] : [];
557
+ },
558
+ },
559
+ // `initialFocus()` is read once when the content mounts. The directive
560
+ // re-mounts on every open (consumer wraps with `@if(open())`), so the
561
+ // value is fresh each time.
562
+ initialFocus: {
563
+ move: this.ctx.initialFocus() === 'container' ? 'container' : 'first',
564
+ veto: () => this.ctx.emitAutoFocusOnOpen(),
565
+ },
566
+ returnFocus: {
567
+ enabled: this.ctx.returnFocus,
568
+ target: () => this.ctx.trigger(),
569
+ // `(autoFocusOnClose)` lets the consumer veto the return-focus.
570
+ veto: () => this.ctx.emitAutoFocusOnClose(),
571
+ },
572
+ });
573
+ }
574
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
575
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForPopoverContent, isStandalone: true, selector: "[forPopoverContent]", host: { attributes: { "role": "dialog", "tabindex": "-1" }, properties: { "id": "ctx.contentId()", "attr.aria-label": "ctx.ariaLabel()", "attr.aria-labelledby": "ctx.labelledBy()", "attr.aria-describedby": "ctx.describedBy()", "attr.data-state": "ctx.open() ? \"open\" : \"closed\"", "attr.data-reduced-motion": "ctx.reducedMotion() ? \"\" : null" } }, exportAs: ["forPopoverContent"], ngImport: i0 });
576
+ }
577
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverContent, decorators: [{
578
+ type: Directive,
579
+ args: [{
580
+ selector: '[forPopoverContent]',
581
+ exportAs: 'forPopoverContent',
582
+ host: {
583
+ role: 'dialog',
584
+ '[id]': 'ctx.contentId()',
585
+ '[attr.aria-label]': 'ctx.ariaLabel()',
586
+ '[attr.aria-labelledby]': 'ctx.labelledBy()',
587
+ '[attr.aria-describedby]': 'ctx.describedBy()',
588
+ '[attr.data-state]': 'ctx.open() ? "open" : "closed"',
589
+ '[attr.data-reduced-motion]': 'ctx.reducedMotion() ? "" : null',
590
+ tabindex: '-1',
591
+ },
592
+ }]
593
+ }], ctorParameters: () => [] });
594
+
595
+ /**
596
+ * Convenience: gives the host element a stable id and registers it as the
597
+ * popover's accessible name (`aria-labelledby`). Apply on a heading element
598
+ * (`<h2>`, `<h3>`...) inside `[forPopoverContent]`.
599
+ *
600
+ * If you don't render a visible title, omit this and pass `ariaLabel` on
601
+ * the popover root instead.
602
+ */
603
+ class ForPopoverTitle {
604
+ id = registerA11yName(injectPopoverContext('ForPopoverTitle'), 'for-popover-title');
605
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverTitle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
606
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForPopoverTitle, isStandalone: true, selector: "[forPopoverTitle]", host: { properties: { "id": "id()" } }, exportAs: ["forPopoverTitle"], ngImport: i0 });
607
+ }
608
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverTitle, decorators: [{
609
+ type: Directive,
610
+ args: [{
611
+ selector: '[forPopoverTitle]',
612
+ exportAs: 'forPopoverTitle',
613
+ host: {
614
+ '[id]': 'id()',
615
+ },
616
+ }]
617
+ }] });
618
+
619
+ /**
620
+ * Convenience: gives the host element a stable id and registers it as the
621
+ * popover's accessible description (`aria-describedby`). Apply on a paragraph
622
+ * or similar text element inside `[forPopoverContent]`.
623
+ */
624
+ class ForPopoverDescription {
625
+ id = registerA11yDescription(injectPopoverContext('ForPopoverDescription'), 'for-popover-description');
626
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive });
627
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForPopoverDescription, isStandalone: true, selector: "[forPopoverDescription]", host: { properties: { "id": "id()" } }, exportAs: ["forPopoverDescription"], ngImport: i0 });
628
+ }
629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverDescription, decorators: [{
630
+ type: Directive,
631
+ args: [{
632
+ selector: '[forPopoverDescription]',
633
+ exportAs: 'forPopoverDescription',
634
+ host: {
635
+ '[id]': 'id()',
636
+ },
637
+ }]
638
+ }] });
639
+
640
+ /**
641
+ * Button that closes the surrounding popover when clicked. Apply on a
642
+ * `<button>` inside `[forPopoverContent]`. Bypasses `dismissible` — an
643
+ * explicit close button is always honored.
644
+ */
645
+ class ForPopoverClose {
646
+ #ctx = injectPopoverContext('ForPopoverClose');
647
+ onClick() {
648
+ this.#ctx.open.set(false);
649
+ }
650
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverClose, deps: [], target: i0.ɵɵFactoryTarget.Directive });
651
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForPopoverClose, isStandalone: true, selector: "[forPopoverClose]", host: { attributes: { "type": "button" }, listeners: { "click": "onClick()" } }, exportAs: ["forPopoverClose"], ngImport: i0 });
652
+ }
653
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverClose, decorators: [{
654
+ type: Directive,
655
+ args: [{
656
+ selector: '[forPopoverClose]',
657
+ exportAs: 'forPopoverClose',
658
+ host: {
659
+ type: 'button',
660
+ '(click)': 'onClick()',
661
+ },
662
+ }]
663
+ }] });
664
+
665
+ /**
666
+ * Optional visual arrow inside `ForPopoverContent`. Registers itself with
667
+ * the popover context so floating-ui's `arrow` middleware can position it
668
+ * along the surface edge that points at the trigger. Style size and color
669
+ * yourself — the directive only sets `position`, `left`/`top`, and the
670
+ * opposite-side offset.
671
+ */
672
+ class ForPopoverArrow {
673
+ #ctx = injectPopoverContext('ForPopoverArrow');
674
+ #host = inject(ElementRef);
675
+ constructor() {
676
+ registerHandle(this.#host.nativeElement, (el) => this.#ctx.registerArrow(el), (el) => this.#ctx.unregisterArrow(el));
677
+ }
678
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverArrow, deps: [], target: i0.ɵɵFactoryTarget.Directive });
679
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForPopoverArrow, isStandalone: true, selector: "[forPopoverArrow]", host: { attributes: { "aria-hidden": "true", "data-popover-arrow": "" } }, exportAs: ["forPopoverArrow"], ngImport: i0 });
680
+ }
681
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForPopoverArrow, decorators: [{
682
+ type: Directive,
683
+ args: [{
684
+ selector: '[forPopoverArrow]',
685
+ exportAs: 'forPopoverArrow',
686
+ host: {
687
+ 'aria-hidden': 'true',
688
+ 'data-popover-arrow': '',
689
+ },
690
+ }]
691
+ }], ctorParameters: () => [] });
692
+
693
+ /**
694
+ * Generated bundle index. Do not edit.
695
+ */
696
+
697
+ export { FOR_POPOVER_CONTEXT, FOR_POPOVER_DEFAULTS, ForPopover, ForPopoverAnchor, ForPopoverArrow, ForPopoverClose, ForPopoverContent, ForPopoverDescription, ForPopoverTitle, ForPopoverTrigger, provideForPopoverDefaults };
698
+ //# sourceMappingURL=forty-cdk-popover.mjs.map