ngx-com 0.0.21 → 0.1.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 (114) hide show
  1. package/README.md +137 -33
  2. package/fesm2022/ngx-com-components-alert.mjs +21 -11
  3. package/fesm2022/ngx-com-components-alert.mjs.map +1 -1
  4. package/fesm2022/ngx-com-components-avatar.mjs +9 -7
  5. package/fesm2022/ngx-com-components-avatar.mjs.map +1 -1
  6. package/fesm2022/ngx-com-components-button.mjs +1 -1
  7. package/fesm2022/ngx-com-components-button.mjs.map +1 -1
  8. package/fesm2022/ngx-com-components-calendar.mjs +27 -3112
  9. package/fesm2022/ngx-com-components-calendar.mjs.map +1 -1
  10. package/fesm2022/ngx-com-components-card.mjs +8 -8
  11. package/fesm2022/ngx-com-components-card.mjs.map +1 -1
  12. package/fesm2022/ngx-com-components-carousel.mjs +16 -4
  13. package/fesm2022/ngx-com-components-carousel.mjs.map +1 -1
  14. package/fesm2022/ngx-com-components-checkbox.mjs +1 -1
  15. package/fesm2022/ngx-com-components-checkbox.mjs.map +1 -1
  16. package/fesm2022/ngx-com-components-code-block.mjs +9 -9
  17. package/fesm2022/ngx-com-components-code-block.mjs.map +1 -1
  18. package/fesm2022/ngx-com-components-collapsible.mjs +15 -13
  19. package/fesm2022/ngx-com-components-collapsible.mjs.map +1 -1
  20. package/fesm2022/ngx-com-components-confirm.mjs +4 -4
  21. package/fesm2022/ngx-com-components-confirm.mjs.map +1 -1
  22. package/fesm2022/ngx-com-components-datepicker.mjs +2334 -0
  23. package/fesm2022/ngx-com-components-datepicker.mjs.map +1 -0
  24. package/fesm2022/ngx-com-components-dialog.mjs +47 -45
  25. package/fesm2022/ngx-com-components-dialog.mjs.map +1 -1
  26. package/fesm2022/ngx-com-components-dropdown.mjs +446 -340
  27. package/fesm2022/ngx-com-components-dropdown.mjs.map +1 -1
  28. package/fesm2022/ngx-com-components-empty-state.mjs +5 -3
  29. package/fesm2022/ngx-com-components-empty-state.mjs.map +1 -1
  30. package/fesm2022/ngx-com-components-form-field.mjs +11 -6
  31. package/fesm2022/ngx-com-components-form-field.mjs.map +1 -1
  32. package/fesm2022/ngx-com-components-icon-lucide.mjs +41 -0
  33. package/fesm2022/ngx-com-components-icon-lucide.mjs.map +1 -0
  34. package/fesm2022/ngx-com-components-icon.mjs +89 -61
  35. package/fesm2022/ngx-com-components-icon.mjs.map +1 -1
  36. package/fesm2022/ngx-com-components-item.mjs +14 -4
  37. package/fesm2022/ngx-com-components-item.mjs.map +1 -1
  38. package/fesm2022/ngx-com-components-menu.mjs +61 -69
  39. package/fesm2022/ngx-com-components-menu.mjs.map +1 -1
  40. package/fesm2022/ngx-com-components-native-control.mjs +170 -0
  41. package/fesm2022/ngx-com-components-native-control.mjs.map +1 -0
  42. package/fesm2022/ngx-com-components-paginator.mjs +11 -3
  43. package/fesm2022/ngx-com-components-paginator.mjs.map +1 -1
  44. package/fesm2022/ngx-com-components-popover.mjs +58 -33
  45. package/fesm2022/ngx-com-components-popover.mjs.map +1 -1
  46. package/fesm2022/ngx-com-components-radio.mjs +4 -4
  47. package/fesm2022/ngx-com-components-radio.mjs.map +1 -1
  48. package/fesm2022/ngx-com-components-segmented-control.mjs +6 -4
  49. package/fesm2022/ngx-com-components-segmented-control.mjs.map +1 -1
  50. package/fesm2022/ngx-com-components-sort.mjs +63 -57
  51. package/fesm2022/ngx-com-components-sort.mjs.map +1 -1
  52. package/fesm2022/ngx-com-components-spinner.mjs +6 -6
  53. package/fesm2022/ngx-com-components-spinner.mjs.map +1 -1
  54. package/fesm2022/ngx-com-components-switch.mjs +18 -9
  55. package/fesm2022/ngx-com-components-switch.mjs.map +1 -1
  56. package/fesm2022/ngx-com-components-table.mjs +23 -9
  57. package/fesm2022/ngx-com-components-table.mjs.map +1 -1
  58. package/fesm2022/ngx-com-components-tabs.mjs +81 -58
  59. package/fesm2022/ngx-com-components-tabs.mjs.map +1 -1
  60. package/fesm2022/ngx-com-components-timepicker.mjs +1048 -0
  61. package/fesm2022/ngx-com-components-timepicker.mjs.map +1 -0
  62. package/fesm2022/ngx-com-components-toast.mjs +18 -14
  63. package/fesm2022/ngx-com-components-toast.mjs.map +1 -1
  64. package/fesm2022/ngx-com-components-tooltip.mjs +5 -5
  65. package/fesm2022/ngx-com-components-tooltip.mjs.map +1 -1
  66. package/fesm2022/ngx-com-components.mjs +0 -13
  67. package/fesm2022/ngx-com-components.mjs.map +1 -1
  68. package/fesm2022/ngx-com-tokens.mjs +0 -8
  69. package/fesm2022/ngx-com-tokens.mjs.map +1 -1
  70. package/fesm2022/ngx-com-utils.mjs +13 -1
  71. package/fesm2022/ngx-com-utils.mjs.map +1 -1
  72. package/fesm2022/ngx-com.mjs +1 -1
  73. package/fesm2022/ngx-com.mjs.map +1 -1
  74. package/package.json +51 -8
  75. package/styles/animations.css +38 -0
  76. package/styles/candy.css +121 -0
  77. package/styles/dark.css +159 -0
  78. package/styles/forest.css +117 -0
  79. package/styles/ocean.css +117 -0
  80. package/styles/themes.css +7 -0
  81. package/styles/tokens.css +277 -0
  82. package/styles/utilities.css +16 -0
  83. package/types/ngx-com-components-alert.d.ts +14 -4
  84. package/types/ngx-com-components-avatar.d.ts +2 -0
  85. package/types/ngx-com-components-calendar.d.ts +3 -747
  86. package/types/ngx-com-components-card.d.ts +2 -2
  87. package/types/ngx-com-components-carousel.d.ts +11 -1
  88. package/types/ngx-com-components-code-block.d.ts +4 -4
  89. package/types/ngx-com-components-collapsible.d.ts +10 -2
  90. package/types/ngx-com-components-confirm.d.ts +2 -2
  91. package/types/ngx-com-components-datepicker.d.ts +623 -0
  92. package/types/ngx-com-components-dialog.d.ts +5 -2
  93. package/types/ngx-com-components-dropdown.d.ts +22 -4
  94. package/types/ngx-com-components-empty-state.d.ts +2 -0
  95. package/types/ngx-com-components-form-field.d.ts +4 -1
  96. package/types/ngx-com-components-icon-lucide.d.ts +32 -0
  97. package/types/ngx-com-components-icon.d.ts +49 -35
  98. package/types/ngx-com-components-item.d.ts +12 -2
  99. package/types/ngx-com-components-menu.d.ts +38 -38
  100. package/types/ngx-com-components-native-control.d.ts +99 -0
  101. package/types/ngx-com-components-paginator.d.ts +2 -0
  102. package/types/ngx-com-components-popover.d.ts +19 -12
  103. package/types/ngx-com-components-segmented-control.d.ts +3 -1
  104. package/types/ngx-com-components-sort.d.ts +13 -10
  105. package/types/ngx-com-components-switch.d.ts +7 -2
  106. package/types/ngx-com-components-table.d.ts +16 -2
  107. package/types/ngx-com-components-tabs.d.ts +46 -34
  108. package/types/ngx-com-components-timepicker.d.ts +273 -0
  109. package/types/ngx-com-components-toast.d.ts +4 -2
  110. package/types/ngx-com-components-tooltip.d.ts +1 -1
  111. package/types/ngx-com-components.d.ts +6 -7
  112. package/types/ngx-com-tokens.d.ts +5 -3
  113. package/types/ngx-com-utils.d.ts +11 -1
  114. package/types/ngx-com.d.ts +1 -1
