@seniorsistemas/angular-components 19.7.4 → 19.7.6

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 (65) hide show
  1. package/alert/lib/alert/alert.component.d.ts +8 -1
  2. package/chips/README.md +3 -2
  3. package/chips/lib/chips/chip-item/chip-item.component.d.ts +2 -0
  4. package/chips/lib/chips/chips/chips.component.d.ts +2 -1
  5. package/datepicker/README.md +15 -2
  6. package/datepicker/lib/datepicker/date-segment-input.d.ts +82 -0
  7. package/datepicker/lib/datepicker/date.utils.d.ts +3 -7
  8. package/datepicker/lib/datepicker/datepicker.component.d.ts +101 -8
  9. package/dynamic-form/dynamic-form/components/lookup/models/lookup-api.model.d.ts +7 -1
  10. package/dynamic-form/dynamic-form/form-field/configurations/fields/lookup-field.d.ts +1 -1
  11. package/dynamic-form/public-api.d.ts +1 -1
  12. package/esm2022/alert/lib/alert/alert.component.mjs +12 -6
  13. package/esm2022/chips/lib/chips/chip-item/chip-item.component.mjs +15 -4
  14. package/esm2022/chips/lib/chips/chips/chips.component.mjs +26 -14
  15. package/esm2022/datepicker/lib/datepicker/date-segment-input.mjs +418 -0
  16. package/esm2022/datepicker/lib/datepicker/date.utils.mjs +7 -79
  17. package/esm2022/datepicker/lib/datepicker/datepicker.component.mjs +451 -41
  18. package/esm2022/dynamic-form/dynamic-form/components/lookup/lookup.component.mjs +2 -2
  19. package/esm2022/dynamic-form/dynamic-form/components/lookup/models/lookup-api.model.mjs +1 -1
  20. package/esm2022/dynamic-form/dynamic-form/form-field/configurations/fields/lookup-field.mjs +1 -1
  21. package/esm2022/dynamic-form/public-api.mjs +1 -1
  22. package/esm2022/panel/lib/panel/panel.component.mjs +3 -3
  23. package/esm2022/sidebar/lib/sidebar/sidebar.component.mjs +8 -6
  24. package/esm2022/thumbnail/lib/thumbnail/thumbnail.component.mjs +3 -3
  25. package/fesm2022/seniorsistemas-angular-components-alert.mjs +12 -6
  26. package/fesm2022/seniorsistemas-angular-components-alert.mjs.map +1 -1
  27. package/fesm2022/seniorsistemas-angular-components-chips.mjs +39 -16
  28. package/fesm2022/seniorsistemas-angular-components-chips.mjs.map +1 -1
  29. package/fesm2022/seniorsistemas-angular-components-datepicker.mjs +873 -118
  30. package/fesm2022/seniorsistemas-angular-components-datepicker.mjs.map +1 -1
  31. package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs +1 -1
  32. package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs.map +1 -1
  33. package/fesm2022/seniorsistemas-angular-components-panel.mjs +2 -2
  34. package/fesm2022/seniorsistemas-angular-components-panel.mjs.map +1 -1
  35. package/fesm2022/seniorsistemas-angular-components-sidebar.mjs +7 -5
  36. package/fesm2022/seniorsistemas-angular-components-sidebar.mjs.map +1 -1
  37. package/fesm2022/seniorsistemas-angular-components-thumbnail.mjs +2 -2
  38. package/fesm2022/seniorsistemas-angular-components-thumbnail.mjs.map +1 -1
  39. package/package.json +13 -13
  40. package/schematics/migrations/migrate-steps-to-stepper/index.d.ts +2 -0
  41. package/schematics/migrations/migrate-steps-to-stepper/index.js +104 -0
  42. package/schematics/migrations/migrate-steps-to-stepper/index.js.map +1 -0
  43. package/schematics/migrations.json +5 -0
  44. package/schematics/utils/ast-utils.d.ts +13 -0
  45. package/schematics/utils/ast-utils.js +76 -0
  46. package/schematics/utils/ast-utils.js.map +1 -1
  47. package/sidebar/lib/sidebar/sidebar.component.d.ts +3 -1
  48. package/schematics/migrations/migrate-dynamic-form-v19/index.test.d.ts +0 -1
  49. package/schematics/migrations/migrate-dynamic-form-v19/index.test.js +0 -888
  50. package/schematics/migrations/migrate-dynamic-form-v19/index.test.js.map +0 -1
  51. package/schematics/migrations/migrate-p-datepicker/index.test.d.ts +0 -1
  52. package/schematics/migrations/migrate-p-datepicker/index.test.js +0 -392
  53. package/schematics/migrations/migrate-p-datepicker/index.test.js.map +0 -1
  54. package/schematics/migrations/migrate-p-editor/index.test.d.ts +0 -1
  55. package/schematics/migrations/migrate-p-editor/index.test.js +0 -393
  56. package/schematics/migrations/migrate-p-editor/index.test.js.map +0 -1
  57. package/schematics/migrations/migrate-s-chips-outputs/index.test.d.ts +0 -1
  58. package/schematics/migrations/migrate-s-chips-outputs/index.test.js +0 -125
  59. package/schematics/migrations/migrate-s-chips-outputs/index.test.js.map +0 -1
  60. package/schematics/utils/ast-utils.test.d.ts +0 -1
  61. package/schematics/utils/ast-utils.test.js +0 -138
  62. package/schematics/utils/ast-utils.test.js.map +0 -1
  63. package/schematics/utils/workspace-utils.test.d.ts +0 -1
  64. package/schematics/utils/workspace-utils.test.js +0 -152
  65. package/schematics/utils/workspace-utils.test.js.map +0 -1
@@ -3,15 +3,432 @@ import { CdkTrapFocus } from '@angular/cdk/a11y';
3
3
  import { CdkOverlayOrigin, CdkConnectedOverlay } from '@angular/cdk/overlay';
4
4
  import { NgClass } from '@angular/common';
5
5
  import * as i0 from '@angular/core';
6
- import { input, model, output, signal, viewChild, ElementRef, inject, Injector, computed, afterNextRender, Component } from '@angular/core';
6
+ import { input, model, output, signal, viewChild, ElementRef, inject, Injector, DestroyRef, computed, afterNextRender, Component } from '@angular/core';
7
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
8
  import * as i2 from '@angular/forms';
8
9
  import { NgControl, Validators, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormsModule, ReactiveFormsModule } from '@angular/forms';
9
10
  import * as i1 from '@ngx-translate/core';
