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,1011 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, computed, model, input, booleanAttribute, numberAttribute, output, signal, contentChild, effect, isDevMode, Directive, ElementRef } from '@angular/core';
3
+ import { createDefaults, FormUiControlBase, IdGenerator, injectDateAdapter, injectTextDirection, FOR_TIME_VALUE_SOURCE, injectHiddenInput, serializeISODate, clampToBounds, composeWithTime, assertTimeCapable, adoptHostId, createVetoableNativeEvent, emitVetoableEvent, registerHandle, reflectDisabled, injectModalShell, injectOverlayShell } from 'forty-cdk/core';
4
+ import { ForCalendar } from 'forty-cdk/calendar';
5
+
6
+ /** Injection token for {@link ForDatePickerContext}, provided by `ForDatePicker`. */
7
+ const FOR_DATE_PICKER_CONTEXT = new InjectionToken('FOR_DATE_PICKER_CONTEXT');
8
+ /**
9
+ * Injects the nearest {@link ForDatePickerContext}, throwing a descriptive
10
+ * error when used outside a `[forDatePicker]` element.
11
+ *
12
+ * @param piece Name of the calling directive, used in the error message.
13
+ */
14
+ function injectDatePickerContext(piece) {
15
+ const ctx = inject(FOR_DATE_PICKER_CONTEXT, { optional: true });
16
+ if (!ctx) {
17
+ throw new Error(`[forty-cdk/date-picker] ${piece} must be used inside a [forDatePicker] element. ` +
18
+ "If it is declared inside an ng-template, DI resolves at the template's declaration site — " +
19
+ 'not where it is stamped (e.g. via ngTemplateOutlet) — so declare the template inside the ' +
20
+ '[forDatePicker] root.');
21
+ }
22
+ return ctx;
23
+ }
24
+ /**
25
+ * Resolves the trigger's root context: the explicit reference when the
26
+ * `[forDatePickerTrigger]` input carries one, the injected
27
+ * `FOR_DATE_PICKER_CONTEXT` otherwise. The orphan error only fires when neither
28
+ * resolves, on first read of the returned signal. Must be called in an
29
+ * injection context.
30
+ */
31
+ function injectDatePickerTriggerContext(explicitRoot) {
32
+ const injected = inject(FOR_DATE_PICKER_CONTEXT, { optional: true });
33
+ return computed(() => {
34
+ const explicit = explicitRoot();
35
+ if (explicit !== '') {
36
+ return explicit;
37
+ }
38
+ if (injected) {
39
+ return injected;
40
+ }
41
+ throw new Error('[forty-cdk/date-picker] ForDatePickerTrigger could not resolve its [forDatePicker] root: ' +
42
+ 'no FOR_DATE_PICKER_CONTEXT provider is visible and no explicit root reference was passed. ' +
43
+ "If this trigger is declared inside an ng-template, DI resolves at the template's declaration " +
44
+ 'site — not where it is stamped — so either declare the template inside the root or pass the ' +
45
+ 'root explicitly: [forDatePickerTrigger]="root" with #root="forDatePicker".');
46
+ });
47
+ }
48
+
49
+ /**
50
+ * Library fallback for date-picker defaults, read at the root injector when no
51
+ * consumer has called `provideForDatePickerDefaults`. Exported for the shared
52
+ * defaults contract spec; not re-exported from the primitive's public entry.
53
+ */
54
+ const FOR_DATE_PICKER_FALLBACK_DEFAULTS = {
55
+ sideOffset: 8,
56
+ collisionPadding: 8,
57
+ };
58
+ const { token, provideDefaults } = createDefaults('FOR_DATE_PICKER_DEFAULTS', FOR_DATE_PICKER_FALLBACK_DEFAULTS);
59
+ /** Token holding the resolved date-picker defaults for the current scope. */
60
+ const FOR_DATE_PICKER_DEFAULTS = token;
61
+ /**
62
+ * Configures forty-cdk date-picker defaults for this injector scope. Partial
63
+ * overrides inherit unspecified keys from the parent scope (or library
64
+ * defaults at the root).
65
+ */
66
+ function provideForDatePickerDefaults(defaults = {}) {
67
+ return provideDefaults(defaults);
68
+ }
69
+
70
+ /**
71
+ * Headless date picker — the [WAI-ARIA Date Picker Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/)
72
+ * reinterpreted idiomatically for modern Angular: a focusable trigger that opens
73
+ * a floating surface wrapping a projected `ForCalendar`.
74
+ *
75
+ * `ForDatePicker` is the root and the form value: it implements
76
+ * `FormValueControl<D | null>` from `@angular/forms/signals`, so it auto-wires
77
+ * with `[formField]` and auto-associates inside a `[forField]`. The trigger is
78
+ * the focusable control that carries `name` / `disabled` / `invalid`; selection
79
+ * state flows root → projected calendar via `[(value)]`.
80
+ *
81
+ * The surface defaults to a **non-modal popover** (anchored to the trigger,
82
+ * dismiss on Escape / outside-pointer, return focus on close); set `modal`
83
+ * for the trapped / inert / scroll-locked dialog variant. Mount/unmount of the surface is the consumer's job — wrap
84
+ * `[forDatePickerContent]` with `@if (open())`.
85
+ *
86
+ * The projected `ForCalendar` is two-way bound by the consumer (`[(value)]`)
87
+ * and forwarded `[min]` / `[max]` / `[isDateUnavailable]` from the picker's
88
+ * accessors. A selection inside the grid is observed through a `contentChild`
89
+ * query on the calendar's `valueChange`, so the calendar primitive stays
90
+ * untouched; on selection the picker mirrors the value, flips `touched`, and —
91
+ * when `closeOnSelect` is on (default) — closes the surface.
92
+ *
93
+ * Set `granularity` coarser-than-a-day off (`'hour'` / `'minute'` / `'second'`)
94
+ * to make it a **date-time picker**: the consumer projects a `[forTimeField]`
95
+ * beside the calendar (binding both children **one-way** to `picker.value()` so
96
+ * their internal writes don't clobber each other), and the picker grafts the
97
+ * entered time onto each calendar selection. This needs a time-capable adapter
98
+ * (`provideNativeDateAdapter()` / `provideInternationalizedDateTimeAdapter()`).
99
+ *
100
+ * @typeParam D The adapter's immutable date (or, with `granularity > 'day'`,
101
+ * date-time) type.
102
+ *
103
+ * Note: the date bounds are named `minDate` / `maxDate`, not `min` / `max` —
104
+ * the latter are reserved `FormUiControl` members typed `number | undefined`
105
+ * for numeric validators, so a date-typed `min` / `max` would break the
106
+ * `FormValueControl` contract.
107
+ *
108
+ * @example
109
+ * ```html
110
+ * <div forDatePicker [(value)]="date" [(open)]="open" [minDate]="min" [maxDate]="max"
111
+ * name="dob" [ariaLabel]="'Choose date'" #picker="forDatePicker">
112
+ * <button forDatePickerTrigger>
113
+ * <span forDatePickerValue>Pick a date</span>
114
+ * </button>
115
+ *
116
+ * @if (open()) {
117
+ * <div forDatePickerContent>
118
+ * <div forCalendar [(value)]="date" [min]="picker.minDate()" [max]="picker.maxDate()">
119
+ * <!-- …calendar header + grid… -->
120
+ * </div>
121
+ * </div>
122
+ * }
123
+ * </div>
124
+ * ```
125
+ *
126
+ * @example Date-time picker (`granularity="minute"`), children bound one-way:
127
+ * ```html
128
+ * <div forDatePicker [(value)]="when" [(open)]="open" granularity="minute"
129
+ * [hourCycle]="24" #picker="forDatePicker">
130
+ * <button forDatePickerTrigger><span forDatePickerValue>Pick date & time</span></button>
131
+ * @if (open()) {
132
+ * <div forDatePickerContent>
133
+ * <div forCalendar [value]="picker.value()" [min]="picker.minDate()">…</div>
134
+ * <div forTimeField [value]="picker.value()" [hourCycle]="picker.hourCycle()">…</div>
135
+ * </div>
136
+ * }
137
+ * </div>
138
+ * ```
139
+ */
140
+ class ForDatePicker extends FormUiControlBase {
141
+ #idGen = inject(IdGenerator);
142
+ #defaults = inject(FOR_DATE_PICKER_DEFAULTS);
143
+ /** The active date adapter, resolved from `FOR_DATE_ADAPTER` (shared with `ForCalendar`). */
144
+ adapter = injectDateAdapter('ForDatePicker');
145
+ /**
146
+ * Two-way bindable selected date, or `null`. Required by
147
+ * `FormValueControl<D | null>`. The `model()` change emitter (`(valueChange)`)
148
+ * fires only when the picker itself commits a selection, never on consumer
149
+ * writes via `[(value)]`.
150
+ */
151
+ value = model(null, /* @ts-ignore */
152
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
153
+ /**
154
+ * Two-way bindable. Whether the surface is open. The `model()` change emitter
155
+ * (`(openChange)`) fires only on internal transitions (trigger toggle,
156
+ * Escape, outside dismissal, selection), never on consumer writes via
157
+ * `[(open)]`.
158
+ */
159
+ open = model(false, /* @ts-ignore */
160
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
161
+ /**
162
+ * Minimum selectable date (inclusive). Forward to the projected calendar's
163
+ * `[min]`. Named `minDate` (not `min`) because `FormUiControl.min` is reserved
164
+ * for a numeric validator bound by `[formField]`.
165
+ */
166
+ minDate = input(null, /* @ts-ignore */
167
+ ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
168
+ /**
169
+ * Maximum selectable date (inclusive). Forward to the projected calendar's
170
+ * `[max]`. Named `maxDate` (not `max`) for the same reason as {@link minDate}.
171
+ */
172
+ maxDate = input(null, /* @ts-ignore */
173
+ ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
174
+ /** Per-date predicate. Forward to the projected calendar's `[isDateUnavailable]`. */
175
+ isDateUnavailable = input(() => false, /* @ts-ignore */
176
+ ...(ngDevMode ? [{ debugName: "isDateUnavailable" }] : /* istanbul ignore next */ []));
177
+ /**
178
+ * Close the surface after a date is selected in the projected calendar.
179
+ * Default `true`. Honoured only at `granularity="day"` — a date-time picker
180
+ * (`granularity > 'day'`) never closes on a calendar selection, so the user
181
+ * can go on to edit the time.
182
+ */
183
+ closeOnSelect = input(true, { ...(ngDevMode ? { debugName: "closeOnSelect" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
184
+ /**
185
+ * Date-time precision. `'day'` (default, **non-breaking**) keeps a pure
186
+ * calendar picker. Anything coarser-than-a-day off — `'hour'` / `'minute'` /
187
+ * `'second'` — turns it into a date-time picker: the consumer projects a
188
+ * `[forTimeField]` beside the calendar, a calendar selection preserves the
189
+ * entered time, and the value carries a time component. Requires a
190
+ * time-capable adapter (`provideNativeDateAdapter()` or
191
+ * `provideInternationalizedDateTimeAdapter()`).
192
+ */
193
+ granularity = input('day', /* @ts-ignore */
194
+ ...(ngDevMode ? [{ debugName: "granularity" }] : /* istanbul ignore next */ []));
195
+ /**
196
+ * 12- or 24-hour cycle forwarded to `[forDatePickerValue]`'s formatting (and
197
+ * typically to the projected `[forTimeField][hourCycle]`). When `null`
198
+ * (default) it is derived from the runtime locale. Only meaningful when
199
+ * `granularity > 'day'`.
200
+ */
201
+ hourCycle = input(null, /* @ts-ignore */
202
+ ...(ngDevMode ? [{ debugName: "hourCycle" }] : /* istanbul ignore next */ []));
203
+ /**
204
+ * When `true`, the surface is a trapped / inert / scroll-locked modal dialog
205
+ * (routed through `_internal/modal-shell`) instead of the default non-modal
206
+ * anchored popover. Read once when the content mounts.
207
+ */
208
+ modal = input(false, { ...(ngDevMode ? { debugName: "modal" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
209
+ /** When true (default), Escape, pointer-down outside, and focus outside close the surface. */
210
+ dismissible = input(true, { ...(ngDevMode ? { debugName: "dismissible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
211
+ /** When true (default), focus returns to the trigger on close. */
212
+ returnFocus = input(true, { ...(ngDevMode ? { debugName: "returnFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
213
+ /**
214
+ * `Intl.DateTimeFormat` options driving the text rendered by
215
+ * `[forDatePickerValue]`. Default `{ year: 'numeric', month: 'long', day: 'numeric' }`.
216
+ */
217
+ formatOptions = input({
218
+ year: 'numeric',
219
+ month: 'long',
220
+ day: 'numeric',
221
+ }, /* @ts-ignore */
222
+ ...(ngDevMode ? [{ debugName: "formatOptions" }] : /* istanbul ignore next */ []));
223
+ /** Text rendered by `[forDatePickerValue]` when no date is selected. */
224
+ placeholder = input('', /* @ts-ignore */
225
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
226
+ /**
227
+ * Selection mode. `'single'` (default) keeps the existing single-date
228
+ * `[(value)]` and `FormValueControl<D | null>` behaviour unchanged.
229
+ * `'range'` switches to the two-click anchor → commit flow and exposes the
230
+ * result through `[(range)]`. Range mode is day-granular and ignores
231
+ * `granularity` / time. Range is not a Signal Forms value in v1.
232
+ */
233
+ selectionMode = input('single', /* @ts-ignore */
234
+ ...(ngDevMode ? [{ debugName: "selectionMode" }] : /* istanbul ignore next */ []));
235
+ /**
236
+ * Two-way bindable committed date range, or `null`. Only used when
237
+ * `selectionMode="range"`. The `model()` change emitter (`(rangeChange)`)
238
+ * fires only when the picker internally commits or clears a range, never on
239
+ * consumer writes via `[(range)]`.
240
+ */
241
+ range = model(null, /* @ts-ignore */
242
+ ...(ngDevMode ? [{ debugName: "range" }] : /* istanbul ignore next */ []));
243
+ /**
244
+ * Separator rendered between start and end in `[forDatePickerValue]` while
245
+ * in `selectionMode="range"`. Default `' – '` (en-dash with spaces).
246
+ */
247
+ rangeSeparator = input(' – ', /* @ts-ignore */
248
+ ...(ngDevMode ? [{ debugName: "rangeSeparator" }] : /* istanbul ignore next */ []));
249
+ /** Side the surface is anchored to. Defaults to `'bottom'`. Ignored in `modal` mode. */
250
+ side = input('bottom', /* @ts-ignore */
251
+ ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
252
+ /** Alignment along the chosen `side`. Defaults to `'start'`. Ignored in `modal` mode. */
253
+ align = input('start', /* @ts-ignore */
254
+ ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
255
+ /** Gap (px) between trigger and surface along the main axis. Default from `provideForDatePickerDefaults`. */
256
+ sideOffset = input(this.#defaults.sideOffset, { ...(ngDevMode ? { debugName: "sideOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
257
+ /** Gap (px) along the cross axis. Default `0`. */
258
+ alignOffset = input(0, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
259
+ /** When `true` (default), `flip` and `shift` keep the surface inside the viewport. */
260
+ avoidCollisions = input(true, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
261
+ /** Padding (px) applied uniformly to flip / shift / size. Default from `provideForDatePickerDefaults`. */
262
+ collisionPadding = input(this.#defaults.collisionPadding, { ...(ngDevMode ? { debugName: "collisionPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
263
+ /** Stickiness behaviour for `shift`. Default `'partial'`. */
264
+ sticky = input('partial', /* @ts-ignore */
265
+ ...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
266
+ /** When `true`, sets `data-detached=""` while the trigger is scrolled off-screen. */
267
+ hideWhenDetached = input(false, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
268
+ /**
269
+ * When `true` (default), the content is clipped until floating-ui resolves
270
+ * its first position, preventing a flash at the viewport corner. Set to
271
+ * `false` so a dramatic `animate.enter` plays from its first frame (the
272
+ * surface may flash briefly at the unresolved position while positioning
273
+ * computes).
274
+ */
275
+ clipUntilPositioned = input(true, { ...(ngDevMode ? { debugName: "clipUntilPositioned" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
276
+ /** Accessible name for the dialog surface. Emits no `aria-label` while `null`. */
277
+ ariaLabel = input(null, /* @ts-ignore */
278
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
279
+ /**
280
+ * Writing direction. When unset (default `null`), the inherited ambient
281
+ * direction is resolved from the nearest ancestor carrying a `dir` attribute
282
+ * (or `<html dir>`), defaulting to `'ltr'`. An explicit `[dir]` always wins.
283
+ * The resolved value is reflected to the host `dir` attribute.
284
+ */
285
+ _dirInput = input(null, { ...(ngDevMode ? { debugName: "_dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
286
+ dir = injectTextDirection(this._dirInput);
287
+ /**
288
+ * Fires when the user presses Escape while this surface is the topmost
289
+ * dismissable layer. Call `preventDefault()` on the veto to suppress the
290
+ * automatic close.
291
+ */
292
+ escapeKeyDown = output();
293
+ /** Fires when a pointer goes down outside the surface (and trigger). Vetoable. */
294
+ pointerDownOutside = output();
295
+ /** Fires when focus moves outside the surface (and trigger). Vetoable. */
296
+ focusOutside = output();
297
+ /** Composite event: shares veto state with `pointerDownOutside` / `focusOutside`. */
298
+ interactOutside = output();
299
+ /** Fires just before the surface sends focus into itself on open. Vetoable. */
300
+ autoFocusOnOpen = output();
301
+ /** Fires just before focus returns to the trigger on close. Vetoable. */
302
+ autoFocusOnClose = output();
303
+ triggerId = signal(this.#idGen.next('for-date-picker-trigger'), /* @ts-ignore */
304
+ ...(ngDevMode ? [{ debugName: "triggerId" }] : /* istanbul ignore next */ []));
305
+ contentId = signal(this.#idGen.next('for-date-picker-content'), /* @ts-ignore */
306
+ ...(ngDevMode ? [{ debugName: "contentId" }] : /* istanbul ignore next */ []));
307
+ #triggerEl = signal(null, /* @ts-ignore */
308
+ ...(ngDevMode ? [{ debugName: "#triggerEl" }] : /* istanbul ignore next */ []));
309
+ trigger = this.#triggerEl.asReadonly();
310
+ #anchorEl = signal(null, /* @ts-ignore */
311
+ ...(ngDevMode ? [{ debugName: "#anchorEl" }] : /* istanbul ignore next */ []));
312
+ /**
313
+ * Element floating-ui anchors the surface against. Prefers an optional
314
+ * `[forDatePickerAnchor]` when registered, otherwise falls back to the
315
+ * trigger so existing pickers without an anchor keep their behavior.
316
+ * Decoupled from `trigger` so the trigger keeps driving `aria-controls`, the
317
+ * click toggle, focus return, and its dismissal exemption regardless of where
318
+ * the surface paints.
319
+ */
320
+ reference = computed(() => this.#anchorEl() ?? this.#triggerEl(), /* @ts-ignore */
321
+ ...(ngDevMode ? [{ debugName: "reference" }] : /* istanbul ignore next */ []));
322
+ #contentEl = signal(null, /* @ts-ignore */
323
+ ...(ngDevMode ? [{ debugName: "#contentEl" }] : /* istanbul ignore next */ []));
324
+ content = this.#contentEl.asReadonly();
325
+ /**
326
+ * `formatOptions` augmented with time fields when `granularity > 'day'` and
327
+ * the consumer hasn't already specified any — so a date-time picker's value
328
+ * display shows the time without extra wiring, while an explicit
329
+ * `formatOptions` is always honoured verbatim.
330
+ */
331
+ #effectiveFormatOptions = computed(() => {
332
+ const options = this.formatOptions();
333
+ const granularity = this.granularity();
334
+ if (granularity === 'day' ||
335
+ options.hour !== undefined ||
336
+ options.minute !== undefined ||
337
+ options.second !== undefined) {
338
+ return options;
339
+ }
340
+ const cycle = this.hourCycle();
341
+ return {
342
+ ...options,
343
+ hour: 'numeric',
344
+ minute: '2-digit',
345
+ ...(granularity === 'second' ? { second: '2-digit' } : {}),
346
+ ...(cycle !== null ? { hour12: cycle === 12 } : {}),
347
+ };
348
+ }, /* @ts-ignore */
349
+ ...(ngDevMode ? [{ debugName: "#effectiveFormatOptions" }] : /* istanbul ignore next */ []));
350
+ /** Formatted current value via the adapter, or `null` when empty. */
351
+ formattedValue = computed(() => {
352
+ if (this.selectionMode() === 'range') {
353
+ const r = this.range();
354
+ if (r === null) {
355
+ return null;
356
+ }
357
+ const fmtOpts = this.#effectiveFormatOptions();
358
+ return (this.adapter.format(r.start, fmtOpts) +
359
+ this.rangeSeparator() +
360
+ this.adapter.format(r.end, fmtOpts));
361
+ }
362
+ const value = this.value();
363
+ return value === null ? null : this.adapter.format(value, this.#effectiveFormatOptions());
364
+ }, /* @ts-ignore */
365
+ ...(ngDevMode ? [{ debugName: "formattedValue" }] : /* istanbul ignore next */ []));
366
+ /**
367
+ * The projected `ForCalendar`. Mounts only while the surface is open, so the
368
+ * query resolves to the live instance on open and to `undefined` on close.
369
+ * Its `valueChange` is the single signal that a date was selected inside the
370
+ * grid — wired in the constructor.
371
+ *
372
+ * Invariant: the projected calendar MUST resolve the same `DateAdapter` as
373
+ * this picker (the same `FOR_DATE_ADAPTER` scope). Angular's `contentChild`
374
+ * erases the generic, so the bridge reads `calendar.value` as `D | null` via
375
+ * a cast; a mismatched adapter would leak a wrong-shaped date through that
376
+ * seam. A dev-mode assertion in the bridge effect catches it early.
377
+ */
378
+ calendar = contentChild(ForCalendar, { ...(ngDevMode ? { debugName: "calendar" } : /* istanbul ignore next */ {}), descendants: true });
379
+ /**
380
+ * The projected `ForTimeField`, present only in a date-time picker
381
+ * (`granularity > 'day'`). Like the calendar, it mounts with the surface; its
382
+ * `valueChange` (a composed date-time, anchored on the picker's current
383
+ * value) is mirrored straight into the picker's value.
384
+ *
385
+ * Invariant: the projected time field MUST resolve the same `DateAdapter` as
386
+ * this picker (see {@link calendar}). The bridge casts its `value` to
387
+ * `D | null` because `contentChild` erases the generic; a dev-mode assertion
388
+ * guards the same-adapter contract.
389
+ */
390
+ timeSource = contentChild(FOR_TIME_VALUE_SOURCE, { ...(ngDevMode ? { debugName: "timeSource" } : /* istanbul ignore next */ {}), descendants: true });
391
+ constructor() {
392
+ super();
393
+ injectHiddenInput({
394
+ name: this.name,
395
+ values: computed(() => {
396
+ const value = this.value();
397
+ if (value === null) {
398
+ return [];
399
+ }
400
+ return [serializeISODate(this.adapter, value, this.granularity(), 'ForDatePicker')];
401
+ }),
402
+ disabled: this.effectiveDisabled,
403
+ });
404
+ // Eager validation: a date-time picker needs a time-capable adapter. Fail
405
+ // loudly as soon as the granularity input settles, rather than on first
406
+ // selection deep in a subscription. The throw is raised during change
407
+ // detection (inside this `effect`) and propagates through Angular's error
408
+ // handling, so a day-only adapter misconfiguration is surfaced — never
409
+ // silently swallowed.
410
+ effect(() => {
411
+ if (this.granularity() !== 'day') {
412
+ this.#time();
413
+ }
414
+ });
415
+ // Calendar selection bridge. This `effect` does no state derivation — it
416
+ // only (re)subscribes to the projected calendar's `valueChange` or
417
+ // `rangeChange` as the surface mounts / unmounts. The writes happen
418
+ // asynchronously in the subscription callback (a discrete selection event),
419
+ // exactly like a click handler, never during the effect's reactive
420
+ // computation.
421
+ effect((onCleanup) => {
422
+ const calendar = this.calendar();
423
+ if (!calendar) {
424
+ return;
425
+ }
426
+ if (isDevMode() && calendar.adapter !== this.adapter) {
427
+ throw new Error('[forty-cdk/date-picker] The projected ForCalendar must use the same DateAdapter as the ForDatePicker.');
428
+ }
429
+ if (this.selectionMode() === 'range') {
430
+ const sub = calendar.range.subscribe((next) => {
431
+ if (this.readonly() || this.effectiveDisabled()) {
432
+ return;
433
+ }
434
+ this.range.set(next);
435
+ this.markTouched();
436
+ if (next !== null && this.closeOnSelect()) {
437
+ this.close();
438
+ }
439
+ });
440
+ onCleanup(() => sub.unsubscribe());
441
+ return;
442
+ }
443
+ const sub = calendar.value.subscribe((date) => {
444
+ if (this.readonly() || this.effectiveDisabled()) {
445
+ return;
446
+ }
447
+ const selected = date;
448
+ // The projected calendar already preserves the time-of-day when it is
449
+ // one-way bound (`[value]`) to a timed value — `selectDate` re-grafts
450
+ // the current time via `#withPreservedTime`. This graft is a defensive
451
+ // fallback for a date-time picker whose calendar value was null or
452
+ // midnight (e.g. the very first selection): re-apply the previously
453
+ // entered time-of-day. Reading `value()` here is safe because the
454
+ // one-way binding means the calendar's own write didn't clobber it.
455
+ if (selected !== null && this.granularity() !== 'day') {
456
+ const base = this.value() ?? selected;
457
+ this.value.set(clampToBounds(this.adapter, composeWithTime(this.#time(), selected, base), this.minDate(), this.maxDate()));
458
+ }
459
+ else {
460
+ this.value.set(selected === null
461
+ ? null
462
+ : clampToBounds(this.adapter, selected, this.minDate(), this.maxDate()));
463
+ }
464
+ this.markTouched();
465
+ // A date-time picker stays open after a day is picked so the time can
466
+ // still be edited; only a pure day picker honours `closeOnSelect`.
467
+ if (this.closeOnSelect() && this.granularity() === 'day') {
468
+ this.close();
469
+ }
470
+ });
471
+ onCleanup(() => sub.unsubscribe());
472
+ });
473
+ // Time-source bridge (date-time pickers only). The projected time source
474
+ // (ForTimeField or ForTimePicker) is bound one-way to the picker's value,
475
+ // so its composed `valueChange` already carries the correct day plus the
476
+ // new time — mirror it straight in.
477
+ effect((onCleanup) => {
478
+ const timeSource = this.timeSource();
479
+ if (!timeSource) {
480
+ return;
481
+ }
482
+ if (isDevMode() && timeSource.adapter !== this.adapter) {
483
+ throw new Error('[forty-cdk/date-picker] The projected time source (ForTimeField / ForTimePicker) must use the same DateAdapter as the ForDatePicker.');
484
+ }
485
+ const sub = timeSource.value.subscribe((value) => {
486
+ if (this.readonly() || this.effectiveDisabled()) {
487
+ return;
488
+ }
489
+ const next = value;
490
+ this.value.set(next === null ? null : clampToBounds(this.adapter, next, this.minDate(), this.maxDate()));
491
+ this.markTouched();
492
+ });
493
+ onCleanup(() => sub.unsubscribe());
494
+ });
495
+ }
496
+ /** The active adapter, narrowed to a time-capable one; throws when it is day-only. */
497
+ #time() {
498
+ return assertTimeCapable(this.adapter, 'ForDatePicker');
499
+ }
500
+ registerTrigger(el) {
501
+ adoptHostId(el, this.triggerId);
502
+ this.#triggerEl.set(el);
503
+ }
504
+ unregisterTrigger(el) {
505
+ if (this.#triggerEl() === el) {
506
+ this.#triggerEl.set(null);
507
+ }
508
+ }
509
+ registerAnchor(el) {
510
+ const current = this.#anchorEl();
511
+ if (current !== null && current !== el) {
512
+ throw new Error('[forty-cdk/date-picker] Multiple [forDatePickerAnchor] inside the same [forDatePicker]; only one is allowed.');
513
+ }
514
+ this.#anchorEl.set(el);
515
+ }
516
+ unregisterAnchor(el) {
517
+ if (this.#anchorEl() === el) {
518
+ this.#anchorEl.set(null);
519
+ }
520
+ }
521
+ registerContent(el) {
522
+ adoptHostId(el, this.contentId);
523
+ this.#contentEl.set(el);
524
+ }
525
+ unregisterContent(el) {
526
+ if (this.#contentEl() === el) {
527
+ this.#contentEl.set(null);
528
+ }
529
+ }
530
+ toggle() {
531
+ if (this.effectiveDisabled()) {
532
+ return;
533
+ }
534
+ this.open.update((v) => !v);
535
+ }
536
+ close() {
537
+ this.markTouched();
538
+ this.open.set(false);
539
+ }
540
+ markTouched() {
541
+ super.markTouched();
542
+ }
543
+ focusCalendarCell() {
544
+ return this.calendar()?.focusActiveCell() ?? false;
545
+ }
546
+ // Both the anchored (`injectOverlayShell`) and modal (`injectModalShell`)
547
+ // surfaces now own the shared `#pendingOutsideVeto` reuse between the
548
+ // specific outside channels and the composite `interactOutside`, so this
549
+ // directive only forwards the shell-built veto to the matching `output()`
550
+ // and owns the close decision via `requestClose`. Escape is the one channel
551
+ // this directive still owns end-to-end on the anchored path (the modal path
552
+ // routes it through `emitEscapeKeyDown` on the shell, which builds the veto
553
+ // and calls `requestClose` itself).
554
+ /**
555
+ * Anchored-path Escape. Builds the veto, emits `(escapeKeyDown)`, and —
556
+ * unless vetoed and `dismissible` is off — stops propagation and closes.
557
+ */
558
+ emitEscapeKeyDown(event) {
559
+ const veto = createVetoableNativeEvent(event);
560
+ this.escapeKeyDown.emit(veto);
561
+ if (!veto.defaultPrevented && this.dismissible()) {
562
+ event.stopPropagation();
563
+ this.close();
564
+ }
565
+ }
566
+ /**
567
+ * Outside-interaction emit forwarders shared by both shells. The shell
568
+ * builds and reuses the veto; these only fire the matching output.
569
+ */
570
+ emitPointerDownOutside(veto) {
571
+ this.pointerDownOutside.emit(veto);
572
+ }
573
+ emitFocusOutside(veto) {
574
+ this.focusOutside.emit(veto);
575
+ }
576
+ emitInteractOutside(veto) {
577
+ this.interactOutside.emit(veto);
578
+ }
579
+ /** Modal-path Escape forwarder: emit only; the modal shell owns the close. */
580
+ forwardEscapeKeyDown(veto) {
581
+ this.escapeKeyDown.emit(veto);
582
+ }
583
+ /**
584
+ * Implicit close requested by either shell after an un-vetoed outside
585
+ * interaction (or, on the modal path, an un-vetoed Escape). Marks the
586
+ * control touched and closes.
587
+ */
588
+ requestClose() {
589
+ this.close();
590
+ }
591
+ emitAutoFocusOnOpen() {
592
+ return emitVetoableEvent(this.autoFocusOnOpen);
593
+ }
594
+ emitAutoFocusOnClose() {
595
+ return emitVetoableEvent(this.autoFocusOnClose);
596
+ }
597
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePicker, deps: [], target: i0.ɵɵFactoryTarget.Directive });
598
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.2", type: ForDatePicker, isStandalone: true, selector: "[forDatePicker]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, isDateUnavailable: { classPropertyName: "isDateUnavailable", publicName: "isDateUnavailable", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null }, granularity: { classPropertyName: "granularity", publicName: "granularity", isSignal: true, isRequired: false, transformFunction: null }, hourCycle: { classPropertyName: "hourCycle", publicName: "hourCycle", isSignal: true, isRequired: false, transformFunction: null }, modal: { classPropertyName: "modal", publicName: "modal", 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 }, formatOptions: { classPropertyName: "formatOptions", publicName: "formatOptions", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, rangeSeparator: { classPropertyName: "rangeSeparator", publicName: "rangeSeparator", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, sideOffset: { classPropertyName: "sideOffset", 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 }, collisionPadding: { classPropertyName: "collisionPadding", publicName: "collisionPadding", 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 }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, _dirInput: { classPropertyName: "_dirInput", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", open: "openChange", range: "rangeChange", escapeKeyDown: "escapeKeyDown", pointerDownOutside: "pointerDownOutside", focusOutside: "focusOutside", interactOutside: "interactOutside", autoFocusOnOpen: "autoFocusOnOpen", autoFocusOnClose: "autoFocusOnClose" }, host: { properties: { "attr.dir": "dir()", "attr.data-state": "open() ? \"open\" : \"closed\"", "attr.data-disabled": "effectiveDisabled() ? \"\" : null" } }, providers: [{ provide: FOR_DATE_PICKER_CONTEXT, useExisting: ForDatePicker }], queries: [{ propertyName: "calendar", first: true, predicate: ForCalendar, descendants: true, isSignal: true }, { propertyName: "timeSource", first: true, predicate: FOR_TIME_VALUE_SOURCE, descendants: true, isSignal: true }], exportAs: ["forDatePicker"], usesInheritance: true, ngImport: i0 });
599
+ }
600
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePicker, decorators: [{
601
+ type: Directive,
602
+ args: [{
603
+ selector: '[forDatePicker]',
604
+ exportAs: 'forDatePicker',
605
+ host: {
606
+ '[attr.dir]': 'dir()',
607
+ '[attr.data-state]': 'open() ? "open" : "closed"',
608
+ '[attr.data-disabled]': 'effectiveDisabled() ? "" : null',
609
+ },
610
+ providers: [{ provide: FOR_DATE_PICKER_CONTEXT, useExisting: ForDatePicker }],
611
+ }]
612
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], isDateUnavailable: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDateUnavailable", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], granularity: [{ type: i0.Input, args: [{ isSignal: true, alias: "granularity", required: false }] }], hourCycle: [{ type: i0.Input, args: [{ isSignal: true, alias: "hourCycle", required: false }] }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], returnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "returnFocus", required: false }] }], formatOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatOptions", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], range: [{ type: i0.Input, args: [{ isSignal: true, alias: "range", required: false }] }, { type: i0.Output, args: ["rangeChange"] }], rangeSeparator: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeSeparator", required: false }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], sideOffset: [{ 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 }] }], collisionPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionPadding", 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 }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], _dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", 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"] }], calendar: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForCalendar), { ...{ descendants: true }, isSignal: true }] }], timeSource: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FOR_TIME_VALUE_SOURCE), { ...{ descendants: true }, isSignal: true }] }] } });
613
+
614
+ /**
615
+ * Optional positioning anchor. When present, `[forDatePickerContent]` is
616
+ * positioned against this element instead of `[forDatePickerTrigger]` — useful
617
+ * when the trigger lives inside a decorated field box (padding, prefix icon,
618
+ * clear / chevron buttons) and the surface should align to the visible field
619
+ * rather than the inner button.
620
+ *
621
+ * Only positioning changes: the trigger still owns `aria-controls`,
622
+ * `aria-expanded`, the click toggle, focus return on close, and its exemption
623
+ * from outside-pointer dismissal. If no anchor is registered the surface falls
624
+ * back to anchoring against the trigger, so existing usages are unaffected.
625
+ *
626
+ * A date picker's calendar has its own intrinsic width and ignores
627
+ * `--for-anchor-width`, so the practical effect is mostly start / side
628
+ * alignment to the box edge.
629
+ *
630
+ * At most one `[forDatePickerAnchor]` may be registered per `[forDatePicker]`;
631
+ * a second one throws.
632
+ *
633
+ * ```html
634
+ * <div forDatePicker [(value)]="date">
635
+ * <div forDatePickerAnchor class="field-box">
636
+ * <icon />
637
+ * <button forDatePickerTrigger><span forDatePickerValue>…</span></button>
638
+ * <button class="clear">×</button>
639
+ * </div>
640
+ * @if (open()) {
641
+ * <div forDatePickerContent>…</div>
642
+ * }
643
+ * </div>
644
+ * ```
645
+ */
646
+ class ForDatePickerAnchor {
647
+ #ctx = injectDatePickerContext('ForDatePickerAnchor');
648
+ #host = inject(ElementRef);
649
+ constructor() {
650
+ registerHandle(this.#host.nativeElement, (el) => this.#ctx.registerAnchor(el), (el) => this.#ctx.unregisterAnchor(el));
651
+ }
652
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerAnchor, deps: [], target: i0.ɵɵFactoryTarget.Directive });
653
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForDatePickerAnchor, isStandalone: true, selector: "[forDatePickerAnchor]", exportAs: ["forDatePickerAnchor"], ngImport: i0 });
654
+ }
655
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerAnchor, decorators: [{
656
+ type: Directive,
657
+ args: [{
658
+ selector: '[forDatePickerAnchor]',
659
+ exportAs: 'forDatePickerAnchor',
660
+ }]
661
+ }], ctorParameters: () => [] });
662
+
663
+ /**
664
+ * The button that opens the date-picker surface — and the focusable form
665
+ * control the picker exposes for `[formField]`. Apply on a `<button>` so
666
+ * Space / Enter dispatch native clicks that toggle via `(click)`.
667
+ *
668
+ * Wires APG Date Picker Dialog attributes: `aria-haspopup="dialog"`,
669
+ * `aria-expanded` reflecting `open()`, and `aria-controls` pointing to the
670
+ * content while open. It also reflects the root's form-control state
671
+ * (`aria-disabled` / `aria-readonly` / `aria-required` / `aria-invalid` /
672
+ * `aria-busy`) so the focusable element advertises validity to assistive tech.
673
+ *
674
+ * The trigger is exempt from the surface's dismissable layer — its own click
675
+ * toggles open/close, so an outside-pointer dismissal never races with it.
676
+ *
677
+ * The root is normally resolved via DI from the enclosing `[forDatePicker]`.
678
+ * When the trigger is declared inside an `ng-template` stamped into the root
679
+ * (e.g. via `ngTemplateOutlet`), DI resolves at the template's declaration
680
+ * site and misses the root — pass it explicitly through the selector input,
681
+ * `routerLink`-style: `[forDatePickerTrigger]="root"` with
682
+ * `#root="forDatePicker"`.
683
+ */
684
+ class ForDatePickerTrigger {
685
+ #host = inject(ElementRef);
686
+ /**
687
+ * Optional explicit reference to the `[forDatePicker]` root, named after the
688
+ * selector `routerLink`-style. The bare valueless attribute keeps resolving
689
+ * the enclosing root via DI; pass the root explicitly
690
+ * (`[forDatePickerTrigger]="root"`, with `#root="forDatePicker"`) when the
691
+ * trigger is declared in an `ng-template` stamped inside the root — DI
692
+ * resolves at the template's declaration site, so the enclosing root is
693
+ * invisible there. The empty string (what the valueless attribute yields) is
694
+ * treated as unset.
695
+ */
696
+ forDatePickerTrigger = input('', /* @ts-ignore */
697
+ ...(ngDevMode ? [{ debugName: "forDatePickerTrigger" }] : /* istanbul ignore next */ []));
698
+ ctx = injectDatePickerTriggerContext(this.forDatePickerTrigger);
699
+ constructor() {
700
+ const el = this.#host.nativeElement;
701
+ // Registration is an imperative call into the resolved root's registry,
702
+ // not state derivation — the effect only re-registers the element when the
703
+ // resolved root changes (explicit reference swapped at runtime).
704
+ effect((onCleanup) => {
705
+ const ctx = this.ctx();
706
+ ctx.registerTrigger(el);
707
+ onCleanup(() => ctx.unregisterTrigger(el));
708
+ });
709
+ reflectDisabled(computed(() => this.ctx().effectiveDisabled()));
710
+ }
711
+ onClick() {
712
+ this.ctx().toggle();
713
+ }
714
+ onFocusOut(event) {
715
+ const next = event.relatedTarget;
716
+ if (next) {
717
+ // Focus moving into the surface we just opened — not a leave.
718
+ const content = this.ctx().content();
719
+ if (content && content.contains(next)) {
720
+ return;
721
+ }
722
+ // Focus staying within the trigger's own subtree — not a leave.
723
+ if (this.#host.nativeElement.contains(next)) {
724
+ return;
725
+ }
726
+ }
727
+ this.ctx().markTouched();
728
+ }
729
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
730
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForDatePickerTrigger, isStandalone: true, selector: "[forDatePickerTrigger]", inputs: { forDatePickerTrigger: { classPropertyName: "forDatePickerTrigger", publicName: "forDatePickerTrigger", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button" }, listeners: { "click": "onClick()", "focusout": "onFocusOut($event)" }, properties: { "id": "ctx().triggerId()", "attr.aria-haspopup": "\"dialog\"", "attr.aria-expanded": "ctx().open() ? \"true\" : \"false\"", "attr.aria-controls": "ctx().open() ? ctx().contentId() : null", "attr.aria-disabled": "ctx().effectiveDisabled() ? \"true\" : null", "attr.aria-readonly": "ctx().readonly() ? \"true\" : null", "attr.aria-required": "ctx().required() ? \"true\" : null", "attr.aria-invalid": "ctx().invalid() ? \"true\" : null", "attr.aria-busy": "ctx().pending() ? \"true\" : null", "attr.data-state": "ctx().open() ? \"open\" : \"closed\"", "attr.data-disabled": "ctx().effectiveDisabled() ? \"\" : null" } }, exportAs: ["forDatePickerTrigger"], ngImport: i0 });
731
+ }
732
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerTrigger, decorators: [{
733
+ type: Directive,
734
+ args: [{
735
+ selector: '[forDatePickerTrigger]',
736
+ exportAs: 'forDatePickerTrigger',
737
+ host: {
738
+ type: 'button',
739
+ '[id]': 'ctx().triggerId()',
740
+ '[attr.aria-haspopup]': '"dialog"',
741
+ '[attr.aria-expanded]': 'ctx().open() ? "true" : "false"',
742
+ '[attr.aria-controls]': 'ctx().open() ? ctx().contentId() : null',
743
+ '[attr.aria-disabled]': 'ctx().effectiveDisabled() ? "true" : null',
744
+ '[attr.aria-readonly]': 'ctx().readonly() ? "true" : null',
745
+ '[attr.aria-required]': 'ctx().required() ? "true" : null',
746
+ '[attr.aria-invalid]': 'ctx().invalid() ? "true" : null',
747
+ '[attr.aria-busy]': 'ctx().pending() ? "true" : null',
748
+ '[attr.data-state]': 'ctx().open() ? "open" : "closed"',
749
+ '[attr.data-disabled]': 'ctx().effectiveDisabled() ? "" : null',
750
+ '(click)': 'onClick()',
751
+ '(focusout)': 'onFocusOut($event)',
752
+ },
753
+ }]
754
+ }], ctorParameters: () => [], propDecorators: { forDatePickerTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "forDatePickerTrigger", required: false }] }] } });
755
+
756
+ /**
757
+ * The date-picker surface. Carries `role="dialog"`, is portaled to
758
+ * `document.body`, and wraps the projected `ForCalendar`. It defers all of its
759
+ * lifecycle to one of the two shared shells, picked once on construction from
760
+ * `[forDatePicker].modal`:
761
+ *
762
+ * - **non-modal (default)** — `injectOverlayShell` anchors the surface to the
763
+ * trigger via `@floating-ui/dom`, activates a `DismissableLayer` (Escape,
764
+ * pointer-down outside, focus outside), moves focus to the calendar's roving
765
+ * cell on open, and returns focus to the trigger on close.
766
+ * - **modal** — `injectModalShell` traps focus, inerts the background, and
767
+ * locks body scroll (a centered dialog the consumer positions with CSS, not
768
+ * trigger-anchored). Same Escape / outside-pointer dismiss and return-focus.
769
+ *
770
+ * The mode is read in the constructor rather than reactively because the two
771
+ * shells are structurally different and switching at runtime would require a
772
+ * remount. This is reliable: the surface mounts lazily (the consumer wraps it
773
+ * with `@if (open())`), well after the root's `modal` input has settled.
774
+ *
775
+ * Mount/unmount of the surface is the consumer's responsibility — wrap with
776
+ * `@if (open())` so `animate.enter` / `animate.leave` fire on the natural
777
+ * mount cycle.
778
+ */
779
+ class ForDatePickerContent {
780
+ ctx = injectDatePickerContext('ForDatePickerContent');
781
+ #host = inject(ElementRef);
782
+ constructor() {
783
+ const ctx = this.ctx;
784
+ registerHandle(this.#host.nativeElement, (el) => ctx.registerContent(el), (el) => ctx.unregisterContent(el));
785
+ if (ctx.modal()) {
786
+ injectModalShell({
787
+ modal: ctx.modal,
788
+ returnFocus: ctx.returnFocus,
789
+ // Land on the calendar's active cell with a first-focusable fallback,
790
+ // matching the non-modal path below — both modes focus the same cell.
791
+ initialFocus: {
792
+ move: () => ctx.focusCalendarCell(),
793
+ veto: () => ctx.emitAutoFocusOnOpen(),
794
+ },
795
+ autoFocusOnOpen: () => (event) => {
796
+ if (ctx.emitAutoFocusOnOpen()) {
797
+ event.preventDefault();
798
+ }
799
+ },
800
+ autoFocusOnClose: () => (event) => {
801
+ if (ctx.emitAutoFocusOnClose()) {
802
+ event.preventDefault();
803
+ }
804
+ },
805
+ dismiss: {
806
+ dismissible: ctx.dismissible,
807
+ requestClose: () => ctx.requestClose(),
808
+ emitEscapeKeyDown: (veto) => ctx.forwardEscapeKeyDown(veto),
809
+ emitPointerDownOutside: (veto) => ctx.emitPointerDownOutside(veto),
810
+ emitFocusOutside: (veto) => ctx.emitFocusOutside(veto),
811
+ emitInteractOutside: (veto) => ctx.emitInteractOutside(veto),
812
+ },
813
+ });
814
+ return;
815
+ }
816
+ injectOverlayShell({
817
+ positioner: {
818
+ kind: 'floating',
819
+ reference: ctx.reference,
820
+ open: ctx.open,
821
+ side: ctx.side,
822
+ align: ctx.align,
823
+ sideOffset: ctx.sideOffset,
824
+ alignOffset: ctx.alignOffset,
825
+ avoidCollisions: ctx.avoidCollisions,
826
+ collisionPadding: ctx.collisionPadding,
827
+ sticky: ctx.sticky,
828
+ hideWhenDetached: ctx.hideWhenDetached,
829
+ clipUntilPositioned: ctx.clipUntilPositioned,
830
+ },
831
+ dismiss: {
832
+ dismissible: ctx.dismissible,
833
+ requestClose: () => ctx.requestClose(),
834
+ emitEscapeKeyDown: (event) => ctx.emitEscapeKeyDown(event),
835
+ emitPointerDownOutside: (veto) => ctx.emitPointerDownOutside(veto),
836
+ emitFocusOutside: (veto) => ctx.emitFocusOutside(veto),
837
+ emitInteractOutside: (veto) => ctx.emitInteractOutside(veto),
838
+ // Trigger is exempt — its own click toggles open/close; without
839
+ // exemption pointer-down-outside would race and double-close.
840
+ exemptElements: () => {
841
+ const trigger = ctx.trigger();
842
+ return trigger ? [trigger] : [];
843
+ },
844
+ },
845
+ // Move focus to the calendar's roving cell on open; the shell falls back
846
+ // to the first focusable descendant when no cell is found.
847
+ initialFocus: {
848
+ move: () => ctx.focusCalendarCell(),
849
+ veto: () => ctx.emitAutoFocusOnOpen(),
850
+ },
851
+ returnFocus: {
852
+ enabled: ctx.returnFocus,
853
+ target: () => ctx.trigger(),
854
+ veto: () => ctx.emitAutoFocusOnClose(),
855
+ },
856
+ });
857
+ }
858
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
859
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForDatePickerContent, isStandalone: true, selector: "[forDatePickerContent]", host: { attributes: { "role": "dialog", "tabindex": "-1" }, properties: { "id": "ctx.contentId()", "attr.aria-label": "ctx.ariaLabel()", "attr.aria-labelledby": "ctx.ariaLabel() ? null : ctx.triggerId()", "attr.aria-modal": "ctx.modal() ? \"true\" : null", "attr.data-state": "ctx.open() ? \"open\" : \"closed\"" } }, exportAs: ["forDatePickerContent"], ngImport: i0 });
860
+ }
861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerContent, decorators: [{
862
+ type: Directive,
863
+ args: [{
864
+ selector: '[forDatePickerContent]',
865
+ exportAs: 'forDatePickerContent',
866
+ host: {
867
+ role: 'dialog',
868
+ tabindex: '-1',
869
+ '[id]': 'ctx.contentId()',
870
+ '[attr.aria-label]': 'ctx.ariaLabel()',
871
+ '[attr.aria-labelledby]': 'ctx.ariaLabel() ? null : ctx.triggerId()',
872
+ '[attr.aria-modal]': 'ctx.modal() ? "true" : null',
873
+ '[attr.data-state]': 'ctx.open() ? "open" : "closed"',
874
+ },
875
+ }]
876
+ }], ctorParameters: () => [] });
877
+
878
+ /**
879
+ * Renders the selected date — formatted through the adapter with the root's
880
+ * `formatOptions` — or the configured placeholder when nothing is selected,
881
+ * into its host element via `textContent`. Apply on a `<span>` (or any inline
882
+ * element) inside `[forDatePickerTrigger]`:
883
+ *
884
+ * ```html
885
+ * <button forDatePickerTrigger>
886
+ * <span forDatePickerValue placeholder="Pick a date…"></span>
887
+ * </button>
888
+ * ```
889
+ *
890
+ * For fully custom rendering, drop this directive and read
891
+ * `forDatePicker.formattedValue()` / `forDatePicker.value()` from your template.
892
+ */
893
+ class ForDatePickerValue {
894
+ #ctx = injectDatePickerContext('ForDatePickerValue');
895
+ /** Text shown when no date is selected. Falls back to `[forDatePicker][placeholder]`. */
896
+ placeholder = input('', /* @ts-ignore */
897
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
898
+ isPlaceholder = computed(() => this.#ctx.formattedValue() === null, /* @ts-ignore */
899
+ ...(ngDevMode ? [{ debugName: "isPlaceholder" }] : /* istanbul ignore next */ []));
900
+ displayText = computed(() => {
901
+ const formatted = this.#ctx.formattedValue();
902
+ if (formatted !== null) {
903
+ return formatted;
904
+ }
905
+ return this.placeholder() || this.#ctx.placeholder();
906
+ }, /* @ts-ignore */
907
+ ...(ngDevMode ? [{ debugName: "displayText" }] : /* istanbul ignore next */ []));
908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerValue, deps: [], target: i0.ɵɵFactoryTarget.Directive });
909
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForDatePickerValue, isStandalone: true, selector: "[forDatePickerValue]", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "textContent": "displayText()", "attr.data-placeholder": "isPlaceholder() ? \"\" : null" } }, exportAs: ["forDatePickerValue"], ngImport: i0 });
910
+ }
911
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDatePickerValue, decorators: [{
912
+ type: Directive,
913
+ args: [{
914
+ selector: '[forDatePickerValue]',
915
+ exportAs: 'forDatePickerValue',
916
+ host: {
917
+ '[textContent]': 'displayText()',
918
+ '[attr.data-placeholder]': 'isPlaceholder() ? "" : null',
919
+ },
920
+ }]
921
+ }], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }] } });
922
+
923
+ /**
924
+ * Exact public names of every `ForDatePicker` input, its models included. Spread it into the
925
+ * `inputs` array of a `hostDirectives` entry so a wrapper component re-exposes the
926
+ * primitive's full surface — the Signal Forms members `[formField]` binds among them —
927
+ * without hand-maintaining the list. Always spread into an inline object literal as shown
928
+ * below: the literal is what keeps the entry statically analyzable for consumers compiling
929
+ * against the published package. An anti-drift spec fails when this list no longer matches
930
+ * the directive's actual API. See `docs/wrapping-form-primitives.md` for both supported
931
+ * wrapping patterns.
932
+ *
933
+ * @example
934
+ * ```ts
935
+ * @Component({
936
+ * selector: 'div[myDatePicker]',
937
+ * template: '',
938
+ * hostDirectives: [
939
+ * {
940
+ * directive: ForDatePicker,
941
+ * inputs: [...FOR_DATE_PICKER_HOST_DIRECTIVE_INPUTS],
942
+ * outputs: [...FOR_DATE_PICKER_HOST_DIRECTIVE_OUTPUTS],
943
+ * },
944
+ * ],
945
+ * })
946
+ * export class MyDatePicker {}
947
+ * ```
948
+ */
949
+ const FOR_DATE_PICKER_HOST_DIRECTIVE_INPUTS = [
950
+ 'value',
951
+ 'open',
952
+ 'range',
953
+ 'align',
954
+ 'alignOffset',
955
+ 'ariaLabel',
956
+ 'avoidCollisions',
957
+ 'clipUntilPositioned',
958
+ 'closeOnSelect',
959
+ 'collisionPadding',
960
+ 'dir',
961
+ 'dirty',
962
+ 'disabled',
963
+ 'dismissible',
964
+ 'errors',
965
+ 'formatOptions',
966
+ 'granularity',
967
+ 'hideWhenDetached',
968
+ 'hourCycle',
969
+ 'invalid',
970
+ 'isDateUnavailable',
971
+ 'maxDate',
972
+ 'minDate',
973
+ 'modal',
974
+ 'name',
975
+ 'pending',
976
+ 'placeholder',
977
+ 'rangeSeparator',
978
+ 'readonly',
979
+ 'required',
980
+ 'returnFocus',
981
+ 'selectionMode',
982
+ 'side',
983
+ 'sideOffset',
984
+ 'sticky',
985
+ 'touched',
986
+ ];
987
+ /**
988
+ * Exact public names of every `ForDatePicker` output, the Signal Forms `touch` output
989
+ * included. Spread it into the `outputs` array of the same `hostDirectives` entry as
990
+ * {@link FOR_DATE_PICKER_HOST_DIRECTIVE_INPUTS}.
991
+ */
992
+ const FOR_DATE_PICKER_HOST_DIRECTIVE_OUTPUTS = [
993
+ 'valueChange',
994
+ 'openChange',
995
+ 'rangeChange',
996
+ 'escapeKeyDown',
997
+ 'pointerDownOutside',
998
+ 'focusOutside',
999
+ 'interactOutside',
1000
+ 'autoFocusOnOpen',
1001
+ 'autoFocusOnClose',
1002
+ 'touchedChange',
1003
+ 'touch',
1004
+ ];
1005
+
1006
+ /**
1007
+ * Generated bundle index. Do not edit.
1008
+ */
1009
+
1010
+ export { FOR_DATE_PICKER_CONTEXT, FOR_DATE_PICKER_DEFAULTS, FOR_DATE_PICKER_HOST_DIRECTIVE_INPUTS, FOR_DATE_PICKER_HOST_DIRECTIVE_OUTPUTS, ForDatePicker, ForDatePickerAnchor, ForDatePickerContent, ForDatePickerTrigger, ForDatePickerValue, provideForDatePickerDefaults };
1011
+ //# sourceMappingURL=forty-cdk-date-picker.mjs.map