@@ -0,0 +1,1048 @@
1
+ import { cva } from 'class-variance-authority';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, LOCALE_ID, viewChild, model, input, output, linkedSignal, signal, computed, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { NgControl, NgForm, FormGroupDirective } from '@angular/forms';
5
+ import { ErrorStateMatcher, FormFieldControl } from 'ngx-com/components/form-field';
6
+ import { mergeClasses } from 'ngx-com/utils';
7
+
8
+ /**
9
+ * Types and interfaces for the TimePicker component.
10
+ */
11
+ /** Creates a ComTimeValue */
12
+ function createTimeValue(hours = 0, minutes = 0, seconds = 0) {
13
+ return { hours, minutes, seconds };
14
+ }
15
+ /**
16
+ * Compares two ComTimeValue objects.
17
+ * @returns negative if a < b, 0 if equal, positive if a > b
18
+ */
19
+ function compareTime(a, b) {
20
+ const diff = a.hours * 3600 + a.minutes * 60 + a.seconds - (b.hours * 3600 + b.minutes * 60 + b.seconds);
21
+ return diff;
22
+ }
23
+ /** Generates a unique ID for time picker instances */
24
+ let timePickerIdCounter = 0;
25
+ function generateTimePickerId() {
26
+ return `com-time-picker-${timePickerIdCounter++}`;
27
+ }
28
+
29
+ /**
30
+ * CVA variants for the time picker container.
31
+ *
32
+ * @tokens `--color-input-background`, `--color-input-foreground`, `--color-input-border`,
33
+ * `--color-ring`, `--color-warn`, `--color-success`, `--radius-input`
34
+ */
35
+ const timepickerContainerVariants = cva([
36
+ 'inline-flex',
37
+ 'items-center',
38
+ 'gap-1',
39
+ 'rounded-input',
40
+ 'transition-colors',
41
+ 'duration-normal',
42
+ ], {
43
+ variants: {
44
+ variant: {
45
+ standalone: [
46
+ 'border',
47
+ 'border-input-border',
48
+ 'bg-input-background',
49
+ 'text-input-foreground',
50
+ 'focus-within:outline-1',
51
+ 'focus-within:outline-offset-2',
52
+ 'focus-within:outline-ring',
53
+ ],
54
+ embedded: [
55
+ 'border-transparent',
56
+ 'bg-transparent',
57
+ 'text-foreground',
58
+ ],
59
+ naked: [
60
+ 'border-transparent',
61
+ 'bg-transparent',
62
+ 'shadow-none',
63
+ 'focus-within:outline-none',
64
+ 'rounded-none',
65
+ ],
66
+ },
67
+ size: {
68
+ sm: ['h-8', 'px-2', 'text-xs', 'gap-0.5'],
69
+ default: ['h-10', 'px-3', 'text-sm', 'gap-1'],
70
+ lg: ['h-12', 'px-4', 'text-base', 'gap-1.5'],
71
+ },
72
+ state: {
73
+ default: [],
74
+ error: [
75
+ 'border-warn',
76
+ 'focus-within:outline-warn',
77
+ ],
78
+ success: [
79
+ 'border-success',
80
+ 'focus-within:outline-success',
81
+ ],
82
+ },
83
+ },
84
+ compoundVariants: [
85
+ {
86
+ variant: 'embedded',
87
+ state: 'error',
88
+ class: [],
89
+ },
90
+ {
91
+ variant: 'embedded',
92
+ state: 'success',
93
+ class: [],
94
+ },
95
+ // Naked variant should not show ring (form-field provides focus styling)
96
+ {
97
+ variant: 'naked',
98
+ state: 'error',
99
+ class: ['border-transparent', 'focus-within:outline-none'],
100
+ },
101
+ {
102
+ variant: 'naked',
103
+ state: 'success',
104
+ class: ['border-transparent', 'focus-within:outline-none'],
105
+ },
106
+ ],
107
+ defaultVariants: {
108
+ variant: 'standalone',
109
+ size: 'default',
110
+ state: 'default',
111
+ },
112
+ });
113
+ /**
114
+ * CVA variants for the disabled state of time picker.
115
+ *
116
+ * @tokens `--color-disabled`, `--color-disabled-foreground`
117
+ */
118
+ const timepickerDisabledVariants = cva([
119
+ 'cursor-not-allowed',
120
+ 'bg-disabled',
121
+ 'text-disabled-foreground',
122
+ 'pointer-events-none',
123
+ ]);
124
+ /**
125
+ * CVA variants for each time segment input.
126
+ *
127
+ * @tokens `--color-primary-subtle`, `--color-primary-subtle-foreground`, `--radius-interactive-sm`
128
+ */
129
+ const timepickerSegmentVariants = cva([
130
+ 'bg-transparent',
131
+ 'outline-none',
132
+ 'text-center',
133
+ 'font-mono',
134
+ 'tabular-nums',
135
+ 'select-all',
136
+ 'focus-visible:bg-primary-subtle',
137
+ 'focus-visible:text-primary-subtle-foreground',
138
+ 'focus-visible:rounded-interactive-sm',
139
+ ], {
140
+ variants: {
141
+ size: {
142
+ sm: ['w-5', 'text-xs'],
143
+ default: ['w-7', 'text-sm'],
144
+ lg: ['w-9', 'text-base'],
145
+ },
146
+ },
147
+ defaultVariants: {
148
+ size: 'default',
149
+ },
150
+ });
151
+ /**
152
+ * CVA variants for the colon separator.
153
+ *
154
+ * @tokens `--color-muted-foreground`
155
+ */
156
+ const timepickerSeparatorVariants = cva([
157
+ 'text-muted-foreground',
158
+ 'select-none',
159
+ 'font-mono',
160
+ ], {
161
+ variants: {
162
+ size: {
163
+ sm: ['text-xs'],
164
+ default: ['text-sm'],
165
+ lg: ['text-base'],
166
+ },
167
+ },
168
+ defaultVariants: {
169
+ size: 'default',
170
+ },
171
+ });
172
+ /**
173
+ * CVA variants for the AM/PM toggle button.
174
+ *
175
+ * @tokens `--color-muted`, `--color-muted-foreground`, `--color-muted-hover`, `--color-ring`,
176
+ * `--radius-control-sm`
177
+ */
178
+ const timepickerPeriodVariants = cva([
179
+ 'inline-flex',
180
+ 'items-center',
181
+ 'justify-center',
182
+ 'rounded-control-sm',
183
+ 'font-medium',
184
+ 'transition-colors',
185
+ 'select-none',
186
+ 'outline-none',
187
+ 'bg-muted',
188
+ 'text-muted-foreground',
189
+ 'hover:bg-muted-hover',
190
+ 'focus-visible:outline-1',
191
+ 'focus-visible:outline-offset-2',
192
+ 'focus-visible:outline-ring',
193
+ ], {
194
+ variants: {
195
+ size: {
196
+ sm: ['h-5', 'px-1', 'text-xs', 'ml-0.5'],
197
+ default: ['h-6', 'px-1.5', 'text-xs', 'ml-1'],
198
+ lg: ['h-7', 'px-2', 'text-sm', 'ml-1.5'],
199
+ },
200
+ },
201
+ defaultVariants: {
202
+ size: 'default',
203
+ },
204
+ });
205
+ /**
206
+ * CVA variants for the time section divider in datepicker panel.
207
+ *
208
+ * @tokens `--color-border-subtle`
209
+ */
210
+ const timepickerSectionVariants = cva([
211
+ 'flex',
212
+ 'items-center',
213
+ 'justify-center',
214
+ 'border-t',
215
+ 'border-border-subtle',
216
+ ], {
217
+ variants: {
218
+ size: {
219
+ sm: ['pt-2', 'mt-2'],
220
+ default: ['pt-3', 'mt-3'],
221
+ lg: ['pt-4', 'mt-4'],
222
+ },
223
+ },
224
+ defaultVariants: {
225
+ size: 'default',
226
+ },
227
+ });
228
+ /**
229
+ * CVA variants for time labels in date range picker.
230
+ *
231
+ * @tokens `--color-muted-foreground`
232
+ */
233
+ const timepickerLabelVariants = cva([
234
+ 'text-muted-foreground',
235
+ 'font-medium',
236
+ ], {
237
+ variants: {
238
+ size: {
239
+ sm: ['text-xs'],
240
+ default: ['text-xs'],
241
+ lg: ['text-sm'],
242
+ },
243
+ },
244
+ defaultVariants: {
245
+ size: 'default',
246
+ },
247
+ });
248
+
249
+ /**
250
+ * Time picker component with segmented numeric input fields.
251
+ * Supports standalone usage with ControlValueAccessor and embedded usage
252
+ * within datepicker/date-range-picker panels.
253
+ *
254
+ * Visual layout: `[HH] : [MM] : [SS] [AM|PM]`
255
+ *
256
+ * @tokens `--color-input-background`, `--color-input-foreground`, `--color-input-border`,
257
+ * `--color-ring`, `--color-primary-subtle`, `--color-primary-subtle-foreground`,
258
+ * `--color-muted`, `--color-muted-foreground`, `--color-muted-hover`,
259
+ * `--color-disabled`, `--color-disabled-foreground`,
260
+ * `--color-warn`, `--color-success`, `--color-border`
261
+ *
262
+ * @example
263
+ * ```html
264
+ * <!-- Standalone with reactive forms -->
265
+ * <com-time-picker formControlName="startTime" />
266
+ *
267
+ * <!-- 12-hour format with seconds -->
268
+ * <com-time-picker formControlName="alarm" [use12HourFormat]="true" [showSeconds]="true" />
269
+ *
270
+ * <!-- 15-minute steps -->
271
+ * <com-time-picker formControlName="meeting" [minuteStep]="15" />
272
+ *
273
+ * <!-- Embedded inside datepicker panel -->
274
+ * <com-time-picker variant="embedded" [value]="time" (timeChange)="onTime($event)" />
275
+ * ```
276
+ */
277
+ class ComTimePicker {
278
+ localeId = inject(LOCALE_ID);
279
+ ngControl = inject(NgControl, { optional: true, self: true });
280
+ defaultErrorStateMatcher = inject(ErrorStateMatcher);
281
+ parentForm = inject(NgForm, { optional: true });
282
+ parentFormGroup = inject(FormGroupDirective, { optional: true });
283
+ timepickerId = generateTimePickerId();
284
+ hoursInputRef = viewChild('hoursInput', ...(ngDevMode ? [{ debugName: "hoursInputRef" }] : []));
285
+ minutesInputRef = viewChild('minutesInput', ...(ngDevMode ? [{ debugName: "minutesInputRef" }] : []));
286
+ secondsInputRef = viewChild('secondsInput', ...(ngDevMode ? [{ debugName: "secondsInputRef" }] : []));
287
+ periodButtonRef = viewChild('periodButton', ...(ngDevMode ? [{ debugName: "periodButtonRef" }] : []));
288
+ // ============ INPUTS ============
289
+ /** Current time value. */
290
+ value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
291
+ /** Whether the time picker is disabled. */
292
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
293
+ /** Whether the time picker is required. */
294
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
295
+ /** Whether to show the seconds segment. */
296
+ showSeconds = input(false, ...(ngDevMode ? [{ debugName: "showSeconds" }] : []));
297
+ /** 12h vs 24h format. `null` = auto-detect from locale. */
298
+ use12HourFormat = input(null, ...(ngDevMode ? [{ debugName: "use12HourFormat" }] : []));
299
+ /** Step interval for minutes. */
300
+ minuteStep = input(1, ...(ngDevMode ? [{ debugName: "minuteStep" }] : []));
301
+ /** Step interval for seconds. */
302
+ secondStep = input(1, ...(ngDevMode ? [{ debugName: "secondStep" }] : []));
303
+ /** Minimum selectable time. */
304
+ minTime = input(null, ...(ngDevMode ? [{ debugName: "minTime" }] : []));
305
+ /** Maximum selectable time. */
306
+ maxTime = input(null, ...(ngDevMode ? [{ debugName: "maxTime" }] : []));
307
+ /** Visual variant. */
308
+ variant = input('standalone', ...(ngDevMode ? [{ debugName: "variant" }] : []));
309
+ /** Size variant. */
310
+ size = input('default', ...(ngDevMode ? [{ debugName: "size" }] : []));
311
+ /** Validation state. */
312
+ state = input('default', ...(ngDevMode ? [{ debugName: "state" }] : []));
313
+ /** Accessible label for the group. */
314
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
315
+ /** Additional CSS classes. */
316
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : {}), alias: 'class' });
317
+ /** Placeholder text for empty segments. */
318
+ placeholder = input('--', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
319
+ /** Custom error state matcher for determining when to show errors. */
320
+ errorStateMatcher = input(...(ngDevMode ? [undefined, { debugName: "errorStateMatcher" }] : []));
321
+ // Signal Forms inputs — set automatically by [formField] via setInputOnDirectives
322
+ touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
323
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
324
+ sfErrors = input([], { ...(ngDevMode ? { debugName: "sfErrors" } : {}), alias: 'errors' });
325
+ // ============ OUTPUTS ============
326
+ /** Emitted when time value changes. */
327
+ timeChange = output();
328
+ // ============ INTERNAL STATE ============
329
+ /** Internal value state. */
330
+ internalValue = linkedSignal(() => this.value() ?? null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
331
+ /** Which segment is currently focused. */
332
+ activeSegment = signal(null, ...(ngDevMode ? [{ debugName: "activeSegment" }] : []));
333
+ /** Pending typed digits for auto-advance. */
334
+ pendingDigits = signal('', ...(ngDevMode ? [{ debugName: "pendingDigits" }] : []));
335
+ /** Live announcements for screen readers. */
336
+ liveAnnouncement = signal('', ...(ngDevMode ? [{ debugName: "liveAnnouncement" }] : []));
337
+ /** IDs for aria-describedby (set by form-field). */
338
+ _describedByIds = signal('', ...(ngDevMode ? [{ debugName: "_describedByIds" }] : []));
339
+ /** Form field appearance (set by form-field). */
340
+ _appearance = signal('outline', ...(ngDevMode ? [{ debugName: "_appearance" }] : []));
341
+ // ============ FormFieldControl SIGNALS ============
342
+ /** Whether the time picker is focused. Implements FormFieldControl. */
343
+ focused = computed(() => this.activeSegment() !== null, ...(ngDevMode ? [{ debugName: "focused" }] : []));
344
+ /** Whether the label should float. */
345
+ shouldLabelFloat = computed(() => this.focused() || this.internalValue() !== null, ...(ngDevMode ? [{ debugName: "shouldLabelFloat" }] : []));
346
+ /** Whether the control is in an error state. Implements FormFieldControl. */
347
+ errorState = computed(() => {
348
+ if (!this.ngControl) {
349
+ return this.invalid() && this.touched();
350
+ }
351
+ const matcher = this.errorStateMatcher() ?? this.defaultErrorStateMatcher;
352
+ const form = this.parentFormGroup ?? this.parentForm;
353
+ return matcher.isErrorState(this.ngControl.control ?? null, form);
354
+ }, ...(ngDevMode ? [{ debugName: "errorState" }] : []));
355
+ /** Structured validation errors from Signal Forms. */
356
+ errors = computed(() => !this.ngControl ? this.sfErrors() : null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
357
+ /** Unique ID for the control (maps to hours input). Implements FormFieldControl. */
358
+ id = computed(() => `${this.timepickerId}-hours`, ...(ngDevMode ? [{ debugName: "id" }] : []));
359
+ /**
360
+ * Effective state combining manual state with automatic error detection.
361
+ */
362
+ effectiveState = computed(() => {
363
+ const manualState = this.state();
364
+ if (manualState !== 'default')
365
+ return manualState;
366
+ return this.errorState() ? 'error' : 'default';
367
+ }, ...(ngDevMode ? [{ debugName: "effectiveState" }] : []));
368
+ /** Combined aria-describedby from form-field. */
369
+ effectiveAriaDescribedBy = computed(() => this._describedByIds() || null, ...(ngDevMode ? [{ debugName: "effectiveAriaDescribedBy" }] : []));
370
+ // ============ COMPUTED STATE ============
371
+ /** Whether to use 12-hour format. */
372
+ is12Hour = computed(() => {
373
+ const explicit = this.use12HourFormat();
374
+ if (explicit !== null)
375
+ return explicit;
376
+ try {
377
+ const options = new Intl.DateTimeFormat(this.localeId, { hour: 'numeric' }).resolvedOptions();
378
+ return options.hour12 === true;
379
+ }
380
+ catch {
381
+ return false;
382
+ }
383
+ }, ...(ngDevMode ? [{ debugName: "is12Hour" }] : []));
384
+ /** Current period (AM/PM). */
385
+ period = computed(() => {
386
+ const value = this.internalValue();
387
+ if (!value)
388
+ return 'AM';
389
+ return value.hours >= 12 ? 'PM' : 'AM';
390
+ }, ...(ngDevMode ? [{ debugName: "period" }] : []));
391
+ /** Display hours (converted from 24h to 12h when needed). */
392
+ displayHours = computed(() => {
393
+ const value = this.internalValue();
394
+ if (!value)
395
+ return null;
396
+ if (!this.is12Hour())
397
+ return value.hours;
398
+ const h = value.hours % 12;
399
+ return h === 0 ? 12 : h;
400
+ }, ...(ngDevMode ? [{ debugName: "displayHours" }] : []));
401
+ /** Formatted hours string. */
402
+ formattedHours = computed(() => {
403
+ const h = this.displayHours();
404
+ if (h === null)
405
+ return '';
406
+ return h.toString().padStart(2, '0');
407
+ }, ...(ngDevMode ? [{ debugName: "formattedHours" }] : []));
408
+ /** Formatted minutes string. */
409
+ formattedMinutes = computed(() => {
410
+ const value = this.internalValue();
411
+ if (!value)
412
+ return '';
413
+ return value.minutes.toString().padStart(2, '0');
414
+ }, ...(ngDevMode ? [{ debugName: "formattedMinutes" }] : []));
415
+ /** Formatted seconds string. */
416
+ formattedSeconds = computed(() => {
417
+ const value = this.internalValue();
418
+ if (!value)
419
+ return '';
420
+ return value.seconds.toString().padStart(2, '0');
421
+ }, ...(ngDevMode ? [{ debugName: "formattedSeconds" }] : []));
422
+ /** Container classes. */
423
+ containerClasses = computed(() => {
424
+ const base = timepickerContainerVariants({
425
+ variant: this.variant(),
426
+ size: this.size(),
427
+ state: this.effectiveState(),
428
+ });
429
+ const disabled = this.disabled() ? timepickerDisabledVariants() : '';
430
+ // For naked variant, add padding based on form-field appearance
431
+ let paddingClasses = '';
432
+ if (this.variant() === 'naked') {
433
+ paddingClasses = this._appearance() === 'fill' ? 'pt-5 pb-1.5 px-3' : 'py-2.5 px-3';
434
+ }
435
+ return mergeClasses(base, disabled, paddingClasses, this.userClass());
436
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
437
+ /** Segment input classes. */
438
+ segmentClasses = computed(() => {
439
+ return timepickerSegmentVariants({ size: this.size() });
440
+ }, ...(ngDevMode ? [{ debugName: "segmentClasses" }] : []));
441
+ /** Separator classes. */
442
+ separatorClasses = computed(() => {
443
+ return timepickerSeparatorVariants({ size: this.size() });
444
+ }, ...(ngDevMode ? [{ debugName: "separatorClasses" }] : []));
445
+ /** Period button classes. */
446
+ periodClasses = computed(() => {
447
+ return timepickerPeriodVariants({ size: this.size() });
448
+ }, ...(ngDevMode ? [{ debugName: "periodClasses" }] : []));
449
+ // ============ CVA CALLBACKS ============
450
+ onChange = () => { };
451
+ onTouched = () => { };
452
+ onValidatorChange = () => { };
453
+ constructor() {
454
+ if (this.ngControl) {
455
+ this.ngControl.valueAccessor = this;
456
+ }
457
+ }
458
+ // ============ CVA IMPLEMENTATION ============
459
+ writeValue(value) {
460
+ this.internalValue.set(value);
461
+ }
462
+ registerOnChange(fn) {
463
+ this.onChange = fn;
464
+ }
465
+ registerOnTouched(fn) {
466
+ this.onTouched = fn;
467
+ }
468
+ setDisabledState(isDisabled) {
469
+ this.disabled.set(isDisabled);
470
+ }
471
+ // ============ VALIDATOR IMPLEMENTATION ============
472
+ validate() {
473
+ const value = this.internalValue();
474
+ if (this.required() && !value) {
475
+ return { required: true };
476
+ }
477
+ if (value) {
478
+ const minTime = this.minTime();
479
+ if (minTime && compareTime(value, minTime) < 0) {
480
+ return { minTime: { min: minTime, actual: value } };
481
+ }
482
+ const maxTime = this.maxTime();
483
+ if (maxTime && compareTime(value, maxTime) > 0) {
484
+ return { maxTime: { max: maxTime, actual: value } };
485
+ }
486
+ }
487
+ return null;
488
+ }
489
+ registerOnValidatorChange(fn) {
490
+ this.onValidatorChange = fn;
491
+ }
492
+ // ============ EVENT HANDLERS ============
493
+ onSegmentFocus(segment) {
494
+ this.activeSegment.set(segment);
495
+ this.pendingDigits.set('');
496
+ }
497
+ onSegmentBlur(segment) {
498
+ this.activeSegment.set(null);
499
+ this.pendingDigits.set('');
500
+ this.snapToStep(segment);
501
+ this.onTouched();
502
+ this.touched.set(true);
503
+ }
504
+ onSegmentInput(event, segment) {
505
+ // Prevent default browser input handling — we manage value via keydown
506
+ const input = event.target;
507
+ // Restore the formatted value to prevent browser from changing display
508
+ if (segment === 'hours') {
509
+ input.value = this.formattedHours();
510
+ }
511
+ else if (segment === 'minutes') {
512
+ input.value = this.formattedMinutes();
513
+ }
514
+ else {
515
+ input.value = this.formattedSeconds();
516
+ }
517
+ }
518
+ onSegmentKeydown(event, segment) {
519
+ switch (event.key) {
520
+ case 'ArrowUp':
521
+ event.preventDefault();
522
+ this.incrementSegment(segment, 1);
523
+ break;
524
+ case 'ArrowDown':
525
+ event.preventDefault();
526
+ this.incrementSegment(segment, -1);
527
+ break;
528
+ case 'ArrowRight':
529
+ event.preventDefault();
530
+ this.focusNextSegment(segment);
531
+ break;
532
+ case 'ArrowLeft':
533
+ event.preventDefault();
534
+ this.focusPrevSegment(segment);
535
+ break;
536
+ case 'Home':
537
+ event.preventDefault();
538
+ this.setSegmentToMin(segment);
539
+ break;
540
+ case 'End':
541
+ event.preventDefault();
542
+ this.setSegmentToMax(segment);
543
+ break;
544
+ case 'Backspace':
545
+ case 'Delete':
546
+ event.preventDefault();
547
+ this.pendingDigits.set('');
548
+ break;
549
+ default:
550
+ if (/^[0-9]$/.test(event.key)) {
551
+ event.preventDefault();
552
+ this.handleDigitInput(event.key, segment);
553
+ }
554
+ break;
555
+ }
556
+ }
557
+ onPeriodKeydown(event) {
558
+ switch (event.key) {
559
+ case 'ArrowUp':
560
+ case 'ArrowDown':
561
+ event.preventDefault();
562
+ this.togglePeriod();
563
+ break;
564
+ case 'ArrowLeft':
565
+ event.preventDefault();
566
+ this.focusPrevSegment('period');
567
+ break;
568
+ case 'a':
569
+ case 'A':
570
+ event.preventDefault();
571
+ this.setPeriod('AM');
572
+ break;
573
+ case 'p':
574
+ case 'P':
575
+ event.preventDefault();
576
+ this.setPeriod('PM');
577
+ break;
578
+ }
579
+ }
580
+ togglePeriod() {
581
+ const value = this.internalValue();
582
+ if (!value) {
583
+ this.updateValue({ hours: 12, minutes: 0, seconds: 0 });
584
+ return;
585
+ }
586
+ const newHours = value.hours >= 12 ? value.hours - 12 : value.hours + 12;
587
+ this.updateValue({ ...value, hours: newHours });
588
+ }
589
+ // ============ FormFieldControl IMPLEMENTATION ============
590
+ /**
591
+ * Called when the form field container is clicked.
592
+ * Implements FormFieldControl.
593
+ */
594
+ onContainerClick(_event) {
595
+ if (!this.disabled()) {
596
+ this.hoursInputRef()?.nativeElement.focus();
597
+ }
598
+ }
599
+ /**
600
+ * Sets the describedBy IDs from the form field.
601
+ * Called by the parent form field component.
602
+ */
603
+ setDescribedByIds(ids) {
604
+ this._describedByIds.set(ids);
605
+ }
606
+ /**
607
+ * Sets the appearance for styling.
608
+ * Called by the parent form field component.
609
+ */
610
+ setAppearance(appearance) {
611
+ this._appearance.set(appearance);
612
+ }
613
+ // ============ PRIVATE METHODS ============
614
+ setPeriod(period) {
615
+ const value = this.internalValue();
616
+ if (!value) {
617
+ const hours = period === 'AM' ? 0 : 12;
618
+ this.updateValue({ hours, minutes: 0, seconds: 0 });
619
+ return;
620
+ }
621
+ const currentPeriod = this.period();
622
+ if (currentPeriod === period)
623
+ return;
624
+ const newHours = period === 'AM' ? value.hours - 12 : value.hours + 12;
625
+ this.updateValue({ ...value, hours: newHours });
626
+ }
627
+ incrementSegment(segment, direction) {
628
+ const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
629
+ const step = segment === 'minutes' ? this.minuteStep() : segment === 'seconds' ? this.secondStep() : 1;
630
+ const delta = step * direction;
631
+ let newValue;
632
+ switch (segment) {
633
+ case 'hours': {
634
+ const max = this.is12Hour() ? 12 : 23;
635
+ const min = this.is12Hour() ? 1 : 0;
636
+ let h = this.is12Hour() ? (this.displayHours() ?? min) : value.hours;
637
+ h += delta;
638
+ // Wrap
639
+ if (this.is12Hour()) {
640
+ if (h > max)
641
+ h = min;
642
+ if (h < min)
643
+ h = max;
644
+ // Convert back to 24h
645
+ const isPM = this.period() === 'PM';
646
+ let h24 = h === 12 ? 0 : h;
647
+ if (isPM)
648
+ h24 += 12;
649
+ newValue = { ...value, hours: h24 };
650
+ }
651
+ else {
652
+ if (h > 23)
653
+ h = 0;
654
+ if (h < 0)
655
+ h = 23;
656
+ newValue = { ...value, hours: h };
657
+ }
658
+ break;
659
+ }
660
+ case 'minutes': {
661
+ let m = value.minutes + delta;
662
+ if (m > 59)
663
+ m = 0;
664
+ if (m < 0)
665
+ m = 59;
666
+ newValue = { ...value, minutes: m };
667
+ break;
668
+ }
669
+ case 'seconds': {
670
+ let s = value.seconds + delta;
671
+ if (s > 59)
672
+ s = 0;
673
+ if (s < 0)
674
+ s = 59;
675
+ newValue = { ...value, seconds: s };
676
+ break;
677
+ }
678
+ }
679
+ this.updateValue(newValue);
680
+ }
681
+ handleDigitInput(digit, segment) {
682
+ const pending = this.pendingDigits() + digit;
683
+ const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
684
+ let parsed = parseInt(pending, 10);
685
+ switch (segment) {
686
+ case 'hours': {
687
+ const max = this.is12Hour() ? 12 : 23;
688
+ const min = this.is12Hour() ? 1 : 0;
689
+ if (parsed > max)
690
+ parsed = parseInt(digit, 10);
691
+ if (parsed < min && pending.length >= 2)
692
+ parsed = min;
693
+ // Convert to 24h if 12h mode
694
+ let h24 = parsed;
695
+ if (this.is12Hour()) {
696
+ const isPM = this.period() === 'PM';
697
+ h24 = parsed === 12 ? 0 : parsed;
698
+ if (isPM)
699
+ h24 += 12;
700
+ }
701
+ this.updateValue({ ...value, hours: h24 });
702
+ break;
703
+ }
704
+ case 'minutes': {
705
+ if (parsed > 59)
706
+ parsed = parseInt(digit, 10);
707
+ this.updateValue({ ...value, minutes: parsed });
708
+ break;
709
+ }
710
+ case 'seconds': {
711
+ if (parsed > 59)
712
+ parsed = parseInt(digit, 10);
713
+ this.updateValue({ ...value, seconds: parsed });
714
+ break;
715
+ }
716
+ }
717
+ // Auto-advance after 2 digits
718
+ if (pending.length >= 2) {
719
+ this.pendingDigits.set('');
720
+ this.focusNextSegment(segment);
721
+ }
722
+ else {
723
+ this.pendingDigits.set(pending);
724
+ }
725
+ }
726
+ snapToStep(segment) {
727
+ const value = this.internalValue();
728
+ if (!value)
729
+ return;
730
+ if (segment === 'minutes' && this.minuteStep() > 1) {
731
+ const step = this.minuteStep();
732
+ const snapped = Math.round(value.minutes / step) * step;
733
+ const clamped = Math.min(snapped, 59);
734
+ if (clamped !== value.minutes) {
735
+ this.updateValue({ ...value, minutes: clamped });
736
+ }
737
+ }
738
+ if (segment === 'seconds' && this.secondStep() > 1) {
739
+ const step = this.secondStep();
740
+ const snapped = Math.round(value.seconds / step) * step;
741
+ const clamped = Math.min(snapped, 59);
742
+ if (clamped !== value.seconds) {
743
+ this.updateValue({ ...value, seconds: clamped });
744
+ }
745
+ }
746
+ }
747
+ setSegmentToMin(segment) {
748
+ const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
749
+ switch (segment) {
750
+ case 'hours': {
751
+ if (this.is12Hour()) {
752
+ // Min display is 1; convert to 24h
753
+ const isPM = this.period() === 'PM';
754
+ const h24 = isPM ? 13 : 1;
755
+ this.updateValue({ ...value, hours: h24 });
756
+ }
757
+ else {
758
+ this.updateValue({ ...value, hours: 0 });
759
+ }
760
+ break;
761
+ }
762
+ case 'minutes':
763
+ this.updateValue({ ...value, minutes: 0 });
764
+ break;
765
+ case 'seconds':
766
+ this.updateValue({ ...value, seconds: 0 });
767
+ break;
768
+ }
769
+ }
770
+ setSegmentToMax(segment) {
771
+ const value = this.internalValue() ?? { hours: 0, minutes: 0, seconds: 0 };
772
+ switch (segment) {
773
+ case 'hours': {
774
+ if (this.is12Hour()) {
775
+ // Max display is 12; 12 in 12h = 0 in 24h (for AM) or 12 (for PM)
776
+ const isPM = this.period() === 'PM';
777
+ const h24 = isPM ? 12 : 0;
778
+ this.updateValue({ ...value, hours: h24 });
779
+ }
780
+ else {
781
+ this.updateValue({ ...value, hours: 23 });
782
+ }
783
+ break;
784
+ }
785
+ case 'minutes':
786
+ this.updateValue({ ...value, minutes: 59 });
787
+ break;
788
+ case 'seconds':
789
+ this.updateValue({ ...value, seconds: 59 });
790
+ break;
791
+ }
792
+ }
793
+ focusNextSegment(current) {
794
+ switch (current) {
795
+ case 'hours':
796
+ this.minutesInputRef()?.nativeElement.focus();
797
+ break;
798
+ case 'minutes':
799
+ if (this.showSeconds()) {
800
+ this.secondsInputRef()?.nativeElement.focus();
801
+ }
802
+ else if (this.is12Hour()) {
803
+ this.periodButtonRef()?.nativeElement.focus();
804
+ }
805
+ break;
806
+ case 'seconds':
807
+ if (this.is12Hour()) {
808
+ this.periodButtonRef()?.nativeElement.focus();
809
+ }
810
+ break;
811
+ }
812
+ }
813
+ focusPrevSegment(current) {
814
+ switch (current) {
815
+ case 'minutes':
816
+ this.hoursInputRef()?.nativeElement.focus();
817
+ break;
818
+ case 'seconds':
819
+ this.minutesInputRef()?.nativeElement.focus();
820
+ break;
821
+ case 'period':
822
+ if (this.showSeconds()) {
823
+ this.secondsInputRef()?.nativeElement.focus();
824
+ }
825
+ else {
826
+ this.minutesInputRef()?.nativeElement.focus();
827
+ }
828
+ break;
829
+ }
830
+ }
831
+ updateValue(value) {
832
+ this.value.set(value);
833
+ this.onChange(value);
834
+ this.timeChange.emit(value);
835
+ this.onValidatorChange();
836
+ if (value) {
837
+ const h = this.is12Hour()
838
+ ? `${(value.hours % 12 || 12)}:${value.minutes.toString().padStart(2, '0')}${this.showSeconds() ? ':' + value.seconds.toString().padStart(2, '0') : ''} ${value.hours >= 12 ? 'PM' : 'AM'}`
839
+ : `${value.hours.toString().padStart(2, '0')}:${value.minutes.toString().padStart(2, '0')}${this.showSeconds() ? ':' + value.seconds.toString().padStart(2, '0') : ''}`;
840
+ this.liveAnnouncement.set(`Time set to ${h}`);
841
+ }
842
+ }
843
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTimePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
844
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComTimePicker, isStandalone: true, selector: "com-time-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, use12HourFormat: { classPropertyName: "use12HourFormat", publicName: "use12HourFormat", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, secondStep: { classPropertyName: "secondStep", publicName: "secondStep", isSignal: true, isRequired: false, transformFunction: null }, minTime: { classPropertyName: "minTime", publicName: "minTime", isSignal: true, isRequired: false, transformFunction: null }, maxTime: { classPropertyName: "maxTime", publicName: "maxTime", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, sfErrors: { classPropertyName: "sfErrors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange", touched: "touchedChange", timeChange: "timeChange" }, host: { properties: { "class.com-time-picker-disabled": "disabled()", "attr.role": "\"group\"", "attr.aria-label": "ariaLabel() || \"Time picker\"", "attr.aria-disabled": "disabled() || null" }, classAttribute: "com-time-picker-host inline-block" }, providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComTimePicker) }], viewQueries: [{ propertyName: "hoursInputRef", first: true, predicate: ["hoursInput"], descendants: true, isSignal: true }, { propertyName: "minutesInputRef", first: true, predicate: ["minutesInput"], descendants: true, isSignal: true }, { propertyName: "secondsInputRef", first: true, predicate: ["secondsInput"], descendants: true, isSignal: true }, { propertyName: "periodButtonRef", first: true, predicate: ["periodButton"], descendants: true, isSignal: true }], exportAs: ["comTimePicker"], ngImport: i0, template: `
845
+ <div [class]="containerClasses()">
846
+ <!-- Hours -->
847
+ <input
848
+ #hoursInput
849
+ type="text"
850
+ inputmode="numeric"
851
+ role="spinbutton"
852
+ [class]="segmentClasses()"
853
+ [attr.id]="id()"
854
+ [attr.aria-label]="'Hours'"
855
+ [attr.aria-describedby]="effectiveAriaDescribedBy() || null"
856
+ [attr.aria-valuenow]="displayHours()"
857
+ [attr.aria-valuemin]="is12Hour() ? 1 : 0"
858
+ [attr.aria-valuemax]="is12Hour() ? 12 : 23"
859
+ [value]="formattedHours()"
860
+ [placeholder]="placeholder()"
861
+ [disabled]="disabled()"
862
+ maxlength="2"
863
+ (keydown)="onSegmentKeydown($event, 'hours')"
864
+ (input)="onSegmentInput($event, 'hours')"
865
+ (focus)="onSegmentFocus('hours')"
866
+ (blur)="onSegmentBlur('hours')"
867
+ />
868
+
869
+ <span [class]="separatorClasses()" aria-hidden="true">:</span>
870
+
871
+ <!-- Minutes -->
872
+ <input
873
+ #minutesInput
874
+ type="text"
875
+ inputmode="numeric"
876
+ role="spinbutton"
877
+ [class]="segmentClasses()"
878
+ [attr.aria-label]="'Minutes'"
879
+ [attr.aria-valuenow]="internalValue()?.minutes ?? null"
880
+ [attr.aria-valuemin]="0"
881
+ [attr.aria-valuemax]="59"
882
+ [value]="formattedMinutes()"
883
+ [placeholder]="placeholder()"
884
+ [disabled]="disabled()"
885
+ maxlength="2"
886
+ (keydown)="onSegmentKeydown($event, 'minutes')"
887
+ (input)="onSegmentInput($event, 'minutes')"
888
+ (focus)="onSegmentFocus('minutes')"
889
+ (blur)="onSegmentBlur('minutes')"
890
+ />
891
+
892
+ @if (showSeconds()) {
893
+ <span [class]="separatorClasses()" aria-hidden="true">:</span>
894
+
895
+ <!-- Seconds -->
896
+ <input
897
+ #secondsInput
898
+ type="text"
899
+ inputmode="numeric"
900
+ role="spinbutton"
901
+ [class]="segmentClasses()"
902
+ [attr.aria-label]="'Seconds'"
903
+ [attr.aria-valuenow]="internalValue()?.seconds ?? null"
904
+ [attr.aria-valuemin]="0"
905
+ [attr.aria-valuemax]="59"
906
+ [value]="formattedSeconds()"
907
+ [placeholder]="placeholder()"
908
+ [disabled]="disabled()"
909
+ maxlength="2"
910
+ (keydown)="onSegmentKeydown($event, 'seconds')"
911
+ (input)="onSegmentInput($event, 'seconds')"
912
+ (focus)="onSegmentFocus('seconds')"
913
+ (blur)="onSegmentBlur('seconds')"
914
+ />
915
+ }
916
+
917
+ @if (is12Hour()) {
918
+ <button
919
+ #periodButton
920
+ type="button"
921
+ [class]="periodClasses()"
922
+ [attr.aria-label]="'Toggle AM/PM, currently ' + period()"
923
+ [disabled]="disabled()"
924
+ (click)="togglePeriod()"
925
+ (keydown)="onPeriodKeydown($event)"
926
+ >
927
+ {{ period() }}
928
+ </button>
929
+ }
930
+ </div>
931
+
932
+ <div class="sr-only" aria-live="polite" aria-atomic="true">
933
+ {{ liveAnnouncement() }}
934
+ </div>
935
+ `, isInline: true, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
936
+ }
937
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTimePicker, decorators: [{
938
+ type: Component,
939
+ args: [{ selector: 'com-time-picker', exportAs: 'comTimePicker', template: `
940
+ <div [class]="containerClasses()">
941
+ <!-- Hours -->
942
+ <input
943
+ #hoursInput
944
+ type="text"
945
+ inputmode="numeric"
946
+ role="spinbutton"
947
+ [class]="segmentClasses()"
948
+ [attr.id]="id()"
949
+ [attr.aria-label]="'Hours'"
950
+ [attr.aria-describedby]="effectiveAriaDescribedBy() || null"
951
+ [attr.aria-valuenow]="displayHours()"
952
+ [attr.aria-valuemin]="is12Hour() ? 1 : 0"
953
+ [attr.aria-valuemax]="is12Hour() ? 12 : 23"
954
+ [value]="formattedHours()"
955
+ [placeholder]="placeholder()"
956
+ [disabled]="disabled()"
957
+ maxlength="2"
958
+ (keydown)="onSegmentKeydown($event, 'hours')"
959
+ (input)="onSegmentInput($event, 'hours')"
960
+ (focus)="onSegmentFocus('hours')"
961
+ (blur)="onSegmentBlur('hours')"
962
+ />
963
+
964
+ <span [class]="separatorClasses()" aria-hidden="true">:</span>
965
+
966
+ <!-- Minutes -->
967
+ <input
968
+ #minutesInput
969
+ type="text"
970
+ inputmode="numeric"
971
+ role="spinbutton"
972
+ [class]="segmentClasses()"
973
+ [attr.aria-label]="'Minutes'"
974
+ [attr.aria-valuenow]="internalValue()?.minutes ?? null"
975
+ [attr.aria-valuemin]="0"
976
+ [attr.aria-valuemax]="59"
977
+ [value]="formattedMinutes()"
978
+ [placeholder]="placeholder()"
979
+ [disabled]="disabled()"
980
+ maxlength="2"
981
+ (keydown)="onSegmentKeydown($event, 'minutes')"
982
+ (input)="onSegmentInput($event, 'minutes')"
983
+ (focus)="onSegmentFocus('minutes')"
984
+ (blur)="onSegmentBlur('minutes')"
985
+ />
986
+
987
+ @if (showSeconds()) {
988
+ <span [class]="separatorClasses()" aria-hidden="true">:</span>
989
+
990
+ <!-- Seconds -->
991
+ <input
992
+ #secondsInput
993
+ type="text"
994
+ inputmode="numeric"
995
+ role="spinbutton"
996
+ [class]="segmentClasses()"
997
+ [attr.aria-label]="'Seconds'"
998
+ [attr.aria-valuenow]="internalValue()?.seconds ?? null"
999
+ [attr.aria-valuemin]="0"
1000
+ [attr.aria-valuemax]="59"
1001
+ [value]="formattedSeconds()"
1002
+ [placeholder]="placeholder()"
1003
+ [disabled]="disabled()"
1004
+ maxlength="2"
1005
+ (keydown)="onSegmentKeydown($event, 'seconds')"
1006
+ (input)="onSegmentInput($event, 'seconds')"
1007
+ (focus)="onSegmentFocus('seconds')"
1008
+ (blur)="onSegmentBlur('seconds')"
1009
+ />
1010
+ }
1011
+
1012
+ @if (is12Hour()) {
1013
+ <button
1014
+ #periodButton
1015
+ type="button"
1016
+ [class]="periodClasses()"
1017
+ [attr.aria-label]="'Toggle AM/PM, currently ' + period()"
1018
+ [disabled]="disabled()"
1019
+ (click)="togglePeriod()"
1020
+ (keydown)="onPeriodKeydown($event)"
1021
+ >
1022
+ {{ period() }}
1023
+ </button>
1024
+ }
1025
+ </div>
1026
+
1027
+ <div class="sr-only" aria-live="polite" aria-atomic="true">
1028
+ {{ liveAnnouncement() }}
1029
+ </div>
1030
+ `, providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComTimePicker) }], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1031
+ class: 'com-time-picker-host inline-block',
1032
+ '[class.com-time-picker-disabled]': 'disabled()',
1033
+ '[attr.role]': '"group"',
1034
+ '[attr.aria-label]': 'ariaLabel() || "Time picker"',
1035
+ '[attr.aria-disabled]': 'disabled() || null',
1036
+ }, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
1037
+ }], ctorParameters: () => [], propDecorators: { hoursInputRef: [{ type: i0.ViewChild, args: ['hoursInput', { isSignal: true }] }], minutesInputRef: [{ type: i0.ViewChild, args: ['minutesInput', { isSignal: true }] }], secondsInputRef: [{ type: i0.ViewChild, args: ['secondsInput', { isSignal: true }] }], periodButtonRef: [{ type: i0.ViewChild, args: ['periodButton', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], use12HourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "use12HourFormat", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], secondStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondStep", required: false }] }], minTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "minTime", required: false }] }], maxTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTime", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], sfErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], timeChange: [{ type: i0.Output, args: ["timeChange"] }] } });
1038
+
1039
+ /**
1040
+ * TimePicker component public API.
1041
+ */
1042
+
1043
+ /**
1044
+ * Generated bundle index. Do not edit.
1045
+ */
1046
+
1047
+ export { ComTimePicker, compareTime, createTimeValue, generateTimePickerId, timepickerContainerVariants, timepickerDisabledVariants, timepickerLabelVariants, timepickerPeriodVariants, timepickerSectionVariants, timepickerSegmentVariants, timepickerSeparatorVariants };
1048
+ //# sourceMappingURL=ngx-com-components-timepicker.mjs.map