10
- import { TranslateModule } from '@ngx-translate/core';
11
+ import { TranslateService, TranslateModule } from '@ngx-translate/core';
12
+
13
+ const DATE_FORMAT_TOKEN_RE = /yyyy|yy|MM|mm|dd|d/g;
14
+ const YEAR_SEGMENT_LENGTH = 4;
15
+ const SHORT_SEGMENT_LENGTH = 2;
16
+ const HOURS_PER_MERIDIEM = 12;
17
+ const MAX_HOUR_24 = 23;
18
+ const MAX_MINUTE_SECOND = 59;
19
+ const GENERIC_MAX_DAY = 31;
20
+ const LEAP_REFERENCE_YEAR = 2000;
21
+ function isEditableSegment(segment) {
22
+ return segment.kind !== 'literal';
23
+ }
24
+ /** Tokeniza um `dateFormat` (ex.: `dd/MM/yyyy`, `MM/dd/yy`) em tokens de dia/mês/ano + literais. */
25
+ function tokenizeDateFormat(format) {
26
+ const tokens = [];
27
+ let lastIndex = 0;
28
+ DATE_FORMAT_TOKEN_RE.lastIndex = 0;
29
+ let match;
30
+ while ((match = DATE_FORMAT_TOKEN_RE.exec(format))) {
31
+ if (match.index > lastIndex) {
32
+ tokens.push({ kind: 'literal', raw: format.slice(lastIndex, match.index) });
33
+ }
34
+ const raw = match[0];
35
+ const kind = raw[0] === 'y' ? 'year' : raw[0] === 'd' ? 'day' : 'month';
36
+ tokens.push({ kind, raw });
37
+ lastIndex = DATE_FORMAT_TOKEN_RE.lastIndex;
38
+ }
39
+ if (lastIndex < format.length) {
40
+ tokens.push({ kind: 'literal', raw: format.slice(lastIndex) });
41
+ }
42
+ return tokens;
43
+ }
44
+ /** Dias no mês (1-12), já resolvendo fevereiro/ano bissexto via rollover nativo do `Date`. */
45
+ function daysInMonth(month, year) {
46
+ return new Date(year, month, 0).getDate();
47
+ }
48
+ function buildDateSegments(dateFormat) {
49
+ return tokenizeDateFormat(dateFormat).map((token) => {
50
+ if (token.kind === 'literal')
51
+ return { kind: 'literal', text: token.raw };
52
+ if (token.kind === 'year')
53
+ return { kind: 'year', length: YEAR_SEGMENT_LENGTH, text: '' };
54
+ if (token.kind === 'month')
55
+ return { kind: 'month', length: SHORT_SEGMENT_LENGTH, text: '' };
56
+ return { kind: 'day', length: SHORT_SEGMENT_LENGTH, text: '' };
57
+ });
58
+ }
59
+ function buildMonthSegments(dateFormat) {
60
+ const all = buildDateSegments(dateFormat);
61
+ const dayIdx = all.findIndex((s) => s.kind === 'day');
62
+ if (dayIdx === -1)
63
+ return all;
64
+ const dropIndexes = new Set([dayIdx]);
65
+ if (all[dayIdx + 1]?.kind === 'literal') {
66
+ dropIndexes.add(dayIdx + 1);
67
+ }
68
+ else if (all[dayIdx - 1]?.kind === 'literal') {
69
+ dropIndexes.add(dayIdx - 1);
70
+ }
71
+ return all.filter((_, i) => !dropIndexes.has(i));
72
+ }
73
+ function buildTimeSegments(hourFormat, showSeconds) {
74
+ const segments = [
75
+ { kind: 'hour', length: SHORT_SEGMENT_LENGTH, text: '' },
76
+ { kind: 'literal', text: ':' },
77
+ { kind: 'minute', length: SHORT_SEGMENT_LENGTH, text: '' },
78
+ ];
79
+ if (showSeconds) {
80
+ segments.push({ kind: 'literal', text: ':' }, { kind: 'second', length: SHORT_SEGMENT_LENGTH, text: '' });
81
+ }
82
+ if (hourFormat === '12') {
83
+ segments.push({ kind: 'literal', text: ' ' }, { kind: 'ampm', length: SHORT_SEGMENT_LENGTH, text: '' });
84
+ }
85
+ return segments;
86
+ }
87
+ function buildSegmentsFor(config) {
88
+ switch (config.mode) {
89
+ case 'date':
90
+ return buildDateSegments(config.dateFormat);
91
+ case 'month':
92
+ return buildMonthSegments(config.dateFormat);
93
+ case 'time':
94
+ return buildTimeSegments(config.hourFormat, config.showSeconds);
95
+ case 'datetime':
96
+ return [
97
+ ...buildDateSegments(config.dateFormat),
98
+ { kind: 'literal', text: ' ' },
99
+ ...buildTimeSegments(config.hourFormat, config.showSeconds),
100
+ ];
101
+ }
102
+ }
103
+ /**
104
+ * Engine de segmentos (dia/mês/ano/hora/minuto/segundo/ampm) para o trigger do datepicker.
105
+ * Sem dependências de Angular — DOM/CVA ficam por conta do componente que a consome.
106
+ */
107
+ class DateSegmentEngine {
108
+ segments = [];
109
+ config = null;
110
+ activeIndex = null;
111
+ freshEntry = true;
112
+ configure(config) {
113
+ this.config = config;
114
+ this.segments = buildSegmentsFor(config);
115
+ this.activeIndex = null;
116
+ this.freshEntry = true;
117
+ }
118
+ getDisplayString() {
119
+ return this.segments.map((s) => (isEditableSegment(s) ? this.renderSegment(s) : s.text)).join('');
120
+ }
121
+ renderSegment(segment) {
122
+ return segment.text + '_'.repeat(Math.max(0, segment.length - segment.text.length));
123
+ }
124
+ segmentRenderLength(segment) {
125
+ return isEditableSegment(segment) ? segment.length : segment.text.length;
126
+ }
127
+ getSegmentRange(index) {
128
+ let start = 0;
129
+ for (let i = 0; i < index; i++) {
130
+ start += this.segmentRenderLength(this.segments[i]);
131
+ }
132
+ return [start, start + this.segmentRenderLength(this.segments[index])];
133
+ }
134
+ getFirstEditableIndex() {
135
+ return this.segments.findIndex((s) => isEditableSegment(s));
136
+ }
137
+ getLastEditableIndex() {
138
+ for (let i = this.segments.length - 1; i >= 0; i--) {
139
+ if (isEditableSegment(this.segments[i]))
140
+ return i;
141
+ }
142
+ return -1;
143
+ }
144
+ getActiveIndex() {
145
+ return this.activeIndex;
146
+ }
147
+ activeKind() {
148
+ if (this.activeIndex === null)
149
+ return null;
150
+ const segment = this.segments[this.activeIndex];
151
+ return isEditableSegment(segment) ? segment.kind : null;
152
+ }
153
+ selectSegmentAt(charIndex) {
154
+ let acc = 0;
155
+ for (let i = 0; i < this.segments.length; i++) {
156
+ const len = this.segmentRenderLength(this.segments[i]);
157
+ if (charIndex < acc + len) {
158
+ return this.nearestEditableFrom(i);
159
+ }
160
+ acc += len;
161
+ }
162
+ return this.getLastEditableIndex();
163
+ }
164
+ nearestEditableFrom(index) {
165
+ for (let i = index; i < this.segments.length; i++) {
166
+ if (isEditableSegment(this.segments[i]))
167
+ return i;
168
+ }
169
+ return this.getLastEditableIndex();
170
+ }
171
+ selectSegment(index) {
172
+ if (index < 0 || index >= this.segments.length || !isEditableSegment(this.segments[index]))
173
+ return;
174
+ this.activeIndex = index;
175
+ this.freshEntry = true;
176
+ }
177
+ selectFirstSegment() {
178
+ this.selectSegment(this.getFirstEditableIndex());
179
+ }
180
+ /** Commita (pad/clamp) o segmento ativo e move para o próximo/anterior editável. */
181
+ moveToAdjacentSegment(direction) {
182
+ this.commitActiveSegment();
183
+ if (this.activeIndex === null)
184
+ return 'boundary';
185
+ let next = this.activeIndex;
186
+ do {
187
+ next += direction;
188
+ } while (next >= 0 && next < this.segments.length && !isEditableSegment(this.segments[next]));
189
+ if (next < 0 || next >= this.segments.length) {
190
+ this.activeIndex = null;
191
+ return 'boundary';
192
+ }
193
+ this.selectSegment(next);
194
+ return 'moved';
195
+ }
196
+ typeDigit(digit) {
197
+ if (this.activeIndex === null)
198
+ return;
199
+ const segment = this.segments[this.activeIndex];
200
+ if (!isEditableSegment(segment) || segment.kind === 'ampm')
201
+ return;
202
+ if (this.freshEntry) {
203
+ segment.text = '';
204
+ this.freshEntry = false;
205
+ }
206
+ if (segment.text.length >= segment.length)
207
+ return;
208
+ segment.text += digit;
209
+ if (segment.text.length >= segment.length) {
210
+ this.commitActiveSegment();
211
+ this.autoAdvance();
212
+ }
213
+ }
214
+ autoAdvance() {
215
+ if (this.activeIndex === null)
216
+ return;
217
+ let next = this.activeIndex;
218
+ do {
219
+ next += 1;
220
+ } while (next < this.segments.length && !isEditableSegment(this.segments[next]));
221
+ if (next < this.segments.length) {
222
+ this.selectSegment(next);
223
+ }
224
+ }
225
+ setAmPm(value) {
226
+ if (this.activeIndex === null)
227
+ return;
228
+ const segment = this.segments[this.activeIndex];
229
+ if (!isEditableSegment(segment) || segment.kind !== 'ampm')
230
+ return;
231
+ segment.text = value === 1 ? 'PM' : 'AM';
232
+ this.freshEntry = true;
233
+ }
234
+ clearActiveSegment(kind) {
235
+ if (this.activeIndex === null)
236
+ return;
237
+ const segment = this.segments[this.activeIndex];
238
+ if (!isEditableSegment(segment))
239
+ return;
240
+ if (segment.text === '') {
241
+ if (kind === 'backspace') {
242
+ let prev = this.activeIndex;
243
+ do {
244
+ prev -= 1;
245
+ } while (prev >= 0 && !isEditableSegment(this.segments[prev]));
246
+ if (prev >= 0) {
247
+ this.activeIndex = prev;
248
+ this.freshEntry = false;
249
+ }
250
+ }
251
+ return;
252
+ }
253
+ segment.text = '';
254
+ this.freshEntry = true;
255
+ }
256
+ /** Limpa todos os segmentos editáveis (usado quando o campo inteiro está selecionado, ex.: Ctrl+A). */
257
+ clearAll() {
258
+ for (const segment of this.segments) {
259
+ if (isEditableSegment(segment))
260
+ segment.text = '';
261
+ }
262
+ this.activeIndex = this.getFirstEditableIndex();
263
+ this.freshEntry = true;
264
+ }
265
+ /** Incrementa/decrementa o segmento ativo, dando a volta (wrap) dentro do range válido. */
266
+ incrementActiveSegment(delta) {
267
+ if (this.activeIndex === null)
268
+ return;
269
+ const segment = this.segments[this.activeIndex];
270
+ if (!isEditableSegment(segment))
271
+ return;
272
+ if (segment.kind === 'ampm') {
273
+ this.setAmPm(segment.text === 'PM' ? 0 : 1);
274
+ return;
275
+ }
276
+ const { min, max } = this.boundsFor(segment.kind);
277
+ const range = max - min + 1;
278
+ const current = segment.text ? parseInt(segment.text, 10) : delta > 0 ? min - 1 : min;
279
+ const next = (((current + delta - min) % range) + range) % range + min;
280
+ segment.text = String(next).padStart(segment.length, '0');
281
+ this.freshEntry = true;
282
+ this.reconcileDayIfNeeded(segment.kind);
283
+ }
284
+ /** Pad/clamp do segmento ativo (ex.: "2" -> "02", "45" no dia -> clamp para daysInMonth). */
285
+ commitActiveSegment() {
286
+ if (this.activeIndex === null)
287
+ return;
288
+ const segment = this.segments[this.activeIndex];
289
+ if (!isEditableSegment(segment))
290
+ return;
291
+ const kind = segment.kind;
292
+ segment.text = this.clampAndPad(segment);
293
+ this.freshEntry = true;
294
+ this.reconcileDayIfNeeded(kind);
295
+ }
296
+ /** Commita o segmento ativo e sai do modo de edição (usado no blur do campo). */
297
+ deactivate() {
298
+ this.commitActiveSegment();
299
+ this.activeIndex = null;
300
+ }
301
+ clampAndPad(segment) {
302
+ if (segment.kind === 'ampm' || segment.kind === 'year' || !segment.text) {
303
+ return segment.text;
304
+ }
305
+ const num = parseInt(segment.text, 10);
306
+ const { min, max } = this.boundsFor(segment.kind);
307
+ const clamped = Math.min(max, Math.max(min, num));
308
+ return String(clamped).padStart(segment.length, '0');
309
+ }
310
+ boundsFor(kind) {
311
+ switch (kind) {
312
+ case 'day':
313
+ return { min: 1, max: this.daysInMonthNow() };
314
+ case 'month':
315
+ return { min: 1, max: HOURS_PER_MERIDIEM };
316
+ case 'hour':
317
+ return this.config?.hourFormat === '12'
318
+ ? { min: 1, max: HOURS_PER_MERIDIEM }
319
+ : { min: 0, max: MAX_HOUR_24 };
320
+ case 'minute':
321
+ case 'second':
322
+ return { min: 0, max: MAX_MINUTE_SECOND };
323
+ default:
324
+ return { min: 0, max: 9999 };
325
+ }
326
+ }
327
+ /**
328
+ * Bound do dia dependendo do que já é conhecido de mês/ano. Nunca "adivinha" com o mês/ano
329
+ * atuais do sistema — isso estreitaria o range antes da hora (ex.: dia=29 digitado antes do
330
+ * ano, mês=fevereiro conhecido mas ano ainda não -> se assumisse o ano atual do sistema e ele
331
+ * não fosse bissexto, o dia seria clampado para 28 e nunca mais reexpandido para 29 mesmo que
332
+ * o ano realmente digitado depois seja bissexto). Em vez disso, fica sempre no lado permissivo
333
+ * até que a informação realmente exista.
334
+ */
335
+ daysInMonthNow() {
336
+ const monthSegment = this.findEditable('month');
337
+ const monthKnown = !!monthSegment?.text && monthSegment.text.length === monthSegment.length;
338
+ if (!monthSegment || !monthKnown)
339
+ return GENERIC_MAX_DAY;
340
+ const month = parseInt(monthSegment.text, 10);
341
+ const yearSegment = this.findEditable('year');
342
+ const yearKnown = !!yearSegment?.text && yearSegment.text.length === YEAR_SEGMENT_LENGTH;
343
+ if (!yearSegment || !yearKnown)
344
+ return daysInMonth(month, LEAP_REFERENCE_YEAR);
345
+ return daysInMonth(month, parseInt(yearSegment.text, 10));
346
+ }
347
+ reconcileDayIfNeeded(committedKind) {
348
+ if (committedKind !== 'month' && committedKind !== 'year')
349
+ return;
350
+ const daySegment = this.findEditable('day');
351
+ if (!daySegment || daySegment.text.length !== daySegment.length)
352
+ return;
353
+ const max = this.daysInMonthNow();
354
+ const dayNum = parseInt(daySegment.text, 10);
355
+ if (dayNum > max) {
356
+ daySegment.text = String(max).padStart(daySegment.length, '0');
357
+ }
358
+ }
359
+ findEditable(kind) {
360
+ return this.segments.find((s) => isEditableSegment(s) && s.kind === kind);
361
+ }
362
+ isEmpty() {
363
+ return this.segments.every((s) => !isEditableSegment(s) || s.text === '');
364
+ }
365
+ isComplete() {
366
+ return this.segments.every((s) => !isEditableSegment(s) || s.text.length === s.length);
367
+ }
368
+ loadFromDate(date) {
369
+ for (const segment of this.segments) {
370
+ if (isEditableSegment(segment))
371
+ segment.text = '';
372
+ }
373
+ this.activeIndex = null;
374
+ this.freshEntry = true;
375
+ if (!date)
376
+ return;
377
+ for (const segment of this.segments) {
378
+ if (!isEditableSegment(segment))
379
+ continue;
380
+ segment.text = this.renderValueFor(segment.kind, date);
381
+ }
382
+ }
383
+ renderValueFor(kind, date) {
384
+ switch (kind) {
385
+ case 'day':
386
+ return String(date.getDate()).padStart(SHORT_SEGMENT_LENGTH, '0');
387
+ case 'month':
388
+ return String(date.getMonth() + 1).padStart(SHORT_SEGMENT_LENGTH, '0');
389
+ case 'year':
390
+ return String(date.getFullYear()).padStart(YEAR_SEGMENT_LENGTH, '0');
391
+ case 'hour': {
392
+ const h = date.getHours();
393
+ const display = this.config?.hourFormat === '12' ? (h % HOURS_PER_MERIDIEM === 0 ? HOURS_PER_MERIDIEM : h % HOURS_PER_MERIDIEM) : h;
394
+ return String(display).padStart(SHORT_SEGMENT_LENGTH, '0');
395
+ }
396
+ case 'minute':
397
+ return String(date.getMinutes()).padStart(SHORT_SEGMENT_LENGTH, '0');
398
+ case 'second':
399
+ return String(date.getSeconds()).padStart(SHORT_SEGMENT_LENGTH, '0');
400
+ case 'ampm':
401
+ return date.getHours() < HOURS_PER_MERIDIEM ? 'AM' : 'PM';
402
+ }
403
+ }
404
+ /** Constrói o `Date` a partir dos segmentos; `null` enquanto incompleto. Usa `referenceDate` para preencher partes ausentes (ex.: hora ao editar só a data). */
405
+ toDate(referenceDate) {
406
+ if (!this.isComplete())
407
+ return null;
408
+ const ref = referenceDate ?? new Date();
409
+ const getNum = (kind) => {
410
+ const segment = this.findEditable(kind);
411
+ return segment ? parseInt(segment.text, 10) : null;
412
+ };
413
+ const year = getNum('year') ?? ref.getFullYear();
414
+ const monthNum = getNum('month');
415
+ const month = monthNum !== null ? monthNum - 1 : ref.getMonth();
416
+ const day = this.config?.mode === 'month' ? 1 : (getNum('day') ?? ref.getDate());
417
+ let hour = getNum('hour');
418
+ const minute = getNum('minute') ?? ref.getMinutes();
419
+ const second = getNum('second') ?? ref.getSeconds();
420
+ if (hour !== null && this.config?.hourFormat === '12') {
421
+ const ampmSegment = this.findEditable('ampm');
422
+ const isPm = ampmSegment?.text === 'PM';
423
+ hour = hour % HOURS_PER_MERIDIEM;
424
+ if (isPm)
425
+ hour += HOURS_PER_MERIDIEM;
426
+ }
427
+ return new Date(year, month, day, hour ?? ref.getHours(), minute, second);
428
+ }
429
+ }
11
430
 
12
431
  const DAY_MATCH_GROUP = 3;
