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,744 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, ElementRef, model, input, computed, linkedSignal, effect, Directive } from '@angular/core';
3
+ import { createDefaults, FormUiControlBase, injectDateAdapter, injectTextDirection, RovingTabindex, resolveHourCycle, dayPeriodNames, SegmentEditor, injectHiddenInput, serializeISODate, assertTimeCapable, clampToBounds, ForDateTimeSegmentBase, ForDateTimeLiteralBase } from 'forty-cdk/core';
4
+
5
+ const DIGITS = {
6
+ day: 2,
7
+ month: 2,
8
+ year: 4,
9
+ hour: 2,
10
+ minute: 2,
11
+ second: 2,
12
+ dayPeriod: 0,
13
+ };
14
+ /**
15
+ * Derives the ordered segment list for a day-granularity date field from the
16
+ * runtime locale, using `Intl.DateTimeFormat(...).formatToParts`. The visible
17
+ * order of day / month / year (and the separator characters between them)
18
+ * follows the locale — `MM/DD/YYYY` for `en-US`, `DD.MM.YYYY` for `de-DE`,
19
+ * `YYYY/MM/DD` for `ja-JP` — so the field never has to parse ambiguous
20
+ * free text.
21
+ *
22
+ * Pure: the same `locale` always yields the same list, and it touches no
23
+ * signals or DOM. A fixed reference date with distinct day / month values is
24
+ * formatted purely to read back the part order; its actual value never
25
+ * surfaces.
26
+ *
27
+ * @param locale BCP 47 locale, or `undefined` for the runtime default.
28
+ */
29
+ function buildSegments(locale) {
30
+ const parts = new Intl.DateTimeFormat(locale, {
31
+ year: 'numeric',
32
+ month: '2-digit',
33
+ day: '2-digit',
34
+ }).formatToParts(REFERENCE_DATE);
35
+ const segments = [];
36
+ for (const part of parts) {
37
+ switch (part.type) {
38
+ case 'day':
39
+ case 'month':
40
+ case 'year':
41
+ segments.push({ kind: 'editable', type: part.type, digits: DIGITS[part.type] });
42
+ break;
43
+ case 'literal':
44
+ segments.push({ kind: 'literal', literal: part.value });
45
+ break;
46
+ default:
47
+ break;
48
+ }
49
+ }
50
+ return segments;
51
+ }
52
+ // 1 Feb is deliberately distinct in day vs month so a locale that reuses a
53
+ // digit can't mask the part ordering. The year (2000) is irrelevant — only the
54
+ // `type` of each formatted part is read.
55
+ const REFERENCE_DATE = new Date(2000, 1, 1);
56
+ // 1 Feb 2000, 1:23:45 PM — distinct day / month and hour / minute / second, in
57
+ // the afternoon so the `dayPeriod` part is present to read back.
58
+ const REFERENCE_DATE_TIME = new Date(2000, 1, 1, 13, 23, 45);
59
+ /**
60
+ * Derives the ordered segment list for a date(-time) field. At
61
+ * `granularity === 'day'` this is exactly {@link buildSegments} (date-only,
62
+ * byte-for-byte). At a finer granularity it appends the locale-ordered time
63
+ * segments (and the date↔time separator) by formatting a reference date-time
64
+ * with both date and time options in a single `Intl.DateTimeFormat` pass, so
65
+ * the order of every part — and whether an AM/PM `dayPeriod` is present —
66
+ * follows the locale and the resolved hour cycle.
67
+ *
68
+ * Pure: the same arguments always yield the same list.
69
+ *
70
+ * @param locale BCP 47 locale, or `undefined` for the runtime default.
71
+ * @param granularity Smallest editable unit; `'day'` stays date-only.
72
+ * @param hourCycle Resolved hour cycle (`12` shows AM/PM, `24` does not).
73
+ */
74
+ function buildDateTimeSegments(locale, granularity, hourCycle) {
75
+ if (granularity === 'day') {
76
+ return buildSegments(locale);
77
+ }
78
+ const options = {
79
+ year: 'numeric',
80
+ month: '2-digit',
81
+ day: '2-digit',
82
+ hour: '2-digit',
83
+ hour12: hourCycle === 12,
84
+ };
85
+ if (granularity !== 'hour') {
86
+ options.minute = '2-digit';
87
+ }
88
+ if (granularity === 'second') {
89
+ options.second = '2-digit';
90
+ }
91
+ const parts = new Intl.DateTimeFormat(locale, options).formatToParts(REFERENCE_DATE_TIME);
92
+ const segments = [];
93
+ for (const part of parts) {
94
+ switch (part.type) {
95
+ case 'day':
96
+ case 'month':
97
+ case 'year':
98
+ case 'hour':
99
+ case 'minute':
100
+ case 'second':
101
+ case 'dayPeriod':
102
+ segments.push({ kind: 'editable', type: part.type, digits: DIGITS[part.type] });
103
+ break;
104
+ case 'literal':
105
+ segments.push({ kind: 'literal', literal: part.value });
106
+ break;
107
+ default:
108
+ break;
109
+ }
110
+ }
111
+ return segments;
112
+ }
113
+
114
+ /**
115
+ * Injection token for the {@link ForDateFieldContext} provided by
116
+ * `[forDateField]`. Segment and literal children inject it to coordinate with
117
+ * the root.
118
+ */
119
+ const FOR_DATE_FIELD_CONTEXT = new InjectionToken('FOR_DATE_FIELD_CONTEXT');
120
+ /**
121
+ * Resolves the surrounding {@link ForDateFieldContext}, throwing a descriptive,
122
+ * primitive-prefixed error when used outside a `[forDateField]`.
123
+ *
124
+ * @param piece Name of the calling directive, used in the error message.
125
+ */
126
+ function injectDateFieldContext(piece) {
127
+ const ctx = inject(FOR_DATE_FIELD_CONTEXT, { optional: true });
128
+ if (!ctx) {
129
+ throw new Error(`[forty-cdk/date-field] ${piece} must be used inside a [forDateField].`);
130
+ }
131
+ return ctx;
132
+ }
133
+
134
+ /**
135
+ * Library default accessible name for each editable segment. Used when neither
136
+ * the segment's explicit `ariaLabel` nor a `provideForDateFieldDefaults`
137
+ * override supplies one for that part.
138
+ */
139
+ const DEFAULT_DATE_FIELD_SEGMENT_LABELS = {
140
+ day: 'day',
141
+ month: 'month',
142
+ year: 'year',
143
+ hour: 'hour',
144
+ minute: 'minute',
145
+ second: 'second',
146
+ dayPeriod: 'AM/PM',
147
+ };
148
+ const FALLBACK = {
149
+ emptySegmentText: 'Empty',
150
+ segmentLabels: DEFAULT_DATE_FIELD_SEGMENT_LABELS,
151
+ };
152
+ const { token, provideDefaults } = createDefaults('FOR_DATE_FIELD_DEFAULTS', FALLBACK);
153
+ /** Token holding the resolved date-field defaults for the current scope. */
154
+ const FOR_DATE_FIELD_DEFAULTS = token;
155
+ /**
156
+ * Configures forty-cdk date-field defaults for this injector scope. Partial
157
+ * overrides inherit unspecified keys from the parent scope (or library
158
+ * defaults at the root).
159
+ */
160
+ function provideForDateFieldDefaults(defaults = {}) {
161
+ return provideDefaults(defaults);
162
+ }
163
+
164
+ /**
165
+ * Year used to resolve day ranges (Feb length) while the year segment is empty.
166
+ * Deliberately a common (non-leap) year so an empty-year February reports
167
+ * `aria-valuemax="28"` rather than briefly jumping to 29.
168
+ */
169
+ const RESOLVER_YEAR = 2001;
170
+ /**
171
+ * Headless, segmented, spin-editable date input — the keyboard-first
172
+ * counterpart to `ForCalendar`. There is no single WAI-ARIA APG pattern for a
173
+ * date field; it is a composition of
174
+ * [Spinbuttons](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/) inside a
175
+ * labelled `role="group"`.
176
+ * Each day / month / year part is an independent `role="spinbutton"` segment
177
+ * (`[forDateFieldSegment]`) so entry is unambiguous and locale-correct — no
178
+ * free-text parsing, no `03/04`-is-it-March-4th guesswork.
179
+ *
180
+ * `ForDateField` is the root: it owns the entered parts, composes them into the
181
+ * adapter's date type, resolves the locale-ordered segment list, and exposes
182
+ * everything to the segment / literal children through
183
+ * {@link FOR_DATE_FIELD_CONTEXT}. The spin-button engine (digit typing, stepping,
184
+ * Home/End, RTL focus moves, the segment registry) lives in the shared
185
+ * `_internal/datetime` {@link SegmentEditor}; the root supplies only the
186
+ * date-specific bits (segment bounds, the today-seed, the month-name
187
+ * `aria-valuetext`, and the create/clamp compose). All date math goes through
188
+ * the pluggable {@link DateAdapter} shared with `ForCalendar`
189
+ * (`provideInternationalizedDateAdapter()` / `provideNativeDateAdapter()`), so
190
+ * the field hard-depends on no date library.
191
+ *
192
+ * Set `granularity` coarser-than-a-day off (`'hour'` / `'minute'` / `'second'`)
193
+ * to make it a **date-time field**: time segments (hour / minute / second and,
194
+ * in 12-hour mode, an AM·PM `dayPeriod`) are appended after the date segments in
195
+ * the same `role="group"`, sharing the one roving tab stop. This needs a
196
+ * time-capable adapter (`provideNativeDateAdapter()` /
197
+ * `provideInternationalizedDateTimeAdapter()`); `granularity = 'day'` (default)
198
+ * is unchanged and works with any adapter.
199
+ *
200
+ * It implements `FormValueControl<D | null>` from `@angular/forms/signals`, so
201
+ * it auto-wires with `[formField]` and auto-associates inside a `[forField]`.
202
+ * The value stays `null` until every visible segment is filled.
203
+ *
204
+ * @typeParam D The adapter's immutable date (or, with `granularity > 'day'`,
205
+ * date-time) type.
206
+ *
207
+ * Note: the date bounds are named `minDate` / `maxDate`, not `min` / `max` —
208
+ * the latter are reserved `FormUiControl` members typed `number | undefined`
209
+ * for numeric validators, so a date-typed `min` / `max` would break the
210
+ * `FormValueControl` contract.
211
+ *
212
+ * @example
213
+ * ```html
214
+ * <div forDateField [(value)]="date" [minDate]="min" [maxDate]="max" name="dob"
215
+ * [ariaLabel]="'Date of birth'" #field="forDateField">
216
+ * @for (seg of field.segments(); track seg.id) {
217
+ * @if (seg.isLiteral) {
218
+ * <span forDateFieldLiteral>{{ seg.text }}</span>
219
+ * } @else {
220
+ * <span forDateFieldSegment [segment]="seg.type!">{{ seg.text }}</span>
221
+ * }
222
+ * }
223
+ * </div>
224
+ * ```
225
+ */
226
+ class ForDateField extends FormUiControlBase {
227
+ #host = inject(ElementRef);
228
+ #defaults = inject(FOR_DATE_FIELD_DEFAULTS);
229
+ /** The active date adapter, resolved from `FOR_DATE_ADAPTER` (shared with `ForCalendar`). */
230
+ adapter = injectDateAdapter('ForDateField');
231
+ /**
232
+ * Two-way bindable entered date, or `null` while any segment is empty.
233
+ * Required by `FormValueControl<D | null>`. The `model()` change emitter
234
+ * (`(valueChange)`) fires only when the field itself composes a new value,
235
+ * never on consumer writes via `[(value)]`.
236
+ */
237
+ value = model(null, /* @ts-ignore */
238
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
239
+ /**
240
+ * Minimum selectable date (inclusive). A composed value below it is clamped
241
+ * up. The comparison is by the **full instant** (`DateAdapter.compare`), so at
242
+ * `granularity > 'day'` a non-midnight `minDate` clamps the time too, and the
243
+ * result is identical across time-capable adapters. Named `minDate` (not
244
+ * `min`) because `FormUiControl.min` is reserved for a numeric validator bound
245
+ * by `[formField]`.
246
+ */
247
+ minDate = input(null, /* @ts-ignore */
248
+ ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
249
+ /**
250
+ * Maximum selectable date (inclusive). A composed value above it is clamped
251
+ * down. Compared by the **full instant** like {@link minDate}. Named `maxDate`
252
+ * (not `max`) for the same reason as {@link minDate}.
253
+ */
254
+ maxDate = input(null, /* @ts-ignore */
255
+ ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
256
+ /**
257
+ * Date-time precision. `'day'` (default, **non-breaking**) is a pure date
258
+ * field. `'hour'` / `'minute'` / `'second'` append the matching time segments
259
+ * and the value carries a time component — which requires a time-capable
260
+ * adapter (`provideNativeDateAdapter()` /
261
+ * `provideInternationalizedDateTimeAdapter()`).
262
+ */
263
+ granularity = input('day', /* @ts-ignore */
264
+ ...(ngDevMode ? [{ debugName: "granularity" }] : /* istanbul ignore next */ []));
265
+ /**
266
+ * 12- or 24-hour cycle for the time segments. When `null` (default) it is
267
+ * derived from the locale. A 12-hour cycle adds the AM/PM `dayPeriod` segment.
268
+ * Only meaningful when `granularity > 'day'`.
269
+ */
270
+ hourCycle = input(null, /* @ts-ignore */
271
+ ...(ngDevMode ? [{ debugName: "hourCycle" }] : /* istanbul ignore next */ []));
272
+ /** BCP 47 locale driving segment order and separators. Defaults to the runtime locale. */
273
+ locale = input(null, /* @ts-ignore */
274
+ ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
275
+ /**
276
+ * Per-segment placeholder shown while empty. Unspecified parts fall back to a
277
+ * letter-repeat default (`dd` / `mm` / `yyyy` / `hh` / `mm` / `ss` / `--`).
278
+ */
279
+ placeholder = input({}, /* @ts-ignore */
280
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
281
+ /** Accessible name for the field group. Emits no `aria-label` while `null`. */
282
+ ariaLabel = input(null, /* @ts-ignore */
283
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
284
+ /**
285
+ * Writing direction. When unset (default `null`), the inherited ambient
286
+ * direction is resolved from the nearest ancestor carrying a `dir` attribute
287
+ * (or `<html dir>`), defaulting to `'ltr'`. An explicit `[dir]` always wins.
288
+ * The resolved value is reflected to the host `dir` attribute and mirrors the
289
+ * ArrowLeft / ArrowRight segment navigation in RTL.
290
+ */
291
+ _dirInput = input(null, { ...(ngDevMode ? { debugName: "_dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
292
+ dir = injectTextDirection(this._dirInput);
293
+ /** Shared roving-tabindex tracker: exactly one segment owns `tabindex=0`. */
294
+ roving = new RovingTabindex();
295
+ /** Resolved hour cycle (`12` shows AM/PM, `24` does not). */
296
+ #cycle = computed(() => resolveHourCycle(this.locale() ?? undefined, this.hourCycle()), /* @ts-ignore */
297
+ ...(ngDevMode ? [{ debugName: "#cycle" }] : /* istanbul ignore next */ []));
298
+ #specs = computed(() => buildDateTimeSegments(this.locale() ?? undefined, this.granularity(), this.#cycle()), /* @ts-ignore */
299
+ ...(ngDevMode ? [{ debugName: "#specs" }] : /* istanbul ignore next */ []));
300
+ #editableOrder = computed(() => this.#specs()
301
+ .filter((spec) => spec.kind === 'editable')
302
+ .map((spec) => spec.type), /* @ts-ignore */
303
+ ...(ngDevMode ? [{ debugName: "#editableOrder" }] : /* istanbul ignore next */ []));
304
+ #periodNames = computed(() => dayPeriodNames(this.locale() ?? undefined), /* @ts-ignore */
305
+ ...(ngDevMode ? [{ debugName: "#periodNames" }] : /* istanbul ignore next */ []));
306
+ /**
307
+ * The entered parts. A `linkedSignal` keyed on `value`: a non-null write
308
+ * (consumer, `[formField]`, or our own compose) rehydrates the segments from
309
+ * the date. A `null` transition is disambiguated by the prior parts: an
310
+ * *internal* edit clearing one segment always leaves the others, so
311
+ * `previous` still carries a filled part and is preserved (clearing the day
312
+ * never wipes the month / year); an *external* reset of a complete value
313
+ * leaves `previous` fully filled, so the field clears.
314
+ */
315
+ #parts = linkedSignal({ ...(ngDevMode ? { debugName: "#parts" } : /* istanbul ignore next */ {}), source: this.value,
316
+ computation: (current, previous) => {
317
+ if (current !== null) {
318
+ const parts = {
319
+ day: this.adapter.getDate(current),
320
+ month: this.adapter.getMonth(current),
321
+ year: this.adapter.getYear(current),
322
+ hour: null,
323
+ minute: null,
324
+ second: null,
325
+ };
326
+ if (this.granularity() !== 'day') {
327
+ const time = this.#time();
328
+ parts.hour = time.getHours(current);
329
+ parts.minute = time.getMinutes(current);
330
+ parts.second = time.getSeconds(current);
331
+ }
332
+ return parts;
333
+ }
334
+ const prior = previous?.value;
335
+ if (prior && this.#someEditableEmpty(prior)) {
336
+ return prior;
337
+ }
338
+ return { day: null, month: null, year: null, hour: null, minute: null, second: null };
339
+ } });
340
+ #editor = new SegmentEditor({
341
+ disabled: this.effectiveDisabled,
342
+ readonly: this.readonly,
343
+ roving: this.roving,
344
+ cycle: this.#cycle,
345
+ specs: this.#specs,
346
+ editableOrder: this.#editableOrder,
347
+ periodNames: this.#periodNames,
348
+ parts: () => this.#parts(),
349
+ segmentMin: (type) => this.segmentMin(type),
350
+ segmentMax: (type) => this.segmentMax(type),
351
+ seed: (type) => this.#seed(type),
352
+ placeholderFor: (type) => this.#placeholderFor(type),
353
+ valueText: (type) => this.#valueText(type),
354
+ commit: (next) => this.#commitParts(next),
355
+ });
356
+ /**
357
+ * The ordered, locale-derived segments (editable + literals) to render. Each
358
+ * entry carries the text to display: the formatted value when filled, the
359
+ * placeholder while empty, or the literal separator.
360
+ */
361
+ segments = this.#editor.segments;
362
+ constructor() {
363
+ super();
364
+ injectHiddenInput({
365
+ name: this.name,
366
+ values: computed(() => {
367
+ const current = this.value();
368
+ if (current === null) {
369
+ return [];
370
+ }
371
+ return [serializeISODate(this.adapter, current, this.granularity(), 'ForDateField')];
372
+ }),
373
+ disabled: this.effectiveDisabled,
374
+ });
375
+ // Eager validation: a date-time field needs a time-capable adapter. Fail
376
+ // loudly as soon as the granularity input settles. The throw is raised
377
+ // during change detection (inside this `effect`) and propagates through
378
+ // Angular's error handling, so a day-only adapter misconfiguration is
379
+ // surfaced — never silently swallowed.
380
+ effect(() => {
381
+ if (this.granularity() !== 'day') {
382
+ this.#time();
383
+ }
384
+ });
385
+ }
386
+ segmentValue(type) {
387
+ return this.#editor.segmentValue(type);
388
+ }
389
+ segmentMin(type) {
390
+ if (type === 'hour') {
391
+ return this.#cycle() === 12 ? 1 : 0;
392
+ }
393
+ if (type === 'minute' || type === 'second' || type === 'dayPeriod') {
394
+ return 0;
395
+ }
396
+ return 1;
397
+ }
398
+ segmentMax(type) {
399
+ switch (type) {
400
+ case 'month':
401
+ return 12;
402
+ case 'year':
403
+ return 9999;
404
+ case 'hour':
405
+ return this.#cycle() === 12 ? 12 : 23;
406
+ case 'minute':
407
+ case 'second':
408
+ return 59;
409
+ case 'dayPeriod':
410
+ return 1;
411
+ default: {
412
+ const parts = this.#parts();
413
+ const probe = this.adapter.createDate(parts.year ?? RESOLVER_YEAR, parts.month ?? 1, 1);
414
+ return this.adapter.getDaysInMonth(probe);
415
+ }
416
+ }
417
+ }
418
+ segmentValueText(type) {
419
+ return this.#editor.segmentValueText(type);
420
+ }
421
+ segmentDisplayText(type) {
422
+ return this.#editor.segmentDisplayText(type);
423
+ }
424
+ isSegmentEmpty(type) {
425
+ return this.#editor.isSegmentEmpty(type);
426
+ }
427
+ isFirstSegmentType(type) {
428
+ return this.#editor.isFirstSegmentType(type);
429
+ }
430
+ registerSegment(handle) {
431
+ this.#editor.registerSegment(handle);
432
+ }
433
+ unregisterSegment(handle) {
434
+ this.#editor.unregisterSegment(handle);
435
+ }
436
+ focusSegment(type) {
437
+ this.#editor.focusSegment(type);
438
+ }
439
+ typeDigit(type, digit) {
440
+ this.#editor.typeDigit(type, digit);
441
+ }
442
+ step(type, delta) {
443
+ this.#editor.step(type, delta);
444
+ }
445
+ goToBound(type, bound) {
446
+ this.#editor.goToBound(type, bound);
447
+ }
448
+ setDayPeriod(period) {
449
+ this.#editor.setDayPeriod(period);
450
+ }
451
+ clear(type) {
452
+ this.#editor.clear(type);
453
+ }
454
+ focusSibling(type, step) {
455
+ this.#editor.focusSibling(type, step);
456
+ }
457
+ onFocusOut(event) {
458
+ const next = event.relatedTarget;
459
+ if (!next || !this.#host.nativeElement.contains(next)) {
460
+ this.markTouched();
461
+ }
462
+ }
463
+ /** The active adapter, narrowed to a time-capable one; throws when it is day-only. */
464
+ #time() {
465
+ return assertTimeCapable(this.adapter, 'ForDateField');
466
+ }
467
+ /** Field-specific `aria-valuetext`: the empty marker or the localized month name. */
468
+ #valueText(type) {
469
+ if (this.isSegmentEmpty(type)) {
470
+ return this.#defaults.emptySegmentText;
471
+ }
472
+ if (type !== 'month') {
473
+ return null;
474
+ }
475
+ const month = this.#parts().month;
476
+ if (month === null) {
477
+ return null;
478
+ }
479
+ // Route the month name through the adapter's `format` so it tracks the
480
+ // adapter's calendar system rather than assuming Gregorian month numbering.
481
+ // A probe date built with `createDate` maps the 1-12 number to a localized
482
+ // long name without involving the entered value.
483
+ return this.adapter.format(this.adapter.createDate(RESOLVER_YEAR, month, 1), {
484
+ month: 'long',
485
+ });
486
+ }
487
+ /** Base value for stepping an empty segment: date parts from today, time parts from their minimum. */
488
+ #seed(type) {
489
+ if (type === 'hour') {
490
+ return this.#toInternalHour(this.segmentMin('hour'));
491
+ }
492
+ if (type === 'minute' || type === 'second' || type === 'dayPeriod') {
493
+ return 0;
494
+ }
495
+ const today = this.adapter.today();
496
+ const base = type === 'day'
497
+ ? this.adapter.getDate(today)
498
+ : type === 'month'
499
+ ? this.adapter.getMonth(today)
500
+ : this.adapter.getYear(today);
501
+ return Math.min(this.segmentMax(type), Math.max(this.segmentMin(type), base));
502
+ }
503
+ /** Converts a 12-hour display hour to a 0-23 hour, preserving the entered AM/PM. */
504
+ #toInternalHour(display) {
505
+ if (this.#cycle() === 24) {
506
+ return display;
507
+ }
508
+ const hour = this.#parts().hour;
509
+ const pm = hour !== null && hour >= 12;
510
+ const base = display % 12;
511
+ return pm ? base + 12 : base;
512
+ }
513
+ #placeholderFor(type) {
514
+ const override = this.placeholder()[type];
515
+ if (override) {
516
+ return override;
517
+ }
518
+ switch (type) {
519
+ case 'day':
520
+ return 'dd';
521
+ case 'month':
522
+ return 'mm';
523
+ case 'year':
524
+ return 'yyyy';
525
+ case 'hour':
526
+ return 'hh';
527
+ case 'minute':
528
+ return 'mm';
529
+ case 'second':
530
+ return 'ss';
531
+ default:
532
+ return '--';
533
+ }
534
+ }
535
+ #someEditableEmpty(parts) {
536
+ return this.#editableOrder().some((type) => {
537
+ if (type === 'dayPeriod') {
538
+ return parts.hour === null;
539
+ }
540
+ return parts[type] === null;
541
+ });
542
+ }
543
+ /**
544
+ * Re-clamps `day` to the resolved month/year length so the day segment's
545
+ * `aria-valuenow` never exceeds its `aria-valuemax` after stepping the month
546
+ * or year (e.g. day 31 + step to February → 28/29). Only ever shrinks the
547
+ * day; an empty day is left untouched.
548
+ */
549
+ #clampDay(parts) {
550
+ if (parts.day === null) {
551
+ return parts;
552
+ }
553
+ const probe = this.adapter.createDate(parts.year ?? RESOLVER_YEAR, parts.month ?? 1, 1);
554
+ const maxDay = this.adapter.getDaysInMonth(probe);
555
+ if (parts.day <= maxDay) {
556
+ return parts;
557
+ }
558
+ return { ...parts, day: maxDay };
559
+ }
560
+ #commitParts(rawNext) {
561
+ const next = this.#clampDay(rawNext);
562
+ this.#parts.set(next);
563
+ const granularity = this.granularity();
564
+ const needHour = granularity !== 'day';
565
+ const needMinute = granularity === 'minute' || granularity === 'second';
566
+ const needSecond = granularity === 'second';
567
+ const complete = next.day !== null &&
568
+ next.month !== null &&
569
+ next.year !== null &&
570
+ (!needHour || next.hour !== null) &&
571
+ (!needMinute || next.minute !== null) &&
572
+ (!needSecond || next.second !== null);
573
+ if (!complete) {
574
+ this.value.set(null);
575
+ return;
576
+ }
577
+ let created = this.adapter.createDate(next.year, next.month, next.day);
578
+ if (needHour) {
579
+ created = this.#time().setTime(created, next.hour, needMinute ? next.minute : 0, needSecond ? next.second : 0);
580
+ }
581
+ this.value.set(clampToBounds(this.adapter, created, this.minDate(), this.maxDate()));
582
+ }
583
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDateField, deps: [], target: i0.ɵɵFactoryTarget.Directive });
584
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForDateField, isStandalone: true, selector: "[forDateField]", inputs: { value: { classPropertyName: "value", publicName: "value", 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 }, granularity: { classPropertyName: "granularity", publicName: "granularity", isSignal: true, isRequired: false, transformFunction: null }, hourCycle: { classPropertyName: "hourCycle", publicName: "hourCycle", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", 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" }, host: { attributes: { "role": "group" }, listeners: { "focusout": "onFocusOut($event)" }, properties: { "attr.dir": "dir()", "attr.aria-label": "ariaLabel() || null", "attr.aria-disabled": "effectiveDisabled() ? \"true\" : null", "attr.aria-readonly": "readonly() ? \"true\" : null", "attr.aria-required": "required() ? \"true\" : null", "attr.aria-invalid": "invalid() ? \"true\" : null", "attr.data-disabled": "effectiveDisabled() ? \"\" : null", "attr.data-readonly": "readonly() ? \"\" : null", "attr.data-empty": "value() === null ? \"\" : null" } }, providers: [{ provide: FOR_DATE_FIELD_CONTEXT, useExisting: ForDateField }], exportAs: ["forDateField"], usesInheritance: true, ngImport: i0 });
585
+ }
586
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDateField, decorators: [{
587
+ type: Directive,
588
+ args: [{
589
+ selector: '[forDateField]',
590
+ exportAs: 'forDateField',
591
+ host: {
592
+ role: 'group',
593
+ '[attr.dir]': 'dir()',
594
+ '[attr.aria-label]': 'ariaLabel() || null',
595
+ '[attr.aria-disabled]': 'effectiveDisabled() ? "true" : null',
596
+ '[attr.aria-readonly]': 'readonly() ? "true" : null',
597
+ '[attr.aria-required]': 'required() ? "true" : null',
598
+ '[attr.aria-invalid]': 'invalid() ? "true" : null',
599
+ '[attr.data-disabled]': 'effectiveDisabled() ? "" : null',
600
+ '[attr.data-readonly]': 'readonly() ? "" : null',
601
+ '[attr.data-empty]': 'value() === null ? "" : null',
602
+ '(focusout)': 'onFocusOut($event)',
603
+ },
604
+ providers: [{ provide: FOR_DATE_FIELD_CONTEXT, useExisting: ForDateField }],
605
+ }]
606
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], granularity: [{ type: i0.Input, args: [{ isSignal: true, alias: "granularity", required: false }] }], hourCycle: [{ type: i0.Input, args: [{ isSignal: true, alias: "hourCycle", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], _dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }] } });
607
+
608
+ /**
609
+ * One editable spinbutton segment of a `[forDateField]` — the day, month, or
610
+ * year part (and, at `granularity > 'day'`, hour / minute / second / the AM·PM
611
+ * `dayPeriod`). Apply on a focusable element (typically a `<span>`); the shared
612
+ * {@link ForDateTimeSegmentBase} adds `role="spinbutton"`, the `aria-valuemin` /
613
+ * `aria-valuemax` / `aria-valuenow` / `aria-valuetext` reflection, the roving
614
+ * tabindex, and the full keyboard map:
615
+ *
616
+ * - **digits** fill a numeric segment and auto-advance to the next when full;
617
+ * - **a / p** set the period on the AM/PM (`dayPeriod`) segment;
618
+ * - **ArrowUp / ArrowDown** step the value (day / month / hour / minute / second
619
+ * wrap, year clamps, dayPeriod toggles);
620
+ * - **ArrowLeft / ArrowRight** move between segments (mirrored under RTL, no wrap);
621
+ * - **Home / End** jump to the segment minimum / maximum (dayPeriod → AM / PM);
622
+ * - **Backspace / Delete** clear a numeric segment.
623
+ *
624
+ * All state lives on the root `ForDateField`; the segment only reads it and
625
+ * forwards intents. The rendered text comes from the root's `segments()` list
626
+ * (`{{ seg.text }}`), so this element carries behavior and ARIA, not content.
627
+ */
628
+ class ForDateFieldSegment extends ForDateTimeSegmentBase {
629
+ ctx = injectDateFieldContext('ForDateFieldSegment');
630
+ #defaults = inject(FOR_DATE_FIELD_DEFAULTS);
631
+ /** Which date or time part this segment edits. */
632
+ segment = input.required(/* @ts-ignore */
633
+ ...(ngDevMode ? [{ debugName: "segment" }] : /* istanbul ignore next */ []));
634
+ /**
635
+ * Accessible name for this segment. Falls back to the scope's localized
636
+ * default label for the part (via `provideForDateFieldDefaults`), which in
637
+ * turn defaults to the part name — and `'AM/PM'` for the `dayPeriod` segment.
638
+ */
639
+ ariaLabel = input(null, /* @ts-ignore */
640
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
641
+ resolvedAriaLabel = computed(() => {
642
+ const type = this.segment();
643
+ return (this.ariaLabel() ??
644
+ this.#defaults.segmentLabels[type] ??
645
+ DEFAULT_DATE_FIELD_SEGMENT_LABELS[type]);
646
+ }, /* @ts-ignore */
647
+ ...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
648
+ constructor() {
649
+ super();
650
+ this.registerSegment();
651
+ }
652
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDateFieldSegment, deps: [], target: i0.ɵɵFactoryTarget.Directive });
653
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForDateFieldSegment, isStandalone: true, selector: "[forDateFieldSegment]", inputs: { segment: { classPropertyName: "segment", publicName: "segment", isSignal: true, isRequired: true, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["forDateFieldSegment"], usesInheritance: true, ngImport: i0 });
654
+ }
655
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDateFieldSegment, decorators: [{
656
+ type: Directive,
657
+ args: [{
658
+ selector: '[forDateFieldSegment]',
659
+ exportAs: 'forDateFieldSegment',
660
+ }]
661
+ }], ctorParameters: () => [], propDecorators: { segment: [{ type: i0.Input, args: [{ isSignal: true, alias: "segment", required: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
662
+
663
+ /**
664
+ * A decorative separator (`/`, `.`, `-`) between the editable segments of a
665
+ * `[forDateField]`. Apply on the element rendering the literal text; it is
666
+ * marked `aria-hidden` and stays out of the tab order, so assistive tech reads
667
+ * only the spinbutton segments. The separator characters come from the root's
668
+ * locale-ordered `segments()` list (`{{ seg.text }}`).
669
+ */
670
+ class ForDateFieldLiteral extends ForDateTimeLiteralBase {
671
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDateFieldLiteral, deps: null, target: i0.ɵɵFactoryTarget.Directive });
672
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForDateFieldLiteral, isStandalone: true, selector: "[forDateFieldLiteral]", exportAs: ["forDateFieldLiteral"], usesInheritance: true, ngImport: i0 });
673
+ }
674
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDateFieldLiteral, decorators: [{
675
+ type: Directive,
676
+ args: [{
677
+ selector: '[forDateFieldLiteral]',
678
+ exportAs: 'forDateFieldLiteral',
679
+ }]
680
+ }] });
681
+
682
+ /**
683
+ * Exact public names of every `ForDateField` input, its models included. Spread it into the
684
+ * `inputs` array of a `hostDirectives` entry so a wrapper component re-exposes the
685
+ * primitive's full surface — the Signal Forms members `[formField]` binds among them —
686
+ * without hand-maintaining the list. Always spread into an inline object literal as shown
687
+ * below: the literal is what keeps the entry statically analyzable for consumers compiling
688
+ * against the published package. An anti-drift spec fails when this list no longer matches
689
+ * the directive's actual API. See `docs/wrapping-form-primitives.md` for both supported
690
+ * wrapping patterns.
691
+ *
692
+ * @example
693
+ * ```ts
694
+ * @Component({
695
+ * selector: 'div[myDateField]',
696
+ * template: '',
697
+ * hostDirectives: [
698
+ * {
699
+ * directive: ForDateField,
700
+ * inputs: [...FOR_DATE_FIELD_HOST_DIRECTIVE_INPUTS],
701
+ * outputs: [...FOR_DATE_FIELD_HOST_DIRECTIVE_OUTPUTS],
702
+ * },
703
+ * ],
704
+ * })
705
+ * export class MyDateField {}
706
+ * ```
707
+ */
708
+ const FOR_DATE_FIELD_HOST_DIRECTIVE_INPUTS = [
709
+ 'value',
710
+ 'ariaLabel',
711
+ 'dir',
712
+ 'dirty',
713
+ 'disabled',
714
+ 'errors',
715
+ 'granularity',
716
+ 'hourCycle',
717
+ 'invalid',
718
+ 'locale',
719
+ 'maxDate',
720
+ 'minDate',
721
+ 'name',
722
+ 'pending',
723
+ 'placeholder',
724
+ 'readonly',
725
+ 'required',
726
+ 'touched',
727
+ ];
728
+ /**
729
+ * Exact public names of every `ForDateField` output, the Signal Forms `touch` output
730
+ * included. Spread it into the `outputs` array of the same `hostDirectives` entry as
731
+ * {@link FOR_DATE_FIELD_HOST_DIRECTIVE_INPUTS}.
732
+ */
733
+ const FOR_DATE_FIELD_HOST_DIRECTIVE_OUTPUTS = [
734
+ 'valueChange',
735
+ 'touchedChange',
736
+ 'touch',
737
+ ];
738
+
739
+ /**
740
+ * Generated bundle index. Do not edit.
741
+ */
742
+
743
+ export { DEFAULT_DATE_FIELD_SEGMENT_LABELS, FOR_DATE_FIELD_CONTEXT, FOR_DATE_FIELD_DEFAULTS, FOR_DATE_FIELD_HOST_DIRECTIVE_INPUTS, FOR_DATE_FIELD_HOST_DIRECTIVE_OUTPUTS, ForDateField, ForDateFieldLiteral, ForDateFieldSegment, provideForDateFieldDefaults };
744
+ //# sourceMappingURL=forty-cdk-date-field.mjs.map