13
- const MINUTES_MATCH_GROUP = 5;
14
- const MAX_MONTH_INDEX = 11;
15
432
  function formatNativeDate(date) {
16
433
  const year = date.getFullYear();
17
434
  const month = String(date.getMonth() + 1).padStart(2, '0');
@@ -20,8 +437,7 @@ function formatNativeDate(date) {
20
437
  }
21
438
  /**
22
439
  * Formata uma data segundo um padrão simples contendo os tokens `dd`, `MM` e `yyyy`.
23
- * Usado apenas para exibição em campos `type="text"` (modo `range`), onde o browser
24
- * não controla o formato. Para `type="date"` nativo o formato é definido pelo browser.
440
+ * Usado para exibição em campos `type="text"` (modos `range` e seleção múltipla).
25
441
  */
26
442
  function formatWithPattern(date, pattern) {
27
443
  const dd = String(date.getDate()).padStart(2, '0');
@@ -35,10 +451,6 @@ function formatWithPattern(date, pattern) {
35
451
  .replace('MM', MM)
36
452
  .replace('mm', MM); // PrimeNG compat: lowercase mm = month
37
453
  }
38
- function formatTime(h, m, s) {
39
- const base = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
40
- return s !== undefined ? `${base}:${String(s).padStart(2, '0')}` : base;
41
- }
42
454
  /** Parses yyyy-MM-dd. Returns null when invalid. */
43
455
  function parseDate(raw) {
44
456
  const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
@@ -78,19 +490,6 @@ function parseRangeInput(raw, separator = '—') {
78
490
  }
79
491
  return { start, end };
80
492
  }
81
- function parseTimeInput(raw, base, maxHours, maxMinutes) {
82
- const match = /^(\d{1,2}):(\d{2})$/.exec(raw.trim());
83
- if (!match) {
84
- return null;
85
- }
86
- const hours = parseInt(match[1], 10);
87
- const mins = parseInt(match[2], 10);
88
- if (hours >= maxHours || mins >= maxMinutes) {
89
- return null;
90
- }
91
- const baseDate = base ?? new Date();
92
- return new Date(baseDate.getFullYear(), baseDate.getMonth(), baseDate.getDate(), hours, mins);
93
- }
94
493
  function parseYearInput(raw) {
95
494
  const match = /^(\d{4})$/.exec(raw.trim());
96
495
  if (!match) {
@@ -102,59 +501,20 @@ function parseYearInput(raw) {
102
501
  }
103
502
  return new Date(year, 0, 1);
104
503
  }
105
- function parseMonthInput(raw) {
106
- const match = /^(\d{4})-(\d{2})$/.exec(raw.trim());
107
- if (!match) {
108
- return null;
109
- }
110
- const year = parseInt(match[1], 10);
111
- const month = parseInt(match[2], 10) - 1;
112
- if (year < 1000) {
113
- return null;
114
- }
115
- if (month < 0 || month > MAX_MONTH_INDEX) {
116
- return null;
117
- }
118
- return new Date(year, month, 1);
119
- }
120
- function parseDateTimeInput(raw) {
121
- const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(raw.trim());
122
- if (!match) {
123
- return null;
124
- }
125
- const year = parseInt(match[1], 10);
126
- const month = parseInt(match[2], 10) - 1;
127
- const day = parseInt(match[DAY_MATCH_GROUP], 10);
128
- const hours = parseInt(match[4], 10);
129
- const mins = parseInt(match[MINUTES_MATCH_GROUP], 10);
130
- if (year < 1000 || hours >= 24 || mins >= 60) {
131
- return null;
132
- }
133
- const date = new Date(year, month, day, hours, mins);
134
- if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
135
- return null;
136
- }
137
- return date;
138
- }
139
- function parseInput(mode, raw, base, maxHours, maxMinutes, rangeSeparator = '—') {
504
+ function parseInput(mode, raw, rangeSeparator = '—') {
140
505
  if (!raw?.trim()) {
141
506
  return null;
142
507
  }
143
508
  switch (mode) {
144
509
  case 'range':
145
510
  return parseRangeInput(raw, rangeSeparator);
146
- case 'time':
147
- return parseTimeInput(raw, base, maxHours, maxMinutes);
148
- case 'month':
149
- return parseMonthInput(raw);
150
511
  case 'year':
151
512
  return parseYearInput(raw);
152
- case 'datetime':
153
- return parseDateTimeInput(raw);
154
513
  default:
155
514
  return parseDate(raw);
156
515
  }
157
516
  }
517
+ /** Formata range/seleção múltipla (dateFormat) e o ano em `mode='year'`. Demais modos são resolvidos pela engine de segmentos. */
158
518
  function formatValueForInput(mode, value, rangeSeparator = ' — ', dateFormat, multipleSeparator = ', ') {
159
519
  if (!value) {
160
520
  return '';
@@ -172,23 +532,10 @@ function formatValueForInput(mode, value, rangeSeparator = ' — ', dateFormat,
172
532
  ? `${formatPart(range.start)}${rangeSeparator}${formatPart(range.end)}`
173
533
  : formatPart(range.start);
174
534
  }
175
- if (!(value instanceof Date)) {
176
- return '';
177
- }
178
- switch (mode) {
179
- case 'time':
180
- return formatTime(value.getHours(), value.getMinutes());
181
- case 'month': {
182
- const month = String(value.getMonth() + 1).padStart(2, '0');
183
- return `${value.getFullYear()}-${month}`;
184
- }
185
- case 'year':
186
- return String(value.getFullYear());
187
- case 'datetime':
188
- return `${formatNativeDate(value)}T${formatTime(value.getHours(), value.getMinutes())}`;
189
- default:
190
- return formatNativeDate(value);
535
+ if (value instanceof Date && mode === 'year') {
536
+ return String(value.getFullYear());
191
537
  }
538
+ return '';
192
539
  }
193
540
 
194
541
  function numericKeysOnly() {
@@ -217,6 +564,7 @@ class DatepickerComponent {
217
564
  static MAX_HOURS = 24;
218
565
  static MAX_MINUTES = 60;
219
566
  static HOURS_PER_MERIDIEM = 12;
567
+ static DOUBLE_CLICK_THRESHOLD_MS = 400;
220
568
  static nextId() {
221
569
  DatepickerComponent.idCounter++;
222
570
  return `s-datepicker-${DatepickerComponent.idCounter}`;
@@ -258,10 +606,16 @@ class DatepickerComponent {
258
606
  max = input(null);
259
607
  /** Data exibida ao abrir o calendário quando nenhuma data está selecionada. */
260
608
  defaultDate = input(null);
261
- /** Formato de exibição da data no campo (usado apenas para display). */
262
- dateFormat = input('dd/MM/yyyy');
263
- /** Formato de exibição das horas: `'12'` ou `'24'`. */
264
- hourFormat = input('24');
609
+ /**
610
+ * Formato de exibição da data no campo. Quando omitido, é resolvido a partir do
611
+ * locale ativo (chave `platform.angular_components.date_format`), com fallback para `dd/MM/yyyy`.
612
+ */
613
+ dateFormat = input(undefined);
614
+ /**
615
+ * Formato de exibição das horas: `'12'` ou `'24'`. Quando omitido, é resolvido a partir
616
+ * do locale ativo (chave `platform.angular_components.hour_format`), com fallback para `'24'`.
617
+ */
618
+ hourFormat = input(undefined);
265
619
  /** Incremento em minutos ao usar as setas do time picker. */
266
620
  minuteStep = input(DatepickerComponent.DEFAULT_MINUTE_STEP);
267
621
  /** Quando `true`, exibe o campo de segundos no time picker. */
@@ -339,19 +693,68 @@ class DatepickerComponent {
339
693
  pendingRangeStart = signal(null);
340
694
  hoverDate = signal(null);
341
695
  _hasFormatError = false;
342
- _isBadInput = false;
343
696
  onValidatorChange = () => { };
344
697
  _onChange = () => { };
345
698
  _onTouched = () => { };
346
699
  popupPanel = viewChild('popupPanel', { read: ElementRef });
347
700
  increaseHourButton = viewChild('increaseHourButton', { read: ElementRef });
348
701
  iconButton = viewChild('iconButton', { read: ElementRef });
702
+ triggerInput = viewChild.required('triggerInput', { read: ElementRef });
349
703
  injector = inject(Injector);
350
704
  hostElement = inject(ElementRef);
705
+ translateService = inject(TranslateService);
706
+ destroyRef = inject(DestroyRef);
707
+ currentLang = signal(this.translateService.currentLang);
708
+ /** Reflete `(pointer: coarse)` — dispositivos touch preferem o popup do calendário ao teclado. */
709
+ isTouchDevice = signal(typeof window !== 'undefined' && typeof window.matchMedia === 'function'
710
+ ? window.matchMedia('(pointer: coarse)').matches
711
+ : false);
712
+ segmentEngine = new DateSegmentEngine();
713
+ lastSegmentConfig = null;
714
+ measureCanvas;
715
+ /** Incrementado a cada mutação do usuário — invalida callbacks de clique/foco adiados que ficaram obsoletos. */
716
+ interactionGeneration = 0;
717
+ /** `timeStamp` do último mousedown no input segmentado — usado pra detectar duplo clique na mão. */
718
+ lastMouseDownAt = 0;
719
+ constructor() {
720
+ this.translateService.onLangChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
721
+ this.currentLang.set(event.lang);
722
+ });
723
+ if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {
724
+ const mediaQuery = window.matchMedia('(pointer: coarse)');
725
+ const handlePointerChange = (event) => this.isTouchDevice.set(event.matches);
726
+ mediaQuery.addEventListener('change', handlePointerChange);
727
+ this.destroyRef.onDestroy(() => mediaQuery.removeEventListener('change', handlePointerChange));
728
+ }
729
+ }
351
730
  // ── Computed ──────────────────────────────────────────────────────────
352
731
  resolvedIcon = computed(() => (this.mode() === 'time' ? 'fas fa-clock' : this.icon()));
732
+ /** `dateFormat` explícito ou resolvido a partir do locale ativo (fallback `dd/MM/yyyy`). */
733
+ resolvedDateFormat = computed(() => {
734
+ this.currentLang();
735
+ return (this.dateFormat() ??
736
+ this.resolveTranslatedOrDefault('platform.angular_components.date_format', 'dd/MM/yyyy'));
737
+ });
738
+ /** `hourFormat` explícito ou resolvido a partir do locale ativo (fallback `'24'`). */
739
+ resolvedHourFormat = computed(() => {
740
+ this.currentLang();
741
+ const explicit = this.hourFormat();
742
+ if (explicit)
743
+ return explicit;
744
+ const translated = this.resolveTranslatedOrDefault('platform.angular_components.hour_format', '24');
745
+ return translated === '12' ? '12' : '24';
746
+ });
747
+ /** Modos cujo formato de exibição é resolvido pela engine de segmentos (independente de touch/readonly). */
748
+ isSegmentModeCandidate = computed(() => {
749
+ if (this.isMultipleMode())
750
+ return false;
751
+ const mode = this.mode();
752
+ return mode === 'date' || mode === 'month' || mode === 'time' || mode === 'datetime';
753
+ });
754
+ /** Interatividade de digitação por segmento — desligada em touch ou quando `readonlyInput` é usado. */
755
+ isSegmentedMode = computed(() => this.isSegmentModeCandidate() && !this.isTouchDevice() && !this.readonlyInput());
353
756
  /** Indica se o time picker deve usar o formato de 12 horas com seletor AM/PM. */
354
- is12HourFormat = computed(() => this.hourFormat() === '12');
757
+ is12HourFormat = computed(() => this.resolvedHourFormat() === '12');
355
758
  /** Hora exibida no time picker: 1–12 no formato 12h, 0–23 no formato 24h. */
356
759
  displayHours = computed(() => {
357
760
  if (!this.is12HourFormat())
@@ -377,16 +780,6 @@ class DatepickerComponent {
377
780
  const start = this.firstDayOfWeek();
378
781
  return [...this.weekDayKeys.slice(start), ...this.weekDayKeys.slice(0, start)];
379
782
  });
380
- inputType = computed(() => {
381
- if (this.isMultipleMode())
382
- return 'text';
383
- const mode = this.mode();
384
- if (mode === 'datetime')
385
- return 'datetime-local';
386
- if (mode === 'range' || mode === 'year')
387
- return 'text';
388
- return mode;
389
- });
390
783
  dialogAriaLabelKey = computed(() => this.mode() === 'range'
391
784
  ? 'platform.angular_components.datepicker_select_range'
392
785
  : 'platform.angular_components.datepicker_select_date');
@@ -520,7 +913,7 @@ class DatepickerComponent {
520
913
  const d = this.defaultDate();
521
914
  if (d && !this.value() && !this.rangeValue() && this.multipleValues().length === 0) {
522
915
  this.value.set(d);
523
- this.inputDate = this.formatForDisplay(d);
916
+ this.setDisplayFor(d);
524
917
  this.viewDate.set(new Date(d.getFullYear(), d.getMonth(), 1));
525
918
  this._onChange(d);
526
919
  }
@@ -532,7 +925,7 @@ class DatepickerComponent {
532
925
  this.value.set(null);
533
926
  this.rangeValue.set(null);
534
927
  this.multipleValues.set([]);
535
- this.inputDate = '';
928
+ this.setDisplayFor(null);
536
929
  return;
537
930
  }
538
931
  if (Array.isArray(value)) {
@@ -552,8 +945,8 @@ class DatepickerComponent {
552
945
  return;
553
946
  }
554
947
  this.value.set(value);
555
- this.inputDate = this.formatForDisplay(value);
556
948
  const d = value;
949
+ this.setDisplayFor(d);
557
950
  this.viewDate.set(new Date(d.getFullYear(), d.getMonth(), 1));
558
951
  this.hours.set(d.getHours());
559
952
  this.minutes.set(d.getMinutes());
@@ -649,6 +1042,42 @@ class DatepickerComponent {
649
1042
  this.iconButton()?.nativeElement.focus();
650
1043
  }, { injector: this.injector });
651
1044
  }
1045
+ /**
1046
+ * O backdrop do CDK Overlay cobre a tela inteira, incluindo o próprio trigger — clicar no
1047
+ * input ou nos botões do trigger enquanto o painel já está aberto também dispara
1048
+ * `backdropClick` (o próprio `<input>` nunca recebe o `click` nesse caso, então
1049
+ * `handleInputClick` nunca dispara). Sem este filtro, clicar no input para trocar de
1050
+ * segmento fecharia o painel e roubaria o foco para o botão do ícone (via `closePanel`).
1051
+ * Quando o clique caiu especificamente sobre o `<input>`, reaplicamos a mesma lógica de
1052
+ * reposicionamento de segmento que `handleInputClick` faria, em vez de só ignorar o clique.
1053
+ */
1054
+ handleBackdropClick(event) {
1055
+ // `event.target` de um backdropClick do CDK Overlay é sempre o próprio <div> do
1056
+ // backdrop, nunca o elemento realmente clicado — então checar `.contains(event.target)`
1057
+ // nunca funciona. Em vez disso, comparamos a coordenada real do clique (que o
1058
+ // MouseEvent sempre carrega corretamente) contra os retângulos dos elementos do trigger.
1059
+ const inputEl = this.triggerInput()?.nativeElement;
1060
+ if (inputEl && this.isSegmentedMode()) {
1061
+ const inputRect = inputEl.getBoundingClientRect();
1062
+ const clickedOnInput = event.clientX >= inputRect.left &&
1063
+ event.clientX <= inputRect.right &&
1064
+ event.clientY >= inputRect.top &&
1065
+ event.clientY <= inputRect.bottom;
1066
+ if (clickedOnInput) {
1067
+ this.handleSegmentPointerDown(inputEl, event.clientX, event.timeStamp);
1068
+ return;
1069
+ }
1070
+ }
1071
+ const rect = this.hostElement.nativeElement.getBoundingClientRect();
1072
+ const clickedOnTrigger = event.clientX >= rect.left &&
1073
+ event.clientX <= rect.right &&
1074
+ event.clientY >= rect.top &&
1075
+ event.clientY <= rect.bottom;
1076
+ if (clickedOnTrigger) {
1077
+ return;
1078
+ }
1079
+ this.closePanel();
1080
+ }
652
1081
  toggle() {
653
1082
  if (this.isOpen()) {
654
1083
  this.closePanel();
@@ -662,14 +1091,169 @@ class DatepickerComponent {
662
1091
  this.closePanel();
663
1092
  }
664
1093
  }
665
- handleInputClick() { }
1094
+ handleInputClick(event) {
1095
+ if (!this.isSegmentedMode() || !(event.target instanceof HTMLInputElement)) {
1096
+ return;
1097
+ }
1098
+ // Bound to `mousedown`, não `click` (veja o comentário em `applySegmentClick`). O
1099
+ // comportamento nativo padrão do browser pra mousedown num `<input>` de texto é
1100
+ // posicionar o caret/iniciar seleção de texto na coordenada do clique — isso roda
1101
+ // *depois* dos nossos listeners e sobrescreveria o `setSelectionRange` do segmento
1102
+ // aplicado logo abaixo. `preventDefault()` evita essa corrida.
1103
+ event.preventDefault();
1104
+ this.handleSegmentPointerDown(event.target, event.clientX, event.timeStamp);
1105
+ }
1106
+ /**
1107
+ * Ponto de entrada único pro clique no input segmentado — chamado tanto por
1108
+ * `handleInputClick` (mousedown direto no `<input>`, o primeiro clique, antes do backdrop
1109
+ * existir) quanto por `handleBackdropClick` (cliques seguintes, já com o painel aberto e o
1110
+ * backdrop cobrindo o input). Precisa ser único porque os dois cliques de um duplo clique
1111
+ * real costumam chegar por caminhos DIFERENTES — o primeiro via mousedown no input, o
1112
+ * segundo via backdropClick — então rastrear o timestamp do "último clique" separadamente
1113
+ * em cada handler nunca detectaria um duplo clique de verdade. Nem `dblclick` nem
1114
+ * `event.detail` são confiáveis aqui (dependem da contagem/targeting interna de cliques do
1115
+ * browser, que nossas manipulações de foco/seleção parecem atrapalhar — observado
1116
+ * empiricamente: `event.detail` fica sempre 1), daí a detecção manual por timestamp.
1117
+ */
1118
+ handleSegmentPointerDown(input, clientX, timeStamp) {
1119
+ const isDoubleClick = timeStamp - this.lastMouseDownAt < DatepickerComponent.DOUBLE_CLICK_THRESHOLD_MS;
1120
+ this.lastMouseDownAt = timeStamp;
1121
+ if (isDoubleClick) {
1122
+ this.lastMouseDownAt = 0; // evita que um terceiro clique encadeie em outro "duplo"
1123
+ this.selectWholeField(input);
1124
+ return;
1125
+ }
1126
+ this.applySegmentClick(input, clientX);
1127
+ }
1128
+ /**
1129
+ * Seleciona o segmento sob a coordenada `clientX` de um clique no input segmentado.
1130
+ * Compartilhado por `handleInputClick` (mousedown real no `<input>` — usamos `mousedown`
1131
+ * em vez de `click` porque o `focus`/`openOnFocus` disparado no meio do gesto pode montar
1132
+ * o backdrop do CDK Overlay embaixo do cursor antes do `mouseup`, fazendo o browser resolver
1133
+ * o `click` pro ancestral comum entre o alvo do mousedown e o do mouseup — na prática,
1134
+ * `<body>` — em vez do `<input>` ou do backdrop) e `handleBackdropClick` (clique no trigger
1135
+ * com o painel já aberto em regime estável, interceptado pelo backdrop do CDK Overlay).
1136
+ */
1137
+ applySegmentClick(input, clientX) {
1138
+ this.ensureSegmentEngineConfigured();
1139
+ // Calcula o segmento a partir da coordenada do clique (clientX), não de
1140
+ // `input.selectionStart` — o browser ajusta a posição do caret de forma assíncrona
1141
+ // após o clique (observado empiricamente, especialmente na transição de foco), então
1142
+ // ler `selectionStart` nesse momento é uma fonte não confiável. `clientX` é síncrono
1143
+ // e imediato, sem essa corrida.
1144
+ const charIndex = this.clickClientXToCharIndex(input, clientX);
1145
+ const generation = ++this.interactionGeneration;
1146
+ // Foca e seleciona o segmento juntos, e reaplica os dois algumas vezes logo em seguida —
1147
+ // o browser/CDK Overlay pode mexer no foco de forma assíncrona depois do clique (blur
1148
+ // nativo do backdrop, cdkTrapFocus do painel, etc.); em vez de tentar prever/evitar cada
1149
+ // causa, simplesmente vencemos reafirmando foco+seleção por cima.
1150
+ const focusAndSelect = () => {
1151
+ if (generation !== this.interactionGeneration)
1152
+ return;
1153
+ const index = this.segmentEngine.selectSegmentAt(charIndex);
1154
+ this.segmentEngine.selectSegment(index);
1155
+ input.focus();
1156
+ this.selectActiveSegmentRange(input);
1157
+ };
1158
+ focusAndSelect();
1159
+ setTimeout(focusAndSelect, 0);
1160
+ setTimeout(focusAndSelect, 50);
1161
+ }
1162
+ /** Duplo clique seleciona o input inteiro (todos os segmentos), como num `<input>` de texto comum. */
1163
+ handleInputDblClick(event) {
1164
+ if (!this.isSegmentedMode() || !(event.target instanceof HTMLInputElement))
1165
+ return;
1166
+ this.selectWholeField(event.target);
1167
+ }
1168
+ /**
1169
+ * Seleciona todos os segmentos do input (usado tanto pelo duplo clique detectado na mão em
1170
+ * `handleSegmentPointerDown` quanto por `handleInputDblClick`, mantido como caminho direto
1171
+ * pra quem dispara um `dblclick` de verdade, ex.: acessibilidade/testes).
1172
+ */
1173
+ selectWholeField(input) {
1174
+ this.ensureSegmentEngineConfigured();
1175
+ // Garante um segmento ativo *antes* de focar: se o CDK Overlay tiver roubado o foco de
1176
+ // forma assíncrona (blur) entre os cliques que compõem o duplo clique, o `input.focus()`
1177
+ // abaixo dispara um evento `focus` genuíno, e handleInputFocus só evita agendar sua própria
1178
+ // reaplicação concorrente (que sobrescreveria nossa seleção do campo inteiro mais tarde)
1179
+ // quando encontra `getActiveIndex() !== null` — o mesmo motivo pelo qual handleInputClick
1180
+ // sempre chama `segmentEngine.selectSegment(index)` antes de `input.focus()`.
1181
+ if (this.segmentEngine.getActiveIndex() === null) {
1182
+ this.segmentEngine.selectFirstSegment();
1183
+ }
1184
+ const generation = ++this.interactionGeneration;
1185
+ // Mesmo padrão defensivo de handleInputClick: o clique que fecha o duplo clique já rodou
1186
+ // handleInputClick (selecionando só um segmento) e pode ter agendado suas próprias
1187
+ // reaplicações adiadas — a geração recém-incrementada as invalida, então só a seleção do
1188
+ // campo inteiro sobrevive.
1189
+ const focusAndSelectAll = () => {
1190
+ if (generation !== this.interactionGeneration)
1191
+ return;
1192
+ input.focus();
1193
+ input.setSelectionRange(0, input.value.length);
1194
+ };
1195
+ focusAndSelectAll();
1196
+ setTimeout(focusAndSelectAll, 0);
1197
+ setTimeout(focusAndSelectAll, 50);
1198
+ }
666
1199
  handleInputFocus(event) {
667
1200
  this.isFocused.set(true);
668
1201
  this.focused.emit(event);
1202
+ if (this.isSegmentedMode() && event.target instanceof HTMLInputElement) {
1203
+ this.ensureSegmentEngineConfigured();
1204
+ const input = event.target;
1205
+ if (input.value === '') {
1206
+ input.value = this.segmentEngine.getDisplayString();
1207
+ }
1208
+ if (this.segmentEngine.getActiveIndex() === null) {
1209
+ // Foco "genuíno" (Tab a partir de outro campo, ou primeiro foco no campo) —
1210
+ // nenhum segmento ativo ainda, então seleciona o primeiro.
1211
+ const generation = ++this.interactionGeneration;
1212
+ this.segmentEngine.selectFirstSegment();
1213
+ this.selectActiveSegmentRange(input);
1214
+ setTimeout(() => {
1215
+ if (generation !== this.interactionGeneration)
1216
+ return;
1217
+ if (this.segmentEngine.getActiveIndex() !== null) {
1218
+ this.selectActiveSegmentRange(input);
1219
+ }
1220
+ }, 0);
1221
+ }
1222
+ else {
1223
+ // Já existe um segmento ativo — por exemplo, handleInputClick acabou de
1224
+ // defini-lo e está refocando o input programaticamente após um blur espúrio.
1225
+ // Não reinicia para o primeiro segmento; só garante que a seleção visual bata
1226
+ // com o estado atual.
1227
+ this.selectActiveSegmentRange(input);
1228
+ }
1229
+ }
669
1230
  if (this.showOnFocus()) {
670
1231
  this.openOnFocus();
671
1232
  }
672
1233
  }
1234
+ /** Mapeia a coordenada X (viewport) de um clique para o índice de caractere mais próximo no valor exibido. */
1235
+ clickClientXToCharIndex(input, clientX) {
1236
+ const style = getComputedStyle(input);
1237
+ const canvas = (this.measureCanvas ??= document.createElement('canvas'));
1238
+ const ctx = canvas.getContext('2d');
1239
+ if (!ctx)
1240
+ return 0;
1241
+ ctx.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
1242
+ const rect = input.getBoundingClientRect();
1243
+ const paddingLeft = parseFloat(style.paddingLeft) || 0;
1244
+ const localX = clientX - rect.left - paddingLeft + input.scrollLeft;
1245
+ const text = input.value;
1246
+ let bestIndex = 0;
1247
+ let bestDistance = Infinity;
1248
+ for (let i = 0; i <= text.length; i++) {
1249
+ const distance = Math.abs(ctx.measureText(text.slice(0, i)).width - localX);
1250
+ if (distance < bestDistance) {
1251
+ bestDistance = distance;
1252
+ bestIndex = i;
1253
+ }
1254
+ }
1255
+ return bestIndex;
1256
+ }
673
1257
  handleNativeInput(event) {
674
1258
  this.valueInput.emit(event);
675
1259
  }
@@ -677,14 +1261,27 @@ class DatepickerComponent {
677
1261
  this.isFocused.set(false);
678
1262
  this.blurred.emit(event);
679
1263
  const input = event.target;
680
- this._isBadInput = input.validity?.badInput ?? false;
1264
+ const wasSegmented = this.isSegmentedMode();
1265
+ if (wasSegmented) {
1266
+ this.ensureSegmentEngineConfigured();
1267
+ this.segmentEngine.deactivate();
1268
+ this.commitSegmentedValueIfComplete();
1269
+ this.inputDate = this.segmentEngine.isEmpty() ? '' : this.segmentEngine.getDisplayString();
1270
+ input.value = this.inputDate;
1271
+ }
681
1272
  if (!input.value?.trim()) {
682
1273
  this.onValidatorChange();
683
1274
  }
684
1275
  if (this.isOpen()) {
685
1276
  const relatedTarget = event.relatedTarget;
686
1277
  const panel = this.popupPanel()?.nativeElement;
687
- if (!relatedTarget || !panel?.contains(relatedTarget)) {
1278
+ // Em modo segmentado, um blur sem `relatedTarget` normalmente não é uma saída real
1279
+ // de foco — é o backdrop transparente do próprio popup (não focável) interceptando
1280
+ // um clique no trigger para trocar de segmento. Não redireciona o foco pro painel
1281
+ // nesse caso; deixa handleInputClick/handleInputFocus resolverem (eles refocam o
1282
+ // input e reaplicam o segmento correto logo em seguida).
1283
+ const looksLikeOwnTriggerClick = wasSegmented && !relatedTarget;
1284
+ if (!looksLikeOwnTriggerClick && (!relatedTarget || !panel?.contains(relatedTarget))) {
688
1285
  if (this.mode() === 'time') {
689
1286
  this.increaseHourButton()?.nativeElement.focus();
690
1287
  }
@@ -698,6 +1295,8 @@ class DatepickerComponent {
698
1295
  }
699
1296
  }
700
1297
  handleInputChange(raw) {
1298
+ if (this.isSegmentModeCandidate())
1299
+ return;
701
1300
  if (!raw?.trim()) {
702
1301
  this.value.set(null);
703
1302
  this.rangeValue.set(null);
@@ -706,14 +1305,13 @@ class DatepickerComponent {
706
1305
  this._onChange(null);
707
1306
  return;
708
1307
  }
709
- const parsed = parseInput(this.mode(), raw, this.value(), this.maxHours, this.maxMinutes, this.rangeSeparator());
1308
+ const parsed = parseInput(this.mode(), raw, this.rangeSeparator());
710
1309
  if (parsed === null) {
711
1310
  this._hasFormatError = true;
712
1311
  this.onValidatorChange();
713
1312
  return;
714
1313
  }
715
1314
  this._hasFormatError = false;
716
- this._isBadInput = false;
717
1315
  this.onValidatorChange();
718
1316
  if (this.isDateRange(parsed)) {
719
1317
  this.rangeValue.set(parsed);
@@ -729,11 +1327,121 @@ class DatepickerComponent {
729
1327
  }
730
1328
  }
731
1329
  handleTriggerKeydown(event) {
1330
+ if (this.isSegmentedMode() && event.target instanceof HTMLInputElement) {
1331
+ this.handleSegmentedKeydown(event, event.target);
1332
+ }
732
1333
  if (event.key === 'Enter' || event.key === ' ') {
733
1334
  event.preventDefault();
734
1335
  this.open();
735
1336
  }
736
1337
  }
1338
+ /** Teclas que devem passar direto para o browser sem inserir/alterar nada no campo. */
1339
+ static SEGMENTED_PASSTHROUGH_KEYS = ['Home', 'End', 'Shift', 'CapsLock', 'Escape', 'Enter', ' '];
1340
+ /** Digitação por segmento (dia/mês/ano/hora/minuto/segundo/ampm) no input do trigger. */
1341
+ handleSegmentedKeydown(event, input) {
1342
+ this.ensureSegmentEngineConfigured();
1343
+ const { key, ctrlKey, metaKey, altKey, shiftKey } = event;
1344
+ if (ctrlKey || metaKey || altKey || key.startsWith('F'))
1345
+ return;
1346
+ if (DatepickerComponent.SEGMENTED_PASSTHROUGH_KEYS.includes(key))
1347
+ return;
1348
+ this.interactionGeneration++;
1349
+ if (key === 'Tab') {
1350
+ const result = this.segmentEngine.moveToAdjacentSegment(shiftKey ? -1 : 1);
1351
+ this.syncSegmentedInput(input);
1352
+ if (result === 'moved') {
1353
+ event.preventDefault();
1354
+ }
1355
+ return;
1356
+ }
1357
+ if (key === 'ArrowLeft' || key === 'ArrowRight') {
1358
+ event.preventDefault();
1359
+ // Ao contrário do Tab, seta no primeiro/último segmento não deve sair do campo nem
1360
+ // desativar a seleção — `moveToAdjacentSegment` zera `activeIndex` ao bater no limite
1361
+ // (comportamento certo pro Tab escapar do campo), então na borda a gente reseleciona
1362
+ // o segmento que já estava ativo, imitando o `<input type="date">` nativo (a seta some
1363
+ // no limite, não navega pra fora).
1364
+ const before = this.segmentEngine.getActiveIndex();
1365
+ const result = this.segmentEngine.moveToAdjacentSegment(key === 'ArrowLeft' ? -1 : 1);
1366
+ if (result === 'boundary' && before !== null) {
1367
+ this.segmentEngine.selectSegment(before);
1368
+ }
1369
+ this.syncSegmentedInput(input);
1370
+ return;
1371
+ }
1372
+ if (key === 'ArrowUp' || key === 'ArrowDown') {
1373
+ event.preventDefault();
1374
+ this.segmentEngine.incrementActiveSegment(key === 'ArrowUp' ? 1 : -1);
1375
+ this.syncSegmentedInput(input);
1376
+ return;
1377
+ }
1378
+ if (key === 'Backspace' || key === 'Delete') {
1379
+ event.preventDefault();
1380
+ const isWholeFieldSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
1381
+ if (isWholeFieldSelected) {
1382
+ this.segmentEngine.clearAll();
1383
+ }
1384
+ else {
1385
+ this.segmentEngine.clearActiveSegment(key === 'Backspace' ? 'backspace' : 'delete');
1386
+ }
1387
+ this.syncSegmentedInput(input);
1388
+ return;
1389
+ }
1390
+ if (/^[0-9]$/.test(key)) {
1391
+ event.preventDefault();
1392
+ this.segmentEngine.typeDigit(key);
1393
+ this.syncSegmentedInput(input);
1394
+ return;
1395
+ }
1396
+ if (this.segmentEngine.activeKind() === 'ampm' && /^[apAP]$/.test(key)) {
1397
+ event.preventDefault();
1398
+ this.segmentEngine.setAmPm(key.toLowerCase() === 'a' ? 0 : 1);
1399
+ this.syncSegmentedInput(input);
1400
+ return;
1401
+ }
1402
+ // Qualquer outra tecla imprimível (letras, símbolos) não é inserida no campo.
1403
+ event.preventDefault();
1404
+ }
1405
+ /** Sincroniza o DOM com o estado atual da engine (valor + seleção do segmento ativo) após uma mutação. */
1406
+ syncSegmentedInput(input) {
1407
+ this.inputDate = this.segmentEngine.getDisplayString();
1408
+ input.value = this.inputDate;
1409
+ this.selectActiveSegmentRange(input);
1410
+ input.dispatchEvent(new Event('input', { bubbles: true }));
1411
+ this.commitSegmentedValueIfComplete();
1412
+ }
1413
+ selectActiveSegmentRange(input) {
1414
+ const index = this.segmentEngine.getActiveIndex();
1415
+ if (index === null)
1416
+ return;
1417
+ const [start, end] = this.segmentEngine.getSegmentRange(index);
1418
+ input.setSelectionRange(start, end);
1419
+ }
1420
+ /** Propaga o valor da engine para o modelo (CVA) assim que todos os segmentos estiverem completos. */
1421
+ commitSegmentedValueIfComplete() {
1422
+ if (!this.segmentEngine.isComplete()) {
1423
+ if (this.value() !== null) {
1424
+ this.value.set(null);
1425
+ this._onChange(null);
1426
+ }
1427
+ return;
1428
+ }
1429
+ const base = this.value() ??
1430
+ new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate(), this.hours(), this.minutes(), this.showSeconds() ? this.seconds() : 0);
1431
+ const date = this.segmentEngine.toDate(base);
1432
+ if (!date)
1433
+ return;
1434
+ this.value.set(date);
1435
+ if (this.showCalendar()) {
1436
+ this.viewDate.set(new Date(date.getFullYear(), date.getMonth(), 1));
1437
+ }
1438
+ if (this.showTimePicker()) {
1439
+ this.hours.set(date.getHours());
1440
+ this.minutes.set(date.getMinutes());
1441
+ this.seconds.set(date.getSeconds());
1442
+ }
1443
+ this._onChange(date);
1444
+ }
737
1445
  // ── Navigation ────────────────────────────────────────────────────────
738
1446
  navigatePrev() {
739
1447
  const view = this.viewDate();
@@ -861,13 +1569,13 @@ class DatepickerComponent {
861
1569
  const newDate = new Date(day.date.getFullYear(), day.date.getMonth(), day.date.getDate(), this.hours(), this.minutes(), this.showSeconds() ? this.seconds() : 0);
862
1570
  this.value.set(newDate);
863
1571
  if (this.mode() === 'date') {
864
- this.inputDate = this.formatForDisplay(newDate);
1572
+ this.setDisplayFor(newDate);
865
1573
  this._onChange(newDate);
866
1574
  this.selected.emit(newDate);
867
1575
  this.closePanel();
868
1576
  }
869
1577
  else if (this.mode() === 'datetime') {
870
- this.inputDate = this.formatForDisplay(newDate);
1578
+ this.setDisplayFor(newDate);
871
1579
  this._onChange(newDate);
872
1580
  this.selected.emit(newDate);
873
1581
  if (this.hideOnDateTimeSelect()) {
@@ -907,7 +1615,7 @@ class DatepickerComponent {
907
1615
  const newViewDate = new Date(year, monthIndex, 1);
908
1616
  if (this.mode() === 'month') {
909
1617
  this.value.set(newViewDate);
910
- this.inputDate = this.formatForDisplay(newViewDate);
1618
+ this.setDisplayFor(newViewDate);
911
1619
  this._onChange(newViewDate);
912
1620
  this.selected.emit(newViewDate);
913
1621
  this.closePanel();
@@ -944,7 +1652,7 @@ class DatepickerComponent {
944
1652
  this.rangeValue.set(null);
945
1653
  this.multipleValues.set([]);
946
1654
  this.pendingRangeStart.set(null);
947
- this.inputDate = '';
1655
+ this.setDisplayFor(null);
948
1656
  this._onChange(null);
949
1657
  this.clearClick.emit(event);
950
1658
  this.closePanel();
@@ -953,7 +1661,7 @@ class DatepickerComponent {
953
1661
  const current = this.value() ?? new Date();
954
1662
  const confirmed = new Date(current.getFullYear(), current.getMonth(), current.getDate(), this.hours(), this.minutes(), this.showSeconds() ? this.seconds() : 0);
955
1663
  this.value.set(confirmed);
956
- this.inputDate = this.formatForDisplay(confirmed);
1664
+ this.setDisplayFor(confirmed);
957
1665
  this._onChange(confirmed);
958
1666
  this.selected.emit(confirmed);
959
1667
  this.closePanel();
@@ -1068,7 +1776,7 @@ class DatepickerComponent {
1068
1776
  if (disabledDays.length && disabledDays.includes(date.getDay()))
1069
1777
  return true;
1070
1778
  const disabledDates = this.disabledDates();
1071
- if (disabledDates.length && disabledDates.some(d => isSameDay(d, date)))
1779
+ if (disabledDates.length && disabledDates.some((d) => isSameDay(d, date)))
1072
1780
  return true;
1073
1781
  return false;
1074
1782
  }
@@ -1108,7 +1816,7 @@ class DatepickerComponent {
1108
1816
  return String(value).padStart(2, '0');
1109
1817
  }
1110
1818
  formatDayAriaLabel(date) {
1111
- return date.toLocaleDateString(undefined, {
1819
+ return date.toLocaleDateString(this.currentLang(), {
1112
1820
  weekday: 'long',
1113
1821
  year: 'numeric',
1114
1822
  month: 'long',
@@ -1117,13 +1825,62 @@ class DatepickerComponent {
1117
1825
  }
1118
1826
  /** Formata o valor para exibição no input, aplicando rangeSeparator, dateFormat e multipleSeparator. */
1119
1827
  formatForDisplay(value) {
1120
- return formatValueForInput(this.mode(), value, this.rangeSeparator(), this.dateFormat(), this.multipleSeparator());
1828
+ return formatValueForInput(this.mode(), value, this.rangeSeparator(), this.resolvedDateFormat(), this.multipleSeparator());
1829
+ }
1830
+ /**
1831
+ * Atualiza `inputDate` a partir de um valor único, roteando pela engine de segmentos
1832
+ * (date/month/time/datetime) ou pelo formatador legado (range/year/multiple).
1833
+ */
1834
+ setDisplayFor(value) {
1835
+ if (this.isSegmentModeCandidate()) {
1836
+ this.ensureSegmentEngineConfigured();
1837
+ this.segmentEngine.loadFromDate(value);
1838
+ this.inputDate = value ? this.segmentEngine.getDisplayString() : '';
1839
+ return;
1840
+ }
1841
+ this.inputDate = this.formatForDisplay(value);
1842
+ }
1843
+ /**
1844
+ * (Re)configura a engine de segmentos quando `mode`/`dateFormat`/`hourFormat`/`showSeconds`
1845
+ * mudaram desde a última chamada, recarregando o valor atual. Chamado de forma síncrona a
1846
+ * partir de todo ponto de entrada que usa a engine — não depende de um `effect()` (que só
1847
+ * roda em um ciclo de change detection, tarde demais para leituras/escritas imediatas).
1848
+ */
1849
+ ensureSegmentEngineConfigured() {
1850
+ const mode = this.mode();
1851
+ if (mode !== 'date' && mode !== 'month' && mode !== 'time' && mode !== 'datetime')
1852
+ return;
1853
+ const config = {
1854
+ mode,
1855
+ dateFormat: this.resolvedDateFormat(),
1856
+ hourFormat: this.resolvedHourFormat(),
1857
+ showSeconds: this.showSeconds(),
1858
+ };
1859
+ const last = this.lastSegmentConfig;
1860
+ if (last &&
1861
+ last.mode === config.mode &&
1862
+ last.dateFormat === config.dateFormat &&
1863
+ last.hourFormat === config.hourFormat &&
1864
+ last.showSeconds === config.showSeconds) {
1865
+ return;
1866
+ }
1867
+ this.lastSegmentConfig = config;
1868
+ this.segmentEngine.configure(config);
1869
+ this.segmentEngine.loadFromDate(this.value());
1870
+ }
1871
+ /** Retorna a tradução de `key`, ou `fallback` quando a chave não está traduzida (ngx-translate retorna a própria chave). */
1872
+ resolveTranslatedOrDefault(key, fallback) {
1873
+ const translated = this.translateService.instant(key);
1874
+ if (typeof translated !== 'string' || !translated || translated === key) {
1875
+ return fallback;
1876
+ }
1877
+ return translated;
1121
1878
  }
1122
1879
  syncTimeToValue() {
1123
1880
  const date = this.value() ?? new Date();
1124
1881
  const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), this.hours(), this.minutes(), this.showSeconds() ? this.seconds() : 0);
1125
1882
  this.value.set(newDate);
1126
- this.inputDate = this.formatForDisplay(newDate);
1883
+ this.setDisplayFor(newDate);
1127
1884
  if (this.mode() === 'time') {
1128
1885
  this._onChange(newDate);
1129
1886
  }
@@ -1155,8 +1912,6 @@ class DatepickerComponent {
1155
1912
  computeValidationErrors(value, control) {
1156
1913
  if (this._hasFormatError)
1157
1914
  return { invalidFormat: true };
1158
- if (this._isBadInput)
1159
- return { badInput: true };
1160
1915
  if (!value || (Array.isArray(value) && value.length === 0))
1161
1916
  return Validators.nullValidator(control);
1162
1917
  const min = this.min();
@@ -1208,7 +1963,7 @@ class DatepickerComponent {
1208
1963
  useExisting: DatepickerComponent,
1209
1964
  multi: true,
1210
1965
  },
1211
- ], viewQueries: [{ propertyName: "popupPanel", first: true, predicate: ["popupPanel"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "increaseHourButton", first: true, predicate: ["increaseHourButton"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "iconButton", first: true, predicate: ["iconButton"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n class=\"relative flex w-full items-center\"\n role=\"combobox\"\n aria-haspopup=\"grid\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"popupId()\"\n>\n <input\n class=\"hide-native-icon h-[35px] w-full rounded-small border border-solid border-grayscale-30 pl-3 text-sm outline-none transition-colors focus:border-primary\"\n [class.pr-10]=\"showIcon() && !showClear()\"\n [class.pr-16]=\"showIcon() && showClear()\"\n [class.pr-6]=\"!showIcon() && showClear()\"\n [class.bg-grayscale-0]=\"!disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.bg-grayscale-5]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [id]=\"inputId()\"\n [name]=\"name() ?? inputId()\"\n [type]=\"inputType()\"\n [placeholder]=\"placeholder() ?? (placeholderKey() | translate)\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonlyInput() || mode() === 'range' || mode() === 'year' || isMultipleMode()\"\n [tabIndex]=\"tabindex() ?? null\"\n [autofocus]=\"autofocus()\"\n [(ngModel)]=\"inputDate\"\n (ngModelChange)=\"handleInputChange($event)\"\n (focus)=\"handleInputFocus($event)\"\n (blur)=\"handleInputBlur($event)\"\n (click)=\"handleInputClick()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n (input)=\"handleNativeInput($event)\"\n [attr.aria-invalid]=\"ngControl?.control?.invalid || null\"\n />\n\n @if (showClear() && inputDate) {\n <button\n type=\"button\"\n class=\"absolute flex h-[35px] w-6 items-center justify-center text-grayscale-60 transition-colors hover:text-grayscale-90\"\n [class.right-9]=\"showIcon()\"\n [class.right-0]=\"!showIcon()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_clear' | translate\"\n (click)=\"clearValue($event)\"\n >\n <i class=\"fas fa-times text-xs\" aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (showIcon()) {\n <button\n #iconButton\n type=\"button\"\n class=\"absolute right-0 flex h-[35px] w-9 items-center justify-center border-l border-solid transition-colors\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [class.border-grayscale-30]=\"!disabled() && !isFocused()\"\n [class.border-primary]=\"isFocused() && !disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.hover:bg-grayscale-10]=\"!disabled()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_open' | translate\"\n (click)=\"toggle()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n >\n <i [class]=\"resolvedIcon()\" aria-hidden=\"true\"></i>\n </button>\n }\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (backdropClick)=\"closePanel()\"\n (overlayKeydown)=\"handleOverlayKeydown($event)\"\n (detach)=\"isOpen.set(false)\"\n>\n <div\n #popupPanel\n class=\"z-50 w-72 rounded-medium border border-solid border-grayscale-30 bg-grayscale-0 shadow-md\"\n role=\"dialog\"\n cdkTrapFocus\n [id]=\"popupId()\"\n [attr.aria-label]=\"dialogAriaLabelKey() | translate\"\n >\n @if (showCalendar()) {\n @if (pickerView() === 'calendar') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o mensal -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_month' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i class=\"fas fa-chevron-left text-xs\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex items-center gap-1 rounded-small px-2 py-1 text-sm font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month_year' | translate\"\n (click)=\"switchToMonths()\"\n >\n {{ currentMonthKey() | translate }}&nbsp;{{ currentYear() }}\n <i class=\"fas fa-chevron-down text-[10px]\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_month' | translate\"\n (click)=\"navigateNext()\"\n >\n <i class=\"fas fa-chevron-right text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <!-- Cabe\u00E7alhos dos dias da semana -->\n <div class=\"grid grid-cols-7 px-3 pt-2\" role=\"row\">\n @for (dayKey of orderedWeekDayKeys(); track dayKey) {\n <div\n class=\"flex h-8 items-center justify-center text-xs font-medium text-grayscale-60\"\n role=\"columnheader\"\n [attr.aria-label]=\"dayKey | translate\"\n >\n {{ dayKey | translate }}\n </div>\n }\n </div>\n\n <!-- Grade do calend\u00E1rio -->\n <div\n class=\"grid grid-cols-7 px-3 pb-2\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_calendar_label' | translate: { month: (currentMonthKey() | translate), year: currentYear() }\"\n (keydown)=\"handleCalendarGridKeydown($event)\"\n >\n @for (week of calendarWeeks(); track $index) {\n <div role=\"row\" class=\"contents\">\n @for (day of week; track day.date.toISOString()) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-8 w-8 items-center justify-center rounded-[50%] text-sm font-normal transition-colors\"\n [class.invisible]=\"!showOtherMonths() && !day.isCurrentMonth\"\n [attr.aria-label]=\"formatDayAriaLabel(day.date)\"\n [attr.aria-selected]=\"day.isSelected || day.isRangeStart || day.isRangeEnd || null\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n [attr.aria-disabled]=\"day.isDisabled ? 'true' : null\"\n [attr.aria-hidden]=\"!showOtherMonths() && !day.isCurrentMonth ? 'true' : null\"\n [attr.data-focus]=\"day.isSelected || day.isRangeStart || day.isRangeEnd ? 'selected' : day.isToday ? 'today' : null\"\n [disabled]=\"day.isDisabled\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0':\n day.isSelected || day.isRangeStart || day.isRangeEnd,\n 'bg-grayscale-10 hover:bg-grayscale-20':\n day.isInRange && !day.isRangeStart && !day.isRangeEnd,\n 'text-grayscale-90 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n day.isCurrentMonth,\n 'text-grayscale-60 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n !day.isCurrentMonth,\n 'ring-2 ring-inset ring-primary':\n day.isToday &&\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd,\n 'cursor-not-allowed text-grayscale-30': day.isDisabled\n }\"\n (mouseenter)=\"onDayHover(day.date)\"\n (mouseleave)=\"onDayHover(null)\"\n (click)=\"selectDate(day)\"\n >\n {{ day.dayOfMonth }}\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (pickerView() === 'years') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o por d\u00E9cada -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_decade' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i class=\"fas fa-chevron-left text-xs\" aria-hidden=\"true\"></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\">{{ currentDecadeStart() }} \u2013 {{ currentDecadeStart() + 11 }}</span>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_decade' | translate\"\n (click)=\"navigateNext()\"\n >\n <i class=\"fas fa-chevron-right text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <!-- Grade de anos -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_year' | translate\"\n >\n @for (row of yearsGrid(); track $index) {\n @for (year of row; track year) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isYearSelected(year) || null\"\n [attr.aria-disabled]=\"isYearDisabled(year) ? 'true' : null\"\n [attr.aria-current]=\"isCurrentYear(year) ? 'date' : null\"\n [attr.data-focus]=\"isYearSelected(year) ? 'selected' : isCurrentYear(year) ? 'today' : null\"\n [disabled]=\"isYearDisabled(year)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isYearSelected(year),\n 'ring-2 ring-inset ring-primary': isCurrentYear(year) && !isYearSelected(year) && !isYearDisabled(year),\n 'text-grayscale-90 hover:bg-grayscale-10': !isYearSelected(year) && !isYearDisabled(year),\n 'cursor-not-allowed text-grayscale-30': isYearDisabled(year)\n }\"\n (click)=\"selectYear(year)\"\n >\n {{ year }}\n </button>\n }\n }\n </div>\n }\n\n @if (pickerView() === 'months') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o anual -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_year' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i class=\"fas fa-chevron-left text-xs\" aria-hidden=\"true\"></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\">{{ currentYear() }}</span>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_year' | translate\"\n (click)=\"navigateNext()\"\n >\n <i class=\"fas fa-chevron-right text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <!-- Grade de meses -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month' | translate\"\n >\n @for (row of monthsGrid(); track $index) {\n @for (month of row; track month.index) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isMonthSelected(month.index) || null\"\n [attr.aria-disabled]=\"isMonthDisabled(month.index) ? 'true' : null\"\n [attr.data-focus]=\"isMonthSelected(month.index) ? 'selected' : null\"\n [disabled]=\"isMonthDisabled(month.index)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isMonthSelected(month.index),\n 'text-grayscale-90 hover:bg-grayscale-10':\n !isMonthSelected(month.index) && !isMonthDisabled(month.index),\n 'cursor-not-allowed text-grayscale-30': isMonthDisabled(month.index)\n }\"\n (click)=\"selectMonth(month.index)\"\n >\n {{ month.key | translate }}\n </button>\n }\n }\n </div>\n\n @if (mode() !== 'month') {\n <div class=\"border-t border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex items-center gap-2 rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"backToCalendar()\"\n >\n <i class=\"fas fa-arrow-left\" aria-hidden=\"true\"></i>\n {{ 'platform.angular_components.datepicker_back_to_calendar' | translate }}\n </button>\n </div>\n }\n }\n }\n\n <!-- Time picker -->\n @if (showTimePicker()) {\n <div\n class=\"flex items-center justify-center gap-3 px-4 py-3\"\n [class.border-t]=\"showCalendar()\"\n [class.border-solid]=\"showCalendar()\"\n [class.border-grayscale-30]=\"showCalendar()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_time' | translate\"\n >\n <!-- Horas -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n #increaseHourButton\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_hour' | translate\"\n (click)=\"incrementHours()\"\n >\n <i class=\"fas fa-chevron-up text-xs\" aria-hidden=\"true\"></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(displayHours())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_hour' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleHoursKeydown($event)\"\n (change)=\"handleHoursInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_hour' | translate\"\n (click)=\"decrementHours()\"\n >\n <i class=\"fas fa-chevron-down text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <span class=\"text-xl font-bold text-grayscale-60\" aria-hidden=\"true\">:</span>\n\n <!-- Minutos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_minute' | translate\"\n (click)=\"incrementMinutes()\"\n >\n <i class=\"fas fa-chevron-up text-xs\" aria-hidden=\"true\"></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(minutes())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_minute' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleMinutesKeydown($event)\"\n (change)=\"handleMinutesInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_minute' | translate\"\n (click)=\"decrementMinutes()\"\n >\n <i class=\"fas fa-chevron-down text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @if (showSeconds()) {\n <span class=\"text-xl font-bold text-grayscale-60\" aria-hidden=\"true\">:</span>\n\n <!-- Segundos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_second' | translate\"\n (click)=\"incrementSeconds()\"\n >\n <i class=\"fas fa-chevron-up text-xs\" aria-hidden=\"true\"></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(seconds())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_second' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleSecondsKeydown($event)\"\n (change)=\"handleSecondsInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_second' | translate\"\n (click)=\"decrementSeconds()\"\n >\n <i class=\"fas fa-chevron-down text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n @if (is12HourFormat()) {\n <!-- Seletor AM/PM -->\n <button\n type=\"button\"\n class=\"flex h-9 w-12 items-center justify-center rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-base font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_toggle_meridiem' | translate\"\n aria-live=\"polite\"\n (click)=\"toggleAmPm()\"\n >\n {{ amPm() }}\n </button>\n }\n </div>\n }\n\n <!-- Rodap\u00E9 de a\u00E7\u00F5es -->\n @if (showButtonBar() || mode() === 'datetime' || mode() === 'time') {\n <div class=\"flex items-center justify-between border-t border-solid border-grayscale-30 px-3 py-2\">\n @if (showButtonBar()) {\n <div>\n @if (mode() === 'date' || mode() === 'datetime' || mode() === 'range') {\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"selectToday()\"\n >\n {{ 'platform.angular_components.datepicker_today' | translate }}\n </button>\n }\n </div>\n\n <div class=\"flex gap-2\">\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"clearValue($event)\"\n >\n {{ 'platform.angular_components.datepicker_clear' | translate }}\n </button>\n\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n } @else {\n <div></div>\n <div>\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;position:relative;width:100%}:host.ng-invalid.ng-dirty input{border-color:#c13018}:host.ng-invalid.ng-dirty input:focus{border-color:#c13018;outline-color:#c13018}:host.ng-invalid.ng-dirty button[type=button]{border-color:#c13018}:host.no-icon button[type=button]{display:none}:host.no-icon input{padding-right:.75rem}input.hide-native-icon::-webkit-calendar-picker-indicator{display:none}button[type=button]:focus-visible{outline:2px solid #428BCA;outline-offset:1px;border-radius:inherit}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
1966
+ ], viewQueries: [{ propertyName: "popupPanel", first: true, predicate: ["popupPanel"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "increaseHourButton", first: true, predicate: ["increaseHourButton"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "iconButton", first: true, predicate: ["iconButton"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "triggerInput", first: true, predicate: ["triggerInput"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n class=\"relative flex w-full items-center\"\n role=\"combobox\"\n aria-haspopup=\"grid\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"popupId()\"\n>\n <input\n #triggerInput\n class=\"h-[35px] w-full rounded-small border border-solid border-grayscale-30 pl-3 text-sm outline-none transition-colors focus:border-primary\"\n [class.pr-10]=\"showIcon() && !showClear()\"\n [class.pr-16]=\"showIcon() && showClear()\"\n [class.pr-6]=\"!showIcon() && showClear()\"\n [class.bg-grayscale-0]=\"!disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.bg-grayscale-5]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [id]=\"inputId()\"\n [name]=\"name() ?? inputId()\"\n type=\"text\"\n [placeholder]=\"placeholder() ?? (placeholderKey() | translate)\"\n [disabled]=\"disabled()\"\n [readonly]=\"\n readonlyInput() ||\n mode() === 'range' ||\n mode() === 'year' ||\n isMultipleMode() ||\n (isSegmentModeCandidate() && isTouchDevice())\n \"\n [tabIndex]=\"tabindex() ?? null\"\n [autofocus]=\"autofocus()\"\n [(ngModel)]=\"inputDate\"\n (ngModelChange)=\"handleInputChange($event)\"\n (focus)=\"handleInputFocus($event)\"\n (blur)=\"handleInputBlur($event)\"\n (mousedown)=\"handleInputClick($event)\"\n (keydown)=\"handleTriggerKeydown($event)\"\n (input)=\"handleNativeInput($event)\"\n [attr.aria-invalid]=\"ngControl?.control?.invalid || null\"\n />\n\n @if (showClear() && inputDate) {\n <button\n type=\"button\"\n class=\"absolute flex h-[35px] w-6 items-center justify-center text-grayscale-60 transition-colors hover:text-grayscale-90\"\n [class.right-9]=\"showIcon()\"\n [class.right-0]=\"!showIcon()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_clear' | translate\"\n (click)=\"clearValue($event)\"\n >\n <i\n class=\"fas fa-times text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n\n @if (showIcon()) {\n <button\n #iconButton\n type=\"button\"\n class=\"absolute right-0 flex h-[35px] w-9 items-center justify-center border-l border-solid transition-colors\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [class.border-grayscale-30]=\"!disabled() && !isFocused()\"\n [class.border-primary]=\"isFocused() && !disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.hover:bg-grayscale-10]=\"!disabled()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_open' | translate\"\n (click)=\"toggle()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n >\n <i\n [class]=\"resolvedIcon()\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (backdropClick)=\"handleBackdropClick($event)\"\n (overlayKeydown)=\"handleOverlayKeydown($event)\"\n (detach)=\"isOpen.set(false)\"\n>\n <div\n #popupPanel\n class=\"z-50 w-72 rounded-medium border border-solid border-grayscale-30 bg-grayscale-0 shadow-md\"\n role=\"dialog\"\n cdkTrapFocus\n [id]=\"popupId()\"\n [attr.aria-label]=\"dialogAriaLabelKey() | translate\"\n >\n @if (showCalendar()) {\n @if (pickerView() === 'calendar') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o mensal -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_month' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i\n class=\"fas fa-chevron-left text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex items-center gap-1 rounded-small px-2 py-1 text-sm font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month_year' | translate\"\n (click)=\"switchToMonths()\"\n >\n {{ currentMonthKey() | translate }}&nbsp;{{ currentYear() }}\n <i\n class=\"fas fa-chevron-down text-[10px]\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_month' | translate\"\n (click)=\"navigateNext()\"\n >\n <i\n class=\"fas fa-chevron-right text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <!-- Cabe\u00E7alhos dos dias da semana -->\n <div\n class=\"grid grid-cols-7 px-3 pt-2\"\n role=\"row\"\n >\n @for (dayKey of orderedWeekDayKeys(); track dayKey) {\n <div\n class=\"flex h-8 items-center justify-center text-xs font-medium text-grayscale-60\"\n role=\"columnheader\"\n [attr.aria-label]=\"dayKey | translate\"\n >\n {{ dayKey | translate }}\n </div>\n }\n </div>\n\n <!-- Grade do calend\u00E1rio -->\n <div\n class=\"grid grid-cols-7 px-3 pb-2\"\n role=\"grid\"\n [attr.aria-label]=\"\n 'platform.angular_components.datepicker_calendar_label'\n | translate: { month: (currentMonthKey() | translate), year: currentYear() }\n \"\n (keydown)=\"handleCalendarGridKeydown($event)\"\n >\n @for (week of calendarWeeks(); track $index) {\n <div\n role=\"row\"\n class=\"contents\"\n >\n @for (day of week; track day.date.toISOString()) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-8 w-8 items-center justify-center rounded-[50%] text-sm font-normal transition-colors\"\n [class.invisible]=\"!showOtherMonths() && !day.isCurrentMonth\"\n [attr.aria-label]=\"formatDayAriaLabel(day.date)\"\n [attr.aria-selected]=\"day.isSelected || day.isRangeStart || day.isRangeEnd || null\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n [attr.aria-disabled]=\"day.isDisabled ? 'true' : null\"\n [attr.aria-hidden]=\"!showOtherMonths() && !day.isCurrentMonth ? 'true' : null\"\n [attr.data-focus]=\"\n day.isSelected || day.isRangeStart || day.isRangeEnd\n ? 'selected'\n : day.isToday\n ? 'today'\n : null\n \"\n [disabled]=\"day.isDisabled\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0':\n day.isSelected || day.isRangeStart || day.isRangeEnd,\n 'bg-grayscale-10 hover:bg-grayscale-20':\n day.isInRange && !day.isRangeStart && !day.isRangeEnd,\n 'text-grayscale-90 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n day.isCurrentMonth,\n 'text-grayscale-60 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n !day.isCurrentMonth,\n 'ring-2 ring-inset ring-primary':\n day.isToday && !day.isSelected && !day.isRangeStart && !day.isRangeEnd,\n 'cursor-not-allowed text-grayscale-30': day.isDisabled,\n }\"\n (mouseenter)=\"onDayHover(day.date)\"\n (mouseleave)=\"onDayHover(null)\"\n (click)=\"selectDate(day)\"\n >\n {{ day.dayOfMonth }}\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (pickerView() === 'years') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o por d\u00E9cada -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_decade' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i\n class=\"fas fa-chevron-left text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\"\n >{{ currentDecadeStart() }} \u2013 {{ currentDecadeStart() + 11 }}</span\n >\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_decade' | translate\"\n (click)=\"navigateNext()\"\n >\n <i\n class=\"fas fa-chevron-right text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <!-- Grade de anos -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_year' | translate\"\n >\n @for (row of yearsGrid(); track $index) {\n @for (year of row; track year) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isYearSelected(year) || null\"\n [attr.aria-disabled]=\"isYearDisabled(year) ? 'true' : null\"\n [attr.aria-current]=\"isCurrentYear(year) ? 'date' : null\"\n [attr.data-focus]=\"\n isYearSelected(year) ? 'selected' : isCurrentYear(year) ? 'today' : null\n \"\n [disabled]=\"isYearDisabled(year)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isYearSelected(year),\n 'ring-2 ring-inset ring-primary':\n isCurrentYear(year) && !isYearSelected(year) && !isYearDisabled(year),\n 'text-grayscale-90 hover:bg-grayscale-10':\n !isYearSelected(year) && !isYearDisabled(year),\n 'cursor-not-allowed text-grayscale-30': isYearDisabled(year),\n }\"\n (click)=\"selectYear(year)\"\n >\n {{ year }}\n </button>\n }\n }\n </div>\n }\n\n @if (pickerView() === 'months') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o anual -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_year' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i\n class=\"fas fa-chevron-left text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\">{{ currentYear() }}</span>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_year' | translate\"\n (click)=\"navigateNext()\"\n >\n <i\n class=\"fas fa-chevron-right text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <!-- Grade de meses -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month' | translate\"\n >\n @for (row of monthsGrid(); track $index) {\n @for (month of row; track month.index) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isMonthSelected(month.index) || null\"\n [attr.aria-disabled]=\"isMonthDisabled(month.index) ? 'true' : null\"\n [attr.data-focus]=\"isMonthSelected(month.index) ? 'selected' : null\"\n [disabled]=\"isMonthDisabled(month.index)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isMonthSelected(month.index),\n 'text-grayscale-90 hover:bg-grayscale-10':\n !isMonthSelected(month.index) && !isMonthDisabled(month.index),\n 'cursor-not-allowed text-grayscale-30': isMonthDisabled(month.index),\n }\"\n (click)=\"selectMonth(month.index)\"\n >\n {{ month.key | translate }}\n </button>\n }\n }\n </div>\n\n @if (mode() !== 'month') {\n <div class=\"border-t border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex items-center gap-2 rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"backToCalendar()\"\n >\n <i\n class=\"fas fa-arrow-left\"\n aria-hidden=\"true\"\n ></i>\n {{ 'platform.angular_components.datepicker_back_to_calendar' | translate }}\n </button>\n </div>\n }\n }\n }\n\n <!-- Time picker -->\n @if (showTimePicker()) {\n <div\n class=\"flex items-center justify-center gap-3 px-4 py-3\"\n [class.border-t]=\"showCalendar()\"\n [class.border-solid]=\"showCalendar()\"\n [class.border-grayscale-30]=\"showCalendar()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_time' | translate\"\n >\n <!-- Horas -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n #increaseHourButton\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_hour' | translate\"\n (click)=\"incrementHours()\"\n >\n <i\n class=\"fas fa-chevron-up text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(displayHours())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_hour' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleHoursKeydown($event)\"\n (change)=\"handleHoursInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_hour' | translate\"\n (click)=\"decrementHours()\"\n >\n <i\n class=\"fas fa-chevron-down text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <span\n class=\"text-xl font-bold text-grayscale-60\"\n aria-hidden=\"true\"\n >:</span\n >\n\n <!-- Minutos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_minute' | translate\"\n (click)=\"incrementMinutes()\"\n >\n <i\n class=\"fas fa-chevron-up text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(minutes())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_minute' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleMinutesKeydown($event)\"\n (change)=\"handleMinutesInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_minute' | translate\"\n (click)=\"decrementMinutes()\"\n >\n <i\n class=\"fas fa-chevron-down text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n @if (showSeconds()) {\n <span\n class=\"text-xl font-bold text-grayscale-60\"\n aria-hidden=\"true\"\n >:</span\n >\n\n <!-- Segundos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_second' | translate\"\n (click)=\"incrementSeconds()\"\n >\n <i\n class=\"fas fa-chevron-up text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(seconds())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_second' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleSecondsKeydown($event)\"\n (change)=\"handleSecondsInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_second' | translate\"\n (click)=\"decrementSeconds()\"\n >\n <i\n class=\"fas fa-chevron-down text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n }\n\n @if (is12HourFormat()) {\n <!-- Seletor AM/PM -->\n <button\n type=\"button\"\n class=\"flex h-9 w-12 items-center justify-center rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-base font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_toggle_meridiem' | translate\"\n aria-live=\"polite\"\n (click)=\"toggleAmPm()\"\n >\n {{ amPm() }}\n </button>\n }\n </div>\n }\n\n <!-- Rodap\u00E9 de a\u00E7\u00F5es -->\n @if (showButtonBar() || mode() === 'datetime' || mode() === 'time') {\n <div class=\"flex items-center justify-between border-t border-solid border-grayscale-30 px-3 py-2\">\n @if (showButtonBar()) {\n <div>\n @if (mode() === 'date' || mode() === 'datetime' || mode() === 'range') {\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"selectToday()\"\n >\n {{ 'platform.angular_components.datepicker_today' | translate }}\n </button>\n }\n </div>\n\n <div class=\"flex gap-2\">\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"clearValue($event)\"\n >\n {{ 'platform.angular_components.datepicker_clear' | translate }}\n </button>\n\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n } @else {\n <div></div>\n <div>\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n", styles: [":host{display:block;position:relative;width:100%}:host.ng-invalid.ng-dirty input{border-color:#c13018}:host.ng-invalid.ng-dirty input:focus{border-color:#c13018;outline-color:#c13018}:host.ng-invalid.ng-dirty button[type=button]{border-color:#c13018}:host.no-icon button[type=button]{display:none}:host.no-icon input{padding-right:.75rem}button[type=button]:focus-visible{outline:2px solid #428bca;outline-offset:1px;border-radius:inherit}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
1212
1967
  }
1213
1968
  __decorate([
1214
1969
  numericKeysOnly()
@@ -1242,8 +1997,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1242
1997
  useExisting: DatepickerComponent,
1243
1998
  multi: true,
1244
1999
  },
1245
- ], template: "<div\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n class=\"relative flex w-full items-center\"\n role=\"combobox\"\n aria-haspopup=\"grid\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"popupId()\"\n>\n <input\n class=\"hide-native-icon h-[35px] w-full rounded-small border border-solid border-grayscale-30 pl-3 text-sm outline-none transition-colors focus:border-primary\"\n [class.pr-10]=\"showIcon() && !showClear()\"\n [class.pr-16]=\"showIcon() && showClear()\"\n [class.pr-6]=\"!showIcon() && showClear()\"\n [class.bg-grayscale-0]=\"!disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.bg-grayscale-5]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [id]=\"inputId()\"\n [name]=\"name() ?? inputId()\"\n [type]=\"inputType()\"\n [placeholder]=\"placeholder() ?? (placeholderKey() | translate)\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonlyInput() || mode() === 'range' || mode() === 'year' || isMultipleMode()\"\n [tabIndex]=\"tabindex() ?? null\"\n [autofocus]=\"autofocus()\"\n [(ngModel)]=\"inputDate\"\n (ngModelChange)=\"handleInputChange($event)\"\n (focus)=\"handleInputFocus($event)\"\n (blur)=\"handleInputBlur($event)\"\n (click)=\"handleInputClick()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n (input)=\"handleNativeInput($event)\"\n [attr.aria-invalid]=\"ngControl?.control?.invalid || null\"\n />\n\n @if (showClear() && inputDate) {\n <button\n type=\"button\"\n class=\"absolute flex h-[35px] w-6 items-center justify-center text-grayscale-60 transition-colors hover:text-grayscale-90\"\n [class.right-9]=\"showIcon()\"\n [class.right-0]=\"!showIcon()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_clear' | translate\"\n (click)=\"clearValue($event)\"\n >\n <i class=\"fas fa-times text-xs\" aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (showIcon()) {\n <button\n #iconButton\n type=\"button\"\n class=\"absolute right-0 flex h-[35px] w-9 items-center justify-center border-l border-solid transition-colors\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [class.border-grayscale-30]=\"!disabled() && !isFocused()\"\n [class.border-primary]=\"isFocused() && !disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.hover:bg-grayscale-10]=\"!disabled()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_open' | translate\"\n (click)=\"toggle()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n >\n <i [class]=\"resolvedIcon()\" aria-hidden=\"true\"></i>\n </button>\n }\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (backdropClick)=\"closePanel()\"\n (overlayKeydown)=\"handleOverlayKeydown($event)\"\n (detach)=\"isOpen.set(false)\"\n>\n <div\n #popupPanel\n class=\"z-50 w-72 rounded-medium border border-solid border-grayscale-30 bg-grayscale-0 shadow-md\"\n role=\"dialog\"\n cdkTrapFocus\n [id]=\"popupId()\"\n [attr.aria-label]=\"dialogAriaLabelKey() | translate\"\n >\n @if (showCalendar()) {\n @if (pickerView() === 'calendar') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o mensal -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_month' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i class=\"fas fa-chevron-left text-xs\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex items-center gap-1 rounded-small px-2 py-1 text-sm font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month_year' | translate\"\n (click)=\"switchToMonths()\"\n >\n {{ currentMonthKey() | translate }}&nbsp;{{ currentYear() }}\n <i class=\"fas fa-chevron-down text-[10px]\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_month' | translate\"\n (click)=\"navigateNext()\"\n >\n <i class=\"fas fa-chevron-right text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <!-- Cabe\u00E7alhos dos dias da semana -->\n <div class=\"grid grid-cols-7 px-3 pt-2\" role=\"row\">\n @for (dayKey of orderedWeekDayKeys(); track dayKey) {\n <div\n class=\"flex h-8 items-center justify-center text-xs font-medium text-grayscale-60\"\n role=\"columnheader\"\n [attr.aria-label]=\"dayKey | translate\"\n >\n {{ dayKey | translate }}\n </div>\n }\n </div>\n\n <!-- Grade do calend\u00E1rio -->\n <div\n class=\"grid grid-cols-7 px-3 pb-2\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_calendar_label' | translate: { month: (currentMonthKey() | translate), year: currentYear() }\"\n (keydown)=\"handleCalendarGridKeydown($event)\"\n >\n @for (week of calendarWeeks(); track $index) {\n <div role=\"row\" class=\"contents\">\n @for (day of week; track day.date.toISOString()) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-8 w-8 items-center justify-center rounded-[50%] text-sm font-normal transition-colors\"\n [class.invisible]=\"!showOtherMonths() && !day.isCurrentMonth\"\n [attr.aria-label]=\"formatDayAriaLabel(day.date)\"\n [attr.aria-selected]=\"day.isSelected || day.isRangeStart || day.isRangeEnd || null\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n [attr.aria-disabled]=\"day.isDisabled ? 'true' : null\"\n [attr.aria-hidden]=\"!showOtherMonths() && !day.isCurrentMonth ? 'true' : null\"\n [attr.data-focus]=\"day.isSelected || day.isRangeStart || day.isRangeEnd ? 'selected' : day.isToday ? 'today' : null\"\n [disabled]=\"day.isDisabled\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0':\n day.isSelected || day.isRangeStart || day.isRangeEnd,\n 'bg-grayscale-10 hover:bg-grayscale-20':\n day.isInRange && !day.isRangeStart && !day.isRangeEnd,\n 'text-grayscale-90 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n day.isCurrentMonth,\n 'text-grayscale-60 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n !day.isCurrentMonth,\n 'ring-2 ring-inset ring-primary':\n day.isToday &&\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd,\n 'cursor-not-allowed text-grayscale-30': day.isDisabled\n }\"\n (mouseenter)=\"onDayHover(day.date)\"\n (mouseleave)=\"onDayHover(null)\"\n (click)=\"selectDate(day)\"\n >\n {{ day.dayOfMonth }}\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (pickerView() === 'years') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o por d\u00E9cada -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_decade' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i class=\"fas fa-chevron-left text-xs\" aria-hidden=\"true\"></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\">{{ currentDecadeStart() }} \u2013 {{ currentDecadeStart() + 11 }}</span>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_decade' | translate\"\n (click)=\"navigateNext()\"\n >\n <i class=\"fas fa-chevron-right text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <!-- Grade de anos -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_year' | translate\"\n >\n @for (row of yearsGrid(); track $index) {\n @for (year of row; track year) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isYearSelected(year) || null\"\n [attr.aria-disabled]=\"isYearDisabled(year) ? 'true' : null\"\n [attr.aria-current]=\"isCurrentYear(year) ? 'date' : null\"\n [attr.data-focus]=\"isYearSelected(year) ? 'selected' : isCurrentYear(year) ? 'today' : null\"\n [disabled]=\"isYearDisabled(year)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isYearSelected(year),\n 'ring-2 ring-inset ring-primary': isCurrentYear(year) && !isYearSelected(year) && !isYearDisabled(year),\n 'text-grayscale-90 hover:bg-grayscale-10': !isYearSelected(year) && !isYearDisabled(year),\n 'cursor-not-allowed text-grayscale-30': isYearDisabled(year)\n }\"\n (click)=\"selectYear(year)\"\n >\n {{ year }}\n </button>\n }\n }\n </div>\n }\n\n @if (pickerView() === 'months') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o anual -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_year' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i class=\"fas fa-chevron-left text-xs\" aria-hidden=\"true\"></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\">{{ currentYear() }}</span>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_year' | translate\"\n (click)=\"navigateNext()\"\n >\n <i class=\"fas fa-chevron-right text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <!-- Grade de meses -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month' | translate\"\n >\n @for (row of monthsGrid(); track $index) {\n @for (month of row; track month.index) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isMonthSelected(month.index) || null\"\n [attr.aria-disabled]=\"isMonthDisabled(month.index) ? 'true' : null\"\n [attr.data-focus]=\"isMonthSelected(month.index) ? 'selected' : null\"\n [disabled]=\"isMonthDisabled(month.index)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isMonthSelected(month.index),\n 'text-grayscale-90 hover:bg-grayscale-10':\n !isMonthSelected(month.index) && !isMonthDisabled(month.index),\n 'cursor-not-allowed text-grayscale-30': isMonthDisabled(month.index)\n }\"\n (click)=\"selectMonth(month.index)\"\n >\n {{ month.key | translate }}\n </button>\n }\n }\n </div>\n\n @if (mode() !== 'month') {\n <div class=\"border-t border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex items-center gap-2 rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"backToCalendar()\"\n >\n <i class=\"fas fa-arrow-left\" aria-hidden=\"true\"></i>\n {{ 'platform.angular_components.datepicker_back_to_calendar' | translate }}\n </button>\n </div>\n }\n }\n }\n\n <!-- Time picker -->\n @if (showTimePicker()) {\n <div\n class=\"flex items-center justify-center gap-3 px-4 py-3\"\n [class.border-t]=\"showCalendar()\"\n [class.border-solid]=\"showCalendar()\"\n [class.border-grayscale-30]=\"showCalendar()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_time' | translate\"\n >\n <!-- Horas -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n #increaseHourButton\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_hour' | translate\"\n (click)=\"incrementHours()\"\n >\n <i class=\"fas fa-chevron-up text-xs\" aria-hidden=\"true\"></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(displayHours())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_hour' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleHoursKeydown($event)\"\n (change)=\"handleHoursInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_hour' | translate\"\n (click)=\"decrementHours()\"\n >\n <i class=\"fas fa-chevron-down text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <span class=\"text-xl font-bold text-grayscale-60\" aria-hidden=\"true\">:</span>\n\n <!-- Minutos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_minute' | translate\"\n (click)=\"incrementMinutes()\"\n >\n <i class=\"fas fa-chevron-up text-xs\" aria-hidden=\"true\"></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(minutes())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_minute' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleMinutesKeydown($event)\"\n (change)=\"handleMinutesInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_minute' | translate\"\n (click)=\"decrementMinutes()\"\n >\n <i class=\"fas fa-chevron-down text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @if (showSeconds()) {\n <span class=\"text-xl font-bold text-grayscale-60\" aria-hidden=\"true\">:</span>\n\n <!-- Segundos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_second' | translate\"\n (click)=\"incrementSeconds()\"\n >\n <i class=\"fas fa-chevron-up text-xs\" aria-hidden=\"true\"></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(seconds())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_second' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleSecondsKeydown($event)\"\n (change)=\"handleSecondsInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_second' | translate\"\n (click)=\"decrementSeconds()\"\n >\n <i class=\"fas fa-chevron-down text-xs\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n @if (is12HourFormat()) {\n <!-- Seletor AM/PM -->\n <button\n type=\"button\"\n class=\"flex h-9 w-12 items-center justify-center rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-base font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_toggle_meridiem' | translate\"\n aria-live=\"polite\"\n (click)=\"toggleAmPm()\"\n >\n {{ amPm() }}\n </button>\n }\n </div>\n }\n\n <!-- Rodap\u00E9 de a\u00E7\u00F5es -->\n @if (showButtonBar() || mode() === 'datetime' || mode() === 'time') {\n <div class=\"flex items-center justify-between border-t border-solid border-grayscale-30 px-3 py-2\">\n @if (showButtonBar()) {\n <div>\n @if (mode() === 'date' || mode() === 'datetime' || mode() === 'range') {\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"selectToday()\"\n >\n {{ 'platform.angular_components.datepicker_today' | translate }}\n </button>\n }\n </div>\n\n <div class=\"flex gap-2\">\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"clearValue($event)\"\n >\n {{ 'platform.angular_components.datepicker_clear' | translate }}\n </button>\n\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n } @else {\n <div></div>\n <div>\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;position:relative;width:100%}:host.ng-invalid.ng-dirty input{border-color:#c13018}:host.ng-invalid.ng-dirty input:focus{border-color:#c13018;outline-color:#c13018}:host.ng-invalid.ng-dirty button[type=button]{border-color:#c13018}:host.no-icon button[type=button]{display:none}:host.no-icon input{padding-right:.75rem}input.hide-native-icon::-webkit-calendar-picker-indicator{display:none}button[type=button]:focus-visible{outline:2px solid #428BCA;outline-offset:1px;border-radius:inherit}\n"] }]
1246
- }], propDecorators: { handleHoursKeydown: [], handleMinutesKeydown: [], handleSecondsKeydown: [] } });
2000
+ ], template: "<div\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n class=\"relative flex w-full items-center\"\n role=\"combobox\"\n aria-haspopup=\"grid\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"popupId()\"\n>\n <input\n #triggerInput\n class=\"h-[35px] w-full rounded-small border border-solid border-grayscale-30 pl-3 text-sm outline-none transition-colors focus:border-primary\"\n [class.pr-10]=\"showIcon() && !showClear()\"\n [class.pr-16]=\"showIcon() && showClear()\"\n [class.pr-6]=\"!showIcon() && showClear()\"\n [class.bg-grayscale-0]=\"!disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.bg-grayscale-5]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [id]=\"inputId()\"\n [name]=\"name() ?? inputId()\"\n type=\"text\"\n [placeholder]=\"placeholder() ?? (placeholderKey() | translate)\"\n [disabled]=\"disabled()\"\n [readonly]=\"\n readonlyInput() ||\n mode() === 'range' ||\n mode() === 'year' ||\n isMultipleMode() ||\n (isSegmentModeCandidate() && isTouchDevice())\n \"\n [tabIndex]=\"tabindex() ?? null\"\n [autofocus]=\"autofocus()\"\n [(ngModel)]=\"inputDate\"\n (ngModelChange)=\"handleInputChange($event)\"\n (focus)=\"handleInputFocus($event)\"\n (blur)=\"handleInputBlur($event)\"\n (mousedown)=\"handleInputClick($event)\"\n (keydown)=\"handleTriggerKeydown($event)\"\n (input)=\"handleNativeInput($event)\"\n [attr.aria-invalid]=\"ngControl?.control?.invalid || null\"\n />\n\n @if (showClear() && inputDate) {\n <button\n type=\"button\"\n class=\"absolute flex h-[35px] w-6 items-center justify-center text-grayscale-60 transition-colors hover:text-grayscale-90\"\n [class.right-9]=\"showIcon()\"\n [class.right-0]=\"!showIcon()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_clear' | translate\"\n (click)=\"clearValue($event)\"\n >\n <i\n class=\"fas fa-times text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n\n @if (showIcon()) {\n <button\n #iconButton\n type=\"button\"\n class=\"absolute right-0 flex h-[35px] w-9 items-center justify-center border-l border-solid transition-colors\"\n [class.border-grayscale-20]=\"disabled()\"\n [class.text-grayscale-30]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n [class.border-grayscale-30]=\"!disabled() && !isFocused()\"\n [class.border-primary]=\"isFocused() && !disabled()\"\n [class.text-grayscale-90]=\"!disabled()\"\n [class.hover:bg-grayscale-10]=\"!disabled()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_open' | translate\"\n (click)=\"toggle()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n >\n <i\n [class]=\"resolvedIcon()\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (backdropClick)=\"handleBackdropClick($event)\"\n (overlayKeydown)=\"handleOverlayKeydown($event)\"\n (detach)=\"isOpen.set(false)\"\n>\n <div\n #popupPanel\n class=\"z-50 w-72 rounded-medium border border-solid border-grayscale-30 bg-grayscale-0 shadow-md\"\n role=\"dialog\"\n cdkTrapFocus\n [id]=\"popupId()\"\n [attr.aria-label]=\"dialogAriaLabelKey() | translate\"\n >\n @if (showCalendar()) {\n @if (pickerView() === 'calendar') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o mensal -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_month' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i\n class=\"fas fa-chevron-left text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex items-center gap-1 rounded-small px-2 py-1 text-sm font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month_year' | translate\"\n (click)=\"switchToMonths()\"\n >\n {{ currentMonthKey() | translate }}&nbsp;{{ currentYear() }}\n <i\n class=\"fas fa-chevron-down text-[10px]\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_month' | translate\"\n (click)=\"navigateNext()\"\n >\n <i\n class=\"fas fa-chevron-right text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <!-- Cabe\u00E7alhos dos dias da semana -->\n <div\n class=\"grid grid-cols-7 px-3 pt-2\"\n role=\"row\"\n >\n @for (dayKey of orderedWeekDayKeys(); track dayKey) {\n <div\n class=\"flex h-8 items-center justify-center text-xs font-medium text-grayscale-60\"\n role=\"columnheader\"\n [attr.aria-label]=\"dayKey | translate\"\n >\n {{ dayKey | translate }}\n </div>\n }\n </div>\n\n <!-- Grade do calend\u00E1rio -->\n <div\n class=\"grid grid-cols-7 px-3 pb-2\"\n role=\"grid\"\n [attr.aria-label]=\"\n 'platform.angular_components.datepicker_calendar_label'\n | translate: { month: (currentMonthKey() | translate), year: currentYear() }\n \"\n (keydown)=\"handleCalendarGridKeydown($event)\"\n >\n @for (week of calendarWeeks(); track $index) {\n <div\n role=\"row\"\n class=\"contents\"\n >\n @for (day of week; track day.date.toISOString()) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-8 w-8 items-center justify-center rounded-[50%] text-sm font-normal transition-colors\"\n [class.invisible]=\"!showOtherMonths() && !day.isCurrentMonth\"\n [attr.aria-label]=\"formatDayAriaLabel(day.date)\"\n [attr.aria-selected]=\"day.isSelected || day.isRangeStart || day.isRangeEnd || null\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n [attr.aria-disabled]=\"day.isDisabled ? 'true' : null\"\n [attr.aria-hidden]=\"!showOtherMonths() && !day.isCurrentMonth ? 'true' : null\"\n [attr.data-focus]=\"\n day.isSelected || day.isRangeStart || day.isRangeEnd\n ? 'selected'\n : day.isToday\n ? 'today'\n : null\n \"\n [disabled]=\"day.isDisabled\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0':\n day.isSelected || day.isRangeStart || day.isRangeEnd,\n 'bg-grayscale-10 hover:bg-grayscale-20':\n day.isInRange && !day.isRangeStart && !day.isRangeEnd,\n 'text-grayscale-90 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n day.isCurrentMonth,\n 'text-grayscale-60 hover:bg-grayscale-10':\n !day.isSelected &&\n !day.isRangeStart &&\n !day.isRangeEnd &&\n !day.isDisabled &&\n !day.isCurrentMonth,\n 'ring-2 ring-inset ring-primary':\n day.isToday && !day.isSelected && !day.isRangeStart && !day.isRangeEnd,\n 'cursor-not-allowed text-grayscale-30': day.isDisabled,\n }\"\n (mouseenter)=\"onDayHover(day.date)\"\n (mouseleave)=\"onDayHover(null)\"\n (click)=\"selectDate(day)\"\n >\n {{ day.dayOfMonth }}\n </button>\n }\n </div>\n }\n </div>\n }\n\n @if (pickerView() === 'years') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o por d\u00E9cada -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_decade' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i\n class=\"fas fa-chevron-left text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\"\n >{{ currentDecadeStart() }} \u2013 {{ currentDecadeStart() + 11 }}</span\n >\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_decade' | translate\"\n (click)=\"navigateNext()\"\n >\n <i\n class=\"fas fa-chevron-right text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <!-- Grade de anos -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_year' | translate\"\n >\n @for (row of yearsGrid(); track $index) {\n @for (year of row; track year) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isYearSelected(year) || null\"\n [attr.aria-disabled]=\"isYearDisabled(year) ? 'true' : null\"\n [attr.aria-current]=\"isCurrentYear(year) ? 'date' : null\"\n [attr.data-focus]=\"\n isYearSelected(year) ? 'selected' : isCurrentYear(year) ? 'today' : null\n \"\n [disabled]=\"isYearDisabled(year)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isYearSelected(year),\n 'ring-2 ring-inset ring-primary':\n isCurrentYear(year) && !isYearSelected(year) && !isYearDisabled(year),\n 'text-grayscale-90 hover:bg-grayscale-10':\n !isYearSelected(year) && !isYearDisabled(year),\n 'cursor-not-allowed text-grayscale-30': isYearDisabled(year),\n }\"\n (click)=\"selectYear(year)\"\n >\n {{ year }}\n </button>\n }\n }\n </div>\n }\n\n @if (pickerView() === 'months') {\n <!-- Cabe\u00E7alho de navega\u00E7\u00E3o anual -->\n <div class=\"flex items-center justify-between border-b border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"prevDisabled()\"\n [attr.aria-disabled]=\"prevDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_prev_year' | translate\"\n (click)=\"navigatePrev()\"\n >\n <i\n class=\"fas fa-chevron-left text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n\n <span class=\"text-sm font-medium text-grayscale-90\">{{ currentYear() }}</span>\n\n <button\n type=\"button\"\n class=\"flex h-7 w-7 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10 disabled:cursor-not-allowed disabled:text-grayscale-30\"\n [disabled]=\"nextDisabled()\"\n [attr.aria-disabled]=\"nextDisabled() ? 'true' : null\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_next_year' | translate\"\n (click)=\"navigateNext()\"\n >\n <i\n class=\"fas fa-chevron-right text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <!-- Grade de meses -->\n <div\n class=\"grid grid-cols-3 gap-2 px-3 py-3\"\n role=\"grid\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_month' | translate\"\n >\n @for (row of monthsGrid(); track $index) {\n @for (month of row; track month.index) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"flex h-9 items-center justify-center rounded-small text-sm font-normal transition-colors\"\n [attr.aria-selected]=\"isMonthSelected(month.index) || null\"\n [attr.aria-disabled]=\"isMonthDisabled(month.index) ? 'true' : null\"\n [attr.data-focus]=\"isMonthSelected(month.index) ? 'selected' : null\"\n [disabled]=\"isMonthDisabled(month.index)\"\n [ngClass]=\"{\n 'bg-primary font-semibold text-grayscale-0': isMonthSelected(month.index),\n 'text-grayscale-90 hover:bg-grayscale-10':\n !isMonthSelected(month.index) && !isMonthDisabled(month.index),\n 'cursor-not-allowed text-grayscale-30': isMonthDisabled(month.index),\n }\"\n (click)=\"selectMonth(month.index)\"\n >\n {{ month.key | translate }}\n </button>\n }\n }\n </div>\n\n @if (mode() !== 'month') {\n <div class=\"border-t border-solid border-grayscale-30 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex items-center gap-2 rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"backToCalendar()\"\n >\n <i\n class=\"fas fa-arrow-left\"\n aria-hidden=\"true\"\n ></i>\n {{ 'platform.angular_components.datepicker_back_to_calendar' | translate }}\n </button>\n </div>\n }\n }\n }\n\n <!-- Time picker -->\n @if (showTimePicker()) {\n <div\n class=\"flex items-center justify-center gap-3 px-4 py-3\"\n [class.border-t]=\"showCalendar()\"\n [class.border-solid]=\"showCalendar()\"\n [class.border-grayscale-30]=\"showCalendar()\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_select_time' | translate\"\n >\n <!-- Horas -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n #increaseHourButton\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_hour' | translate\"\n (click)=\"incrementHours()\"\n >\n <i\n class=\"fas fa-chevron-up text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(displayHours())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_hour' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleHoursKeydown($event)\"\n (change)=\"handleHoursInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_hour' | translate\"\n (click)=\"decrementHours()\"\n >\n <i\n class=\"fas fa-chevron-down text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n <span\n class=\"text-xl font-bold text-grayscale-60\"\n aria-hidden=\"true\"\n >:</span\n >\n\n <!-- Minutos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_minute' | translate\"\n (click)=\"incrementMinutes()\"\n >\n <i\n class=\"fas fa-chevron-up text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(minutes())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_minute' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleMinutesKeydown($event)\"\n (change)=\"handleMinutesInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_minute' | translate\"\n (click)=\"decrementMinutes()\"\n >\n <i\n class=\"fas fa-chevron-down text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n\n @if (showSeconds()) {\n <span\n class=\"text-xl font-bold text-grayscale-60\"\n aria-hidden=\"true\"\n >:</span\n >\n\n <!-- Segundos -->\n <div class=\"flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_increment_second' | translate\"\n (click)=\"incrementSeconds()\"\n >\n <i\n class=\"fas fa-chevron-up text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n [attr.maxlength]=\"timeInputMaxLength\"\n class=\"h-9 w-12 rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-center text-base font-medium text-grayscale-90 outline-none focus:border-primary focus:outline-1 focus:outline-primary\"\n [value]=\"padTime(seconds())\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_second' | translate\"\n aria-live=\"polite\"\n pattern=\"[0-9]+\"\n (keydown)=\"handleSecondsKeydown($event)\"\n (change)=\"handleSecondsInput($event)\"\n />\n <button\n type=\"button\"\n class=\"flex h-6 w-6 items-center justify-center rounded-small text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_decrement_second' | translate\"\n (click)=\"decrementSeconds()\"\n >\n <i\n class=\"fas fa-chevron-down text-xs\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n }\n\n @if (is12HourFormat()) {\n <!-- Seletor AM/PM -->\n <button\n type=\"button\"\n class=\"flex h-9 w-12 items-center justify-center rounded-small border border-solid border-grayscale-30 bg-grayscale-5 text-base font-medium text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n [attr.aria-label]=\"'platform.angular_components.datepicker_toggle_meridiem' | translate\"\n aria-live=\"polite\"\n (click)=\"toggleAmPm()\"\n >\n {{ amPm() }}\n </button>\n }\n </div>\n }\n\n <!-- Rodap\u00E9 de a\u00E7\u00F5es -->\n @if (showButtonBar() || mode() === 'datetime' || mode() === 'time') {\n <div class=\"flex items-center justify-between border-t border-solid border-grayscale-30 px-3 py-2\">\n @if (showButtonBar()) {\n <div>\n @if (mode() === 'date' || mode() === 'datetime' || mode() === 'range') {\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"selectToday()\"\n >\n {{ 'platform.angular_components.datepicker_today' | translate }}\n </button>\n }\n </div>\n\n <div class=\"flex gap-2\">\n <button\n type=\"button\"\n class=\"rounded-small px-2 py-1 text-sm text-grayscale-90 transition-colors hover:bg-grayscale-10\"\n (click)=\"clearValue($event)\"\n >\n {{ 'platform.angular_components.datepicker_clear' | translate }}\n </button>\n\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n } @else {\n <div></div>\n <div>\n @if (mode() === 'datetime' || mode() === 'time') {\n <button\n type=\"button\"\n class=\"rounded-small bg-primary px-3 py-1 text-sm font-medium text-grayscale-0 transition-colors hover:opacity-90\"\n (click)=\"confirm()\"\n >\n {{ 'platform.angular_components.datepicker_confirm' | translate }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n", styles: [":host{display:block;position:relative;width:100%}:host.ng-invalid.ng-dirty input{border-color:#c13018}:host.ng-invalid.ng-dirty input:focus{border-color:#c13018;outline-color:#c13018}:host.ng-invalid.ng-dirty button[type=button]{border-color:#c13018}:host.no-icon button[type=button]{display:none}:host.no-icon input{padding-right:.75rem}button[type=button]:focus-visible{outline:2px solid #428bca;outline-offset:1px;border-radius:inherit}\n"] }]
2001
+ }], ctorParameters: () => [], propDecorators: { handleHoursKeydown: [], handleMinutesKeydown: [], handleSecondsKeydown: [] } });
1247
2002
 
1248
2003
  /**
1249
2004
  * Generated bundle index. Do not edit.