@seniorsistemas/angular-components 19.5.5 → 19.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/accordion/README.md +95 -59
  2. package/calendar-mask/lib/calendar-mask/calendar-mask.directive.d.ts +4 -0
  3. package/datepicker/README.md +175 -0
  4. package/datepicker/index.d.ts +5 -0
  5. package/datepicker/lib/datepicker/date.utils.d.ts +21 -0
  6. package/datepicker/lib/datepicker/datepicker.component.d.ts +254 -0
  7. package/datepicker/lib/datepicker/models/index.d.ts +16 -0
  8. package/datepicker/public-api.d.ts +2 -0
  9. package/dynamic-form/dynamic-form/components/lookup/lookup.component.d.ts +3 -1
  10. package/dynamic-form/dynamic-form/components/lookup/models/lookup-alert-config.d.ts +5 -0
  11. package/dynamic-form/dynamic-form/form-field/configurations/fields/calendar-field.d.ts +119 -24
  12. package/dynamic-form/dynamic-form/form-field/configurations/fields/lookup-field.d.ts +6 -0
  13. package/dynamic-form/dynamic-form/form-field/fields/calendar/calendar-field.component.d.ts +6 -23
  14. package/dynamic-form/public-api.d.ts +1 -0
  15. package/editor/README.md +229 -0
  16. package/editor/index.d.ts +5 -0
  17. package/editor/lib/editor/editor.component.d.ts +127 -0
  18. package/editor/lib/editor/link-dialog/link-dialog.component.d.ts +18 -0
  19. package/editor/lib/editor/models/index.d.ts +12 -0
  20. package/editor/lib/editor/toolbar-button/editor-toolbar-button.component.d.ts +10 -0
  21. package/editor/public-api.d.ts +2 -0
  22. package/esm2022/calendar-mask/lib/calendar-mask/calendar-mask.directive.mjs +5 -1
  23. package/esm2022/datepicker/lib/datepicker/date.utils.mjs +182 -0
  24. package/esm2022/datepicker/lib/datepicker/datepicker.component.mjs +1062 -0
  25. package/esm2022/datepicker/lib/datepicker/models/index.mjs +2 -0
  26. package/esm2022/datepicker/public-api.mjs +2 -0
  27. package/esm2022/datepicker/seniorsistemas-angular-components-datepicker.mjs +5 -0
  28. package/esm2022/dynamic-form/dynamic-form/components/lookup/lookup.component.mjs +11 -5
  29. package/esm2022/dynamic-form/dynamic-form/components/lookup/models/lookup-alert-config.mjs +2 -0
  30. package/esm2022/dynamic-form/dynamic-form/form-field/configurations/fields/calendar-field.mjs +1 -1
  31. package/esm2022/dynamic-form/dynamic-form/form-field/configurations/fields/lookup-field.mjs +1 -1
  32. package/esm2022/dynamic-form/dynamic-form/form-field/fields/autocomplete/autocomplete-field.component.mjs +2 -2
  33. package/esm2022/dynamic-form/dynamic-form/form-field/fields/calendar/calendar-field.component.mjs +23 -116
  34. package/esm2022/dynamic-form/dynamic-form/form-field/fields/lookup/lookup-field.component.mjs +3 -3
  35. package/esm2022/dynamic-form/public-api.mjs +1 -1
  36. package/esm2022/editor/lib/editor/editor.component.mjs +375 -0
  37. package/esm2022/editor/lib/editor/link-dialog/link-dialog.component.mjs +48 -0
  38. package/esm2022/editor/lib/editor/models/index.mjs +3 -0
  39. package/esm2022/editor/lib/editor/toolbar-button/editor-toolbar-button.component.mjs +17 -0
  40. package/esm2022/editor/public-api.mjs +3 -0
  41. package/esm2022/editor/seniorsistemas-angular-components-editor.mjs +5 -0
  42. package/esm2022/inline-edit/lib/inline-edit/components/fields/inline-edit-calendar/inline-edit-calendar.component.mjs +22 -12
  43. package/esm2022/inline-edit/lib/inline-edit/inline-edit.module.mjs +5 -5
  44. package/esm2022/lib/locale/fallback.mjs +82 -3
  45. package/esm2022/optional-fields/editor/editor-field/editor-field.component.mjs +5 -6
  46. package/esm2022/optional-fields/editor/editor-field.mjs +1 -1
  47. package/esm2022/panel/lib/panel/panel.component.mjs +7 -3
  48. package/esm2022/select/lib/select/select.component.mjs +4 -8
  49. package/esm2022/shared/lib/accessibility/datepicker-no-period-tab.directive.mjs +5 -1
  50. package/fesm2022/seniorsistemas-angular-components-calendar-mask.mjs +4 -0
  51. package/fesm2022/seniorsistemas-angular-components-calendar-mask.mjs.map +1 -1
  52. package/fesm2022/seniorsistemas-angular-components-datepicker.mjs +1250 -0
  53. package/fesm2022/seniorsistemas-angular-components-datepicker.mjs.map +1 -0
  54. package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs +49 -135
  55. package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs.map +1 -1
  56. package/fesm2022/seniorsistemas-angular-components-editor.mjs +437 -0
  57. package/fesm2022/seniorsistemas-angular-components-editor.mjs.map +1 -0
  58. package/fesm2022/seniorsistemas-angular-components-inline-edit.mjs +24 -15
  59. package/fesm2022/seniorsistemas-angular-components-inline-edit.mjs.map +1 -1
  60. package/fesm2022/seniorsistemas-angular-components-optional-fields-editor.mjs +4 -5
  61. package/fesm2022/seniorsistemas-angular-components-optional-fields-editor.mjs.map +1 -1
  62. package/fesm2022/seniorsistemas-angular-components-panel.mjs +6 -2
  63. package/fesm2022/seniorsistemas-angular-components-panel.mjs.map +1 -1
  64. package/fesm2022/seniorsistemas-angular-components-select.mjs +3 -7
  65. package/fesm2022/seniorsistemas-angular-components-select.mjs.map +1 -1
  66. package/fesm2022/seniorsistemas-angular-components-shared.mjs +4 -0
  67. package/fesm2022/seniorsistemas-angular-components-shared.mjs.map +1 -1
  68. package/fesm2022/seniorsistemas-angular-components.mjs +81 -2
  69. package/fesm2022/seniorsistemas-angular-components.mjs.map +1 -1
  70. package/inline-edit/lib/inline-edit/components/fields/inline-edit-calendar/inline-edit-calendar.component.d.ts +6 -4
  71. package/inline-edit/lib/inline-edit/inline-edit.module.d.ts +2 -2
  72. package/loading-state/README.md +57 -17
  73. package/optional-fields/editor/editor-field.d.ts +9 -6
  74. package/package.json +74 -8
  75. package/panel/README.md +12 -1
  76. package/panel/lib/panel/panel.component.d.ts +3 -1
  77. package/schematics/collection.json +4 -0
  78. package/schematics/migrations/migrate-p-datepicker/index.d.ts +2 -0
  79. package/schematics/migrations/migrate-p-datepicker/index.js +141 -0
  80. package/schematics/migrations/migrate-p-datepicker/index.js.map +1 -0
  81. package/schematics/migrations/migrate-p-datepicker/index.test.d.ts +1 -0
  82. package/schematics/migrations/migrate-p-datepicker/index.test.js +392 -0
  83. package/schematics/migrations/migrate-p-datepicker/index.test.js.map +1 -0
  84. package/schematics/migrations.json +10 -0
  85. package/schematics/utils/ast-utils.d.ts +28 -0
  86. package/schematics/utils/ast-utils.js +116 -0
  87. package/schematics/utils/ast-utils.js.map +1 -0
  88. package/schematics/utils/ast-utils.test.d.ts +1 -0
  89. package/schematics/utils/ast-utils.test.js +138 -0
  90. package/schematics/utils/ast-utils.test.js.map +1 -0
  91. package/schematics/utils/workspace-utils.d.ts +6 -0
  92. package/schematics/utils/workspace-utils.js +37 -0
  93. package/schematics/utils/workspace-utils.js.map +1 -0
  94. package/schematics/utils/workspace-utils.test.d.ts +1 -0
  95. package/schematics/utils/workspace-utils.test.js +152 -0
  96. package/schematics/utils/workspace-utils.test.js.map +1 -0
  97. package/shared/lib/accessibility/datepicker-no-period-tab.directive.d.ts +4 -0
  98. package/stats-card/README.md +80 -45
@@ -0,0 +1,1250 @@
1
+ import { __decorate } from 'tslib';
2
+ import { CdkTrapFocus } from '@angular/cdk/a11y';
3
+ import { CdkOverlayOrigin, CdkConnectedOverlay } from '@angular/cdk/overlay';
4
+ import { NgClass } from '@angular/common';
5
+ import * as i0 from '@angular/core';
6
+ import { input, model, output, signal, viewChild, ElementRef, inject, Injector, computed, afterNextRender, Component } from '@angular/core';
7
+ import * as i2 from '@angular/forms';
8
+ import { NgControl, Validators, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormsModule, ReactiveFormsModule } from '@angular/forms';
9
+ import * as i1 from '@ngx-translate/core';
10
+ import { TranslateModule } from '@ngx-translate/core';
11
+
12
+ const DAY_MATCH_GROUP = 3;
13
+ const MINUTES_MATCH_GROUP = 5;
14
+ const MAX_MONTH_INDEX = 11;
15
+ function formatNativeDate(date) {
16
+ const year = date.getFullYear();
17
+ const month = String(date.getMonth() + 1).padStart(2, '0');
18
+ const day = String(date.getDate()).padStart(2, '0');
19
+ return `${year}-${month}-${day}`;
20
+ }
21
+ /**
22
+ * 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.
25
+ */
26
+ function formatWithPattern(date, pattern) {
27
+ const dd = String(date.getDate()).padStart(2, '0');
28
+ const MM = String(date.getMonth() + 1).padStart(2, '0');
29
+ const yyyy = String(date.getFullYear());
30
+ const yy = yyyy.slice(-2);
31
+ return pattern
32
+ .replace('yyyy', yyyy)
33
+ .replace('yy', yy) // must come after yyyy
34
+ .replace('dd', dd)
35
+ .replace('MM', MM)
36
+ .replace('mm', MM); // PrimeNG compat: lowercase mm = month
37
+ }
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
+ /** Parses yyyy-MM-dd. Returns null when invalid. */
43
+ function parseDate(raw) {
44
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
45
+ if (!match) {
46
+ return null;
47
+ }
48
+ const year = parseInt(match[1], 10);
49
+ const month = parseInt(match[2], 10) - 1;
50
+ const day = parseInt(match[DAY_MATCH_GROUP], 10);
51
+ if (year < 1000) {
52
+ return null;
53
+ }
54
+ const date = new Date(year, month, day);
55
+ if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
56
+ return null;
57
+ }
58
+ return date;
59
+ }
60
+ function isSameDay(a, b) {
61
+ return (a.getFullYear() === b.getFullYear() &&
62
+ a.getMonth() === b.getMonth() &&
63
+ a.getDate() === b.getDate());
64
+ }
65
+ function stripTime(date) {
66
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
67
+ }
68
+ function parseRangeInput(raw, separator = '—') {
69
+ const trimmedSeparator = separator.trim() || '—';
70
+ const parts = raw.split(trimmedSeparator).map((part) => part.trim());
71
+ if (parts.length !== 2) {
72
+ return null;
73
+ }
74
+ const start = parseDate(parts[0]);
75
+ const end = parseDate(parts[1]);
76
+ if (!start || !end) {
77
+ return null;
78
+ }
79
+ return { start, end };
80
+ }
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
+ function parseYearInput(raw) {
95
+ const match = /^(\d{4})$/.exec(raw.trim());
96
+ if (!match) {
97
+ return null;
98
+ }
99
+ const year = parseInt(match[1], 10);
100
+ if (year < 1000) {
101
+ return null;
102
+ }
103
+ return new Date(year, 0, 1);
104
+ }
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 = '—') {
140
+ if (!raw?.trim()) {
141
+ return null;
142
+ }
143
+ switch (mode) {
144
+ case 'range':
145
+ return parseRangeInput(raw, rangeSeparator);
146
+ case 'time':
147
+ return parseTimeInput(raw, base, maxHours, maxMinutes);
148
+ case 'month':
149
+ return parseMonthInput(raw);
150
+ case 'year':
151
+ return parseYearInput(raw);
152
+ case 'datetime':
153
+ return parseDateTimeInput(raw);
154
+ default:
155
+ return parseDate(raw);
156
+ }
157
+ }
158
+ function formatValueForInput(mode, value, rangeSeparator = ' — ', dateFormat, multipleSeparator = ', ') {
159
+ if (!value) {
160
+ return '';
161
+ }
162
+ const formatPart = (date) => dateFormat ? formatWithPattern(date, dateFormat) : formatNativeDate(date);
163
+ if (Array.isArray(value)) {
164
+ return value.map(formatPart).join(multipleSeparator);
165
+ }
166
+ if (mode === 'range') {
167
+ const range = value;
168
+ if (!range.start) {
169
+ return '';
170
+ }
171
+ return range.end
172
+ ? `${formatPart(range.start)}${rangeSeparator}${formatPart(range.end)}`
173
+ : formatPart(range.start);
174
+ }
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);
191
+ }
192
+ }
193
+
194
+ function numericKeysOnly() {
195
+ return (_target, _key, descriptor) => {
196
+ const original = descriptor.value;
197
+ descriptor.value = function (event) {
198
+ const isValidKey = ['ArrowUp', 'ArrowDown', 'Backspace', 'Delete', 'Tab'].includes(event.key) || /^\d$/.test(event.key);
199
+ if (!isValidKey) {
200
+ event.preventDefault();
201
+ }
202
+ else {
203
+ original.call(this, event);
204
+ }
205
+ };
206
+ return descriptor;
207
+ };
208
+ }
209
+ class DatepickerComponent {
210
+ static idCounter = 0;
211
+ static DAYS_IN_WEEK = 7;
212
+ static MONTHS_IN_YEAR = 12;
213
+ static MONTHS_PER_ROW = 3;
214
+ static YEARS_PER_GRID = 12;
215
+ static YEARS_PER_ROW = 3;
216
+ static DEFAULT_MINUTE_STEP = 5;
217
+ static MAX_HOURS = 24;
218
+ static MAX_MINUTES = 60;
219
+ static HOURS_PER_MERIDIEM = 12;
220
+ static nextId() {
221
+ DatepickerComponent.idCounter++;
222
+ return `s-datepicker-${DatepickerComponent.idCounter}`;
223
+ }
224
+ // ── Inputs ────────────────────────────────────────────────────────────
225
+ /** HTML `id` aplicado ao elemento `<input>`. Auto-gerado quando omitido. */
226
+ inputId = input(DatepickerComponent.nextId());
227
+ /** Atributo `name` do `<input>` nativo. */
228
+ name = input();
229
+ /** Texto exibido quando nenhum valor está selecionado. */
230
+ placeholder = input();
231
+ /**
232
+ * Modo de seleção do componente.
233
+ * - `'date'` — seleção de data única (padrão)
234
+ * - `'month'` — seleção de mês/ano
235
+ * - `'datetime'` — data com hora (requer confirmação)
236
+ * - `'time'` — apenas seleção de hora
237
+ * - `'range'` — intervalo entre duas datas
238
+ */
239
+ mode = input('date');
240
+ /**
241
+ * Modo de seleção quando `mode` é `'date'`.
242
+ * - `'single'` — seleção de uma única data (padrão)
243
+ * - `'multiple'` — seleção de múltiplas datas avulsas
244
+ */
245
+ selectionMode = input('single');
246
+ /** Separador exibido entre as datas no modo `selectionMode = 'multiple'`. */
247
+ multipleSeparator = input(', ');
248
+ /** Quando `true`, exibe o botão de ícone ao lado do campo. */
249
+ showIcon = input(true);
250
+ /**
251
+ * Classe do ícone exibido no botão do campo.
252
+ * Quando `mode` é `'time'`, o padrão muda automaticamente para `'fas fa-clock'`.
253
+ */
254
+ icon = input('fas fa-calendar');
255
+ /** Data mínima selecionável. Dias/meses anteriores ficam desabilitados. */
256
+ min = input(null);
257
+ /** Data máxima selecionável. Dias/meses posteriores ficam desabilitados. */
258
+ max = input(null);
259
+ /** Data exibida ao abrir o calendário quando nenhuma data está selecionada. */
260
+ 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');
265
+ /** Incremento em minutos ao usar as setas do time picker. */
266
+ minuteStep = input(DatepickerComponent.DEFAULT_MINUTE_STEP);
267
+ /** Quando `true`, exibe o campo de segundos no time picker. */
268
+ showSeconds = input(false);
269
+ /** Separador exibido entre as datas no modo `'range'`. */
270
+ rangeSeparator = input(' — ');
271
+ /** Abre o painel ao focar o input; foco permanece no input, não pula para o dropdown. */
272
+ showOnFocus = input(true);
273
+ /** Impede digitação manual; valor só pode ser selecionado pelo calendário. */
274
+ readonlyInput = input(false);
275
+ /** Primeiro dia da semana (0 = domingo, 1 = segunda-feira, ..., 6 = sábado). */
276
+ firstDayOfWeek = input(0);
277
+ /** Exibe/oculta a barra de botões Hoje e Limpar no rodapé. */
278
+ showButtonBar = input(true);
279
+ /** Exibe ícone de limpar inline no campo de input. */
280
+ showClear = input(false);
281
+ /** Exibe dias de meses adjacentes na grade do calendário. */
282
+ showOtherMonths = input(true);
283
+ /** Permite selecionar dias de meses adjacentes na grade do calendário. */
284
+ selectOtherMonths = input(false);
285
+ /** Fecha o painel após selecionar uma data no modo `datetime`. */
286
+ hideOnDateTimeSelect = input(false);
287
+ /** Incremento de horas no time picker. */
288
+ stepHour = input(1);
289
+ /** Incremento de segundos no time picker. */
290
+ stepSecond = input(1);
291
+ /** Índice de tab do campo de input. */
292
+ tabindex = input();
293
+ /** Foca automaticamente o campo ao carregar o componente. */
294
+ autofocus = input(false);
295
+ /** Datas específicas desabilitadas no calendário. */
296
+ disabledDates = input([]);
297
+ /** Dias da semana desabilitados (0 = domingo, ..., 6 = sábado). */
298
+ disabledDays = input([]);
299
+ // ── Two-way models ────────────────────────────────────────────────────
300
+ /** Estado desabilitado do componente. */
301
+ disabled = model(false);
302
+ // ── Outputs ───────────────────────────────────────────────────────────
303
+ /** Emitido quando o campo perde o foco. */
304
+ blurred = output();
305
+ /** Emitido quando o campo recebe o foco. */
306
+ focused = output();
307
+ /** Emitido ao selecionar uma data, mês, horário ou intervalo. */
308
+ selected = output();
309
+ /** Emitido quando o painel do calendário fecha. Emite o elemento do painel. */
310
+ closed = output();
311
+ /** Emitido ao clicar no botão "Hoje". */
312
+ todayClick = output();
313
+ /** Emitido ao clicar no botão "Limpar" ou no ícone de limpar inline. */
314
+ clearClick = output();
315
+ /** Emitido quando o mês visualizado muda. */
316
+ monthChange = output();
317
+ /** Emitido quando o ano visualizado muda. */
318
+ yearChange = output();
319
+ /** Emitido quando o painel do calendário abre. */
320
+ show = output();
321
+ /** Emitido quando o usuário digita manualmente no campo. */
322
+ valueInput = output();
323
+ // ── Internal state ────────────────────────────────────────────────────
324
+ maxHours = DatepickerComponent.MAX_HOURS;
325
+ maxMinutes = DatepickerComponent.MAX_MINUTES;
326
+ timeInputMaxLength = 2;
327
+ inputDate = '';
328
+ ngControl = null;
329
+ value = signal(null);
330
+ isOpen = signal(false);
331
+ viewDate = signal(new Date());
332
+ pickerView = signal('calendar');
333
+ hours = signal(0);
334
+ minutes = signal(0);
335
+ seconds = signal(0);
336
+ rangeValue = signal(null);
337
+ multipleValues = signal([]);
338
+ pendingRangeStart = signal(null);
339
+ hoverDate = signal(null);
340
+ _hasFormatError = false;
341
+ _isBadInput = false;
342
+ onValidatorChange = () => { };
343
+ _onChange = () => { };
344
+ _onTouched = () => { };
345
+ popupPanel = viewChild('popupPanel', { read: ElementRef });
346
+ increaseHourButton = viewChild('increaseHourButton', { read: ElementRef });
347
+ iconButton = viewChild('iconButton', { read: ElementRef });
348
+ injector = inject(Injector);
349
+ hostElement = inject(ElementRef);
350
+ // ── Computed ──────────────────────────────────────────────────────────
351
+ resolvedIcon = computed(() => (this.mode() === 'time' ? 'fas fa-clock' : this.icon()));
352
+ /** Indica se o time picker deve usar o formato de 12 horas com seletor AM/PM. */
353
+ is12HourFormat = computed(() => this.hourFormat() === '12');
354
+ /** Hora exibida no time picker: 1–12 no formato 12h, 0–23 no formato 24h. */
355
+ displayHours = computed(() => {
356
+ if (!this.is12HourFormat())
357
+ return this.hours();
358
+ const h12 = this.hours() % DatepickerComponent.HOURS_PER_MERIDIEM;
359
+ return h12 === 0 ? DatepickerComponent.HOURS_PER_MERIDIEM : h12;
360
+ });
361
+ /** Meridiano atual derivado da hora interna (0–23). */
362
+ amPm = computed(() => this.hours() < DatepickerComponent.HOURS_PER_MERIDIEM ? 'AM' : 'PM');
363
+ positions = [
364
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
365
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
366
+ ];
367
+ popupId = computed(() => `${this.inputId()}-popup`);
368
+ currentMonthIndex = computed(() => this.viewDate().getMonth());
369
+ currentYear = computed(() => this.viewDate().getFullYear());
370
+ currentMonthKey = computed(() => this.monthKeys[this.currentMonthIndex()]);
371
+ showCalendar = computed(() => this.mode() !== 'time');
372
+ showTimePicker = computed(() => this.mode() === 'datetime' || this.mode() === 'time');
373
+ /** Seleção múltipla ativa: apenas no modo `date` com `selectionMode = 'multiple'`. */
374
+ isMultipleMode = computed(() => this.selectionMode() === 'multiple' && this.mode() === 'date');
375
+ orderedWeekDayKeys = computed(() => {
376
+ const start = this.firstDayOfWeek();
377
+ return [...this.weekDayKeys.slice(start), ...this.weekDayKeys.slice(0, start)];
378
+ });
379
+ inputType = computed(() => {
380
+ if (this.isMultipleMode())
381
+ return 'text';
382
+ const mode = this.mode();
383
+ if (mode === 'datetime')
384
+ return 'datetime-local';
385
+ if (mode === 'range' || mode === 'year')
386
+ return 'text';
387
+ return mode;
388
+ });
389
+ dialogAriaLabelKey = computed(() => this.mode() === 'range'
390
+ ? 'platform.angular_components.datepicker_select_range'
391
+ : 'platform.angular_components.datepicker_select_date');
392
+ placeholderKey = computed(() => {
393
+ const mode = this.mode();
394
+ return `platform.angular_components.datepicker_placeholder_${mode}`;
395
+ });
396
+ effectiveRange = computed(() => {
397
+ const pending = this.pendingRangeStart();
398
+ const hover = this.hoverDate();
399
+ if (pending) {
400
+ if (hover) {
401
+ const [start, end] = pending.getTime() <= hover.getTime() ? [pending, hover] : [hover, pending];
402
+ return { start, end };
403
+ }
404
+ return { start: pending, end: null };
405
+ }
406
+ return this.rangeValue() ?? { start: null, end: null };
407
+ });
408
+ calendarWeeks = computed(() => {
409
+ const year = this.currentYear();
410
+ const month = this.currentMonthIndex();
411
+ const today = new Date();
412
+ const selected = this.value();
413
+ const range = this.effectiveRange();
414
+ const rangeStartTime = range.start ? stripTime(range.start) : null;
415
+ const rangeEndTime = range.end ? stripTime(range.end) : null;
416
+ const firstDayOfMonth = new Date(year, month, 1);
417
+ const startWeekDay = firstDayOfMonth.getDay();
418
+ const adjustedStartDay = (startWeekDay - this.firstDayOfWeek() + 7) % 7;
419
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
420
+ const daysInPrevMonth = new Date(year, month, 0).getDate();
421
+ const allDays = [];
422
+ for (let i = adjustedStartDay - 1; i >= 0; i--) {
423
+ const date = new Date(year, month - 1, daysInPrevMonth - i);
424
+ allDays.push(this.buildCalendarDay(date, false, today, selected, range, rangeStartTime, rangeEndTime));
425
+ }
426
+ for (let d = 1; d <= daysInMonth; d++) {
427
+ const date = new Date(year, month, d);
428
+ allDays.push(this.buildCalendarDay(date, true, today, selected, range, rangeStartTime, rangeEndTime));
429
+ }
430
+ const totalCells = Math.ceil(allDays.length / DatepickerComponent.DAYS_IN_WEEK) * DatepickerComponent.DAYS_IN_WEEK;
431
+ let nextDay = 1;
432
+ while (allDays.length < totalCells) {
433
+ const date = new Date(year, month + 1, nextDay);
434
+ nextDay++;
435
+ allDays.push(this.buildCalendarDay(date, false, today, selected, range, rangeStartTime, rangeEndTime));
436
+ }
437
+ const weeks = [];
438
+ for (let i = 0; i < allDays.length; i += DatepickerComponent.DAYS_IN_WEEK) {
439
+ weeks.push(allDays.slice(i, i + DatepickerComponent.DAYS_IN_WEEK));
440
+ }
441
+ return weeks;
442
+ });
443
+ monthsGrid = computed(() => {
444
+ const rows = [];
445
+ for (let i = 0; i < DatepickerComponent.MONTHS_IN_YEAR; i += DatepickerComponent.MONTHS_PER_ROW) {
446
+ rows.push([
447
+ { key: this.monthKeys[i], index: i },
448
+ { key: this.monthKeys[i + 1], index: i + 1 },
449
+ { key: this.monthKeys[i + 2], index: i + 2 },
450
+ ]);
451
+ }
452
+ return rows;
453
+ });
454
+ currentDecadeStart = computed(() => Math.floor(this.currentYear() / 10) * 10);
455
+ yearsGrid = computed(() => {
456
+ const start = this.currentDecadeStart();
457
+ const rows = [];
458
+ for (let i = 0; i < DatepickerComponent.YEARS_PER_GRID; i += DatepickerComponent.YEARS_PER_ROW) {
459
+ rows.push([start + i, start + i + 1, start + i + 2]);
460
+ }
461
+ return rows;
462
+ });
463
+ prevDisabled = computed(() => {
464
+ const min = this.min();
465
+ if (!min)
466
+ return false;
467
+ const view = this.viewDate();
468
+ if (this.pickerView() === 'years') {
469
+ return this.currentDecadeStart() <= min.getFullYear();
470
+ }
471
+ if (this.pickerView() === 'months') {
472
+ return view.getFullYear() <= min.getFullYear();
473
+ }
474
+ return (view.getFullYear() < min.getFullYear() ||
475
+ (view.getFullYear() === min.getFullYear() && view.getMonth() <= min.getMonth()));
476
+ });
477
+ nextDisabled = computed(() => {
478
+ const max = this.max();
479
+ if (!max)
480
+ return false;
481
+ const view = this.viewDate();
482
+ if (this.pickerView() === 'years') {
483
+ return this.currentDecadeStart() + DatepickerComponent.YEARS_PER_GRID - 1 >= max.getFullYear();
484
+ }
485
+ if (this.pickerView() === 'months') {
486
+ return view.getFullYear() >= max.getFullYear();
487
+ }
488
+ return (view.getFullYear() > max.getFullYear() ||
489
+ (view.getFullYear() === max.getFullYear() && view.getMonth() >= max.getMonth()));
490
+ });
491
+ // ── Translation keys ──────────────────────────────────────────────────
492
+ weekDayKeys = [
493
+ 'platform.angular_components.datepicker_day_sun',
494
+ 'platform.angular_components.datepicker_day_mon',
495
+ 'platform.angular_components.datepicker_day_tue',
496
+ 'platform.angular_components.datepicker_day_wed',
497
+ 'platform.angular_components.datepicker_day_thu',
498
+ 'platform.angular_components.datepicker_day_fri',
499
+ 'platform.angular_components.datepicker_day_sat',
500
+ ];
501
+ monthKeys = [
502
+ 'platform.angular_components.datepicker_month_jan',
503
+ 'platform.angular_components.datepicker_month_feb',
504
+ 'platform.angular_components.datepicker_month_mar',
505
+ 'platform.angular_components.datepicker_month_apr',
506
+ 'platform.angular_components.datepicker_month_may',
507
+ 'platform.angular_components.datepicker_month_jun',
508
+ 'platform.angular_components.datepicker_month_jul',
509
+ 'platform.angular_components.datepicker_month_aug',
510
+ 'platform.angular_components.datepicker_month_sep',
511
+ 'platform.angular_components.datepicker_month_oct',
512
+ 'platform.angular_components.datepicker_month_nov',
513
+ 'platform.angular_components.datepicker_month_dec',
514
+ ];
515
+ // ── Lifecycle ─────────────────────────────────────────────────────────
516
+ ngOnInit() {
517
+ this.ngControl = this.injector.get(NgControl, null, { self: true, optional: true });
518
+ afterNextRender(() => {
519
+ const d = this.defaultDate();
520
+ if (d && !this.value() && !this.rangeValue() && this.multipleValues().length === 0) {
521
+ this.value.set(d);
522
+ this.inputDate = this.formatForDisplay(d);
523
+ this.viewDate.set(new Date(d.getFullYear(), d.getMonth(), 1));
524
+ this._onChange(d);
525
+ }
526
+ }, { injector: this.injector });
527
+ }
528
+ // ── ControlValueAccessor ──────────────────────────────────────────────
529
+ writeValue(value) {
530
+ if (!value || (Array.isArray(value) && value.length === 0)) {
531
+ this.value.set(null);
532
+ this.rangeValue.set(null);
533
+ this.multipleValues.set([]);
534
+ this.inputDate = '';
535
+ return;
536
+ }
537
+ if (Array.isArray(value)) {
538
+ const sorted = [...value].sort((a, b) => a.getTime() - b.getTime());
539
+ this.multipleValues.set(sorted);
540
+ this.inputDate = this.formatForDisplay(sorted);
541
+ const first = sorted[0];
542
+ this.viewDate.set(new Date(first.getFullYear(), first.getMonth(), 1));
543
+ return;
544
+ }
545
+ if (this.isDateRange(value)) {
546
+ this.rangeValue.set(value);
547
+ this.inputDate = this.formatForDisplay(value);
548
+ if (value.start) {
549
+ this.viewDate.set(new Date(value.start.getFullYear(), value.start.getMonth(), 1));
550
+ }
551
+ return;
552
+ }
553
+ this.value.set(value);
554
+ this.inputDate = this.formatForDisplay(value);
555
+ const d = value;
556
+ this.viewDate.set(new Date(d.getFullYear(), d.getMonth(), 1));
557
+ this.hours.set(d.getHours());
558
+ this.minutes.set(d.getMinutes());
559
+ this.seconds.set(d.getSeconds());
560
+ }
561
+ registerOnChange(fn) {
562
+ this._onChange = fn;
563
+ }
564
+ registerOnTouched(fn) {
565
+ this._onTouched = fn;
566
+ }
567
+ setDisabledState(isDisabled) {
568
+ this.disabled.set(isDisabled);
569
+ }
570
+ // ── Validator ─────────────────────────────────────────────────────────
571
+ validate(control) {
572
+ let value;
573
+ if (this.isMultipleMode()) {
574
+ value = this.multipleValues();
575
+ }
576
+ else if (this.mode() === 'range') {
577
+ value = this.rangeValue();
578
+ }
579
+ else {
580
+ value = this.value();
581
+ }
582
+ return this.computeValidationErrors(value, control);
583
+ }
584
+ registerOnValidatorChange(fn) {
585
+ this.onValidatorChange = fn;
586
+ }
587
+ // ── Public actions ────────────────────────────────────────────────────
588
+ open() {
589
+ if (this.disabled())
590
+ return;
591
+ this.isOpen.set(true);
592
+ this.prepareOpenView();
593
+ this.show.emit();
594
+ if (this.mode() === 'time') {
595
+ afterNextRender(() => this.increaseHourButton()?.nativeElement.focus(), { injector: this.injector });
596
+ }
597
+ else {
598
+ this.focusBestCalendarTarget();
599
+ }
600
+ }
601
+ /** Abre o painel sem transferir o foco para o dropdown (usado pelo showOnFocus). */
602
+ openOnFocus() {
603
+ if (this.disabled() || this.isOpen())
604
+ return;
605
+ this.isOpen.set(true);
606
+ this.prepareOpenView();
607
+ this.show.emit();
608
+ // Foco permanece no input — não chama focusBestCalendarTarget()
609
+ }
610
+ /**
611
+ * Define a view inicial do painel e posiciona o calendário no `defaultDate`
612
+ * quando nenhum valor está selecionado.
613
+ */
614
+ prepareOpenView() {
615
+ const defaultDate = this.defaultDate();
616
+ if (defaultDate && !this.value() && !this.rangeValue()) {
617
+ this.viewDate.set(new Date(defaultDate.getFullYear(), defaultDate.getMonth(), 1));
618
+ }
619
+ if (this.mode() === 'month') {
620
+ this.pickerView.set('months');
621
+ }
622
+ else if (this.mode() === 'year') {
623
+ this.pickerView.set('years');
624
+ }
625
+ else {
626
+ this.pickerView.set('calendar');
627
+ }
628
+ }
629
+ focusBestCalendarTarget() {
630
+ afterNextRender(() => {
631
+ const panel = this.popupPanel()?.nativeElement;
632
+ if (!panel)
633
+ return;
634
+ const target = panel.querySelector('[data-focus="selected"]') ??
635
+ panel.querySelector('[data-focus="today"]') ??
636
+ panel.querySelector('button:not([disabled])');
637
+ target?.focus();
638
+ }, { injector: this.injector });
639
+ }
640
+ closePanel() {
641
+ const panel = (this.popupPanel()?.nativeElement ?? this.hostElement.nativeElement);
642
+ this.isOpen.set(false);
643
+ this.pendingRangeStart.set(null);
644
+ this.hoverDate.set(null);
645
+ this._onTouched();
646
+ this.closed.emit(panel);
647
+ afterNextRender(() => {
648
+ this.iconButton()?.nativeElement.focus();
649
+ }, { injector: this.injector });
650
+ }
651
+ toggle() {
652
+ if (this.isOpen()) {
653
+ this.closePanel();
654
+ }
655
+ else {
656
+ this.open();
657
+ }
658
+ }
659
+ handleOverlayKeydown(event) {
660
+ if (event.key === 'Escape') {
661
+ this.closePanel();
662
+ }
663
+ }
664
+ handleInputClick() { }
665
+ handleInputFocus(event) {
666
+ this.focused.emit(event);
667
+ if (this.showOnFocus()) {
668
+ this.openOnFocus();
669
+ }
670
+ }
671
+ handleNativeInput(event) {
672
+ this.valueInput.emit(event);
673
+ }
674
+ handleInputBlur(event) {
675
+ this.blurred.emit(event);
676
+ const input = event.target;
677
+ this._isBadInput = input.validity?.badInput ?? false;
678
+ if (!input.value?.trim()) {
679
+ this.onValidatorChange();
680
+ }
681
+ if (this.isOpen()) {
682
+ const relatedTarget = event.relatedTarget;
683
+ const panel = this.popupPanel()?.nativeElement;
684
+ if (!relatedTarget || !panel?.contains(relatedTarget)) {
685
+ if (this.mode() === 'time') {
686
+ this.increaseHourButton()?.nativeElement.focus();
687
+ }
688
+ else {
689
+ const target = panel?.querySelector('[data-focus="selected"]') ??
690
+ panel?.querySelector('[data-focus="today"]') ??
691
+ panel?.querySelector('button:not([disabled])');
692
+ target?.focus();
693
+ }
694
+ }
695
+ }
696
+ }
697
+ handleInputChange(raw) {
698
+ if (!raw?.trim()) {
699
+ this.value.set(null);
700
+ this.rangeValue.set(null);
701
+ this._hasFormatError = false;
702
+ this.onValidatorChange();
703
+ this._onChange(null);
704
+ return;
705
+ }
706
+ const parsed = parseInput(this.mode(), raw, this.value(), this.maxHours, this.maxMinutes, this.rangeSeparator());
707
+ if (parsed === null) {
708
+ this._hasFormatError = true;
709
+ this.onValidatorChange();
710
+ return;
711
+ }
712
+ this._hasFormatError = false;
713
+ this._isBadInput = false;
714
+ this.onValidatorChange();
715
+ if (this.isDateRange(parsed)) {
716
+ this.rangeValue.set(parsed);
717
+ this._onChange(parsed);
718
+ }
719
+ else {
720
+ const d = parsed;
721
+ this.value.set(d);
722
+ this.viewDate.set(new Date(d.getFullYear(), d.getMonth(), 1));
723
+ this.hours.set(d.getHours());
724
+ this.minutes.set(d.getMinutes());
725
+ this._onChange(d);
726
+ }
727
+ }
728
+ handleTriggerKeydown(event) {
729
+ if (event.key === 'Enter' || event.key === ' ') {
730
+ event.preventDefault();
731
+ this.open();
732
+ }
733
+ }
734
+ // ── Navigation ────────────────────────────────────────────────────────
735
+ navigatePrev() {
736
+ const view = this.viewDate();
737
+ if (this.pickerView() === 'years') {
738
+ this.viewDate.set(new Date(view.getFullYear() - 10, view.getMonth(), 1));
739
+ this.yearChange.emit({ month: view.getMonth(), year: view.getFullYear() - 10 });
740
+ }
741
+ else if (this.pickerView() === 'months') {
742
+ this.viewDate.set(new Date(view.getFullYear() - 1, view.getMonth(), 1));
743
+ this.yearChange.emit({ month: view.getMonth(), year: view.getFullYear() - 1 });
744
+ }
745
+ else {
746
+ const newDate = new Date(view.getFullYear(), view.getMonth() - 1, 1);
747
+ this.viewDate.set(newDate);
748
+ this.monthChange.emit({ month: newDate.getMonth(), year: newDate.getFullYear() });
749
+ }
750
+ }
751
+ navigateNext() {
752
+ const view = this.viewDate();
753
+ if (this.pickerView() === 'years') {
754
+ this.viewDate.set(new Date(view.getFullYear() + 10, view.getMonth(), 1));
755
+ this.yearChange.emit({ month: view.getMonth(), year: view.getFullYear() + 10 });
756
+ }
757
+ else if (this.pickerView() === 'months') {
758
+ this.viewDate.set(new Date(view.getFullYear() + 1, view.getMonth(), 1));
759
+ this.yearChange.emit({ month: view.getMonth(), year: view.getFullYear() + 1 });
760
+ }
761
+ else {
762
+ const newDate = new Date(view.getFullYear(), view.getMonth() + 1, 1);
763
+ this.viewDate.set(newDate);
764
+ this.monthChange.emit({ month: newDate.getMonth(), year: newDate.getFullYear() });
765
+ }
766
+ }
767
+ handleCalendarGridKeydown(event) {
768
+ const ARROWS = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
769
+ if (!ARROWS.includes(event.key))
770
+ return;
771
+ event.preventDefault();
772
+ const grid = event.currentTarget;
773
+ const buttons = Array.from(grid.querySelectorAll('button[role="gridcell"]'));
774
+ const currentIdx = buttons.indexOf(event.target);
775
+ if (currentIdx === -1)
776
+ return;
777
+ let delta = 0;
778
+ if (event.key === 'ArrowRight')
779
+ delta = 1;
780
+ else if (event.key === 'ArrowLeft')
781
+ delta = -1;
782
+ else if (event.key === 'ArrowDown')
783
+ delta = DatepickerComponent.DAYS_IN_WEEK;
784
+ else if (event.key === 'ArrowUp')
785
+ delta = -DatepickerComponent.DAYS_IN_WEEK;
786
+ const nextIdx = currentIdx + delta;
787
+ if (nextIdx < 0) {
788
+ if (this.prevDisabled())
789
+ return;
790
+ this.navigatePrev();
791
+ afterNextRender(() => {
792
+ const panel = this.popupPanel()?.nativeElement;
793
+ const btns = Array.from(panel?.querySelectorAll('button[role="gridcell"]') ?? []);
794
+ const targetIdx = Math.max(0, btns.length + nextIdx);
795
+ const target = DatepickerComponent.findEnabled(btns, targetIdx, -1) ??
796
+ DatepickerComponent.findEnabled(btns, targetIdx, 1);
797
+ target?.focus();
798
+ }, { injector: this.injector });
799
+ return;
800
+ }
801
+ if (nextIdx >= buttons.length) {
802
+ if (this.nextDisabled())
803
+ return;
804
+ this.navigateNext();
805
+ afterNextRender(() => {
806
+ const panel = this.popupPanel()?.nativeElement;
807
+ const btns = Array.from(panel?.querySelectorAll('button[role="gridcell"]') ?? []);
808
+ const targetIdx = Math.min(nextIdx - buttons.length, btns.length - 1);
809
+ const target = DatepickerComponent.findEnabled(btns, targetIdx, 1) ??
810
+ DatepickerComponent.findEnabled(btns, targetIdx, -1);
811
+ target?.focus();
812
+ }, { injector: this.injector });
813
+ return;
814
+ }
815
+ if (!buttons[nextIdx]?.disabled) {
816
+ buttons[nextIdx].focus();
817
+ }
818
+ }
819
+ static findEnabled(buttons, startIdx, direction) {
820
+ for (let i = startIdx; i >= 0 && i < buttons.length; i += direction) {
821
+ if (!buttons[i].disabled)
822
+ return buttons[i];
823
+ }
824
+ return null;
825
+ }
826
+ switchToMonths() {
827
+ this.pickerView.set('months');
828
+ this.focusBestCalendarTarget();
829
+ }
830
+ backToCalendar() {
831
+ this.pickerView.set('calendar');
832
+ this.focusBestCalendarTarget();
833
+ }
834
+ // ── Selection ─────────────────────────────────────────────────────────
835
+ selectDate(day) {
836
+ if (day.isDisabled)
837
+ return;
838
+ if (this.isMultipleMode()) {
839
+ this.toggleMultipleDate(day.date);
840
+ return;
841
+ }
842
+ if (this.mode() === 'range') {
843
+ const pending = this.pendingRangeStart();
844
+ if (!pending) {
845
+ this.pendingRangeStart.set(day.date);
846
+ }
847
+ else {
848
+ const [start, end] = pending.getTime() <= day.date.getTime() ? [pending, day.date] : [day.date, pending];
849
+ const range = { start, end };
850
+ this.rangeValue.set(range);
851
+ this.inputDate = this.formatForDisplay(range);
852
+ this._onChange(range);
853
+ this.selected.emit(range);
854
+ this.closePanel();
855
+ }
856
+ return;
857
+ }
858
+ const newDate = new Date(day.date.getFullYear(), day.date.getMonth(), day.date.getDate(), this.hours(), this.minutes(), this.showSeconds() ? this.seconds() : 0);
859
+ this.value.set(newDate);
860
+ if (this.mode() === 'date') {
861
+ this.inputDate = this.formatForDisplay(newDate);
862
+ this._onChange(newDate);
863
+ this.selected.emit(newDate);
864
+ this.closePanel();
865
+ }
866
+ else if (this.mode() === 'datetime') {
867
+ this.inputDate = this.formatForDisplay(newDate);
868
+ this._onChange(newDate);
869
+ this.selected.emit(newDate);
870
+ if (this.hideOnDateTimeSelect()) {
871
+ this.closePanel();
872
+ }
873
+ }
874
+ }
875
+ /** Adiciona/remove uma data da seleção múltipla. O painel permanece aberto. */
876
+ toggleMultipleDate(date) {
877
+ const normalized = new Date(date.getFullYear(), date.getMonth(), date.getDate());
878
+ const existing = this.multipleValues();
879
+ const index = existing.findIndex((d) => isSameDay(d, normalized));
880
+ const next = index >= 0
881
+ ? existing.filter((_, i) => i !== index)
882
+ : [...existing, normalized].sort((a, b) => a.getTime() - b.getTime());
883
+ this.multipleValues.set(next);
884
+ this.inputDate = this.formatForDisplay(next);
885
+ const emitValue = next.length ? next : null;
886
+ this.onValidatorChange();
887
+ this._onChange(emitValue);
888
+ this.selected.emit(emitValue);
889
+ }
890
+ selectYear(year) {
891
+ if (this.isYearDisabled(year))
892
+ return;
893
+ const newDate = new Date(year, 0, 1);
894
+ this.value.set(newDate);
895
+ this.inputDate = this.formatForDisplay(newDate);
896
+ this._onChange(newDate);
897
+ this.selected.emit(newDate);
898
+ this.closePanel();
899
+ }
900
+ selectMonth(monthIndex) {
901
+ if (this.isMonthDisabled(monthIndex))
902
+ return;
903
+ const year = this.currentYear();
904
+ const newViewDate = new Date(year, monthIndex, 1);
905
+ if (this.mode() === 'month') {
906
+ this.value.set(newViewDate);
907
+ this.inputDate = this.formatForDisplay(newViewDate);
908
+ this._onChange(newViewDate);
909
+ this.selected.emit(newViewDate);
910
+ this.closePanel();
911
+ }
912
+ else {
913
+ this.viewDate.set(newViewDate);
914
+ this.pickerView.set('calendar');
915
+ this.focusBestCalendarTarget();
916
+ }
917
+ }
918
+ selectToday() {
919
+ const today = new Date();
920
+ if (!this.isDateDisabled(today)) {
921
+ const calDay = {
922
+ date: today,
923
+ dayOfMonth: today.getDate(),
924
+ isCurrentMonth: true,
925
+ isToday: true,
926
+ isSelected: false,
927
+ isDisabled: false,
928
+ isRangeStart: false,
929
+ isRangeEnd: false,
930
+ isInRange: false,
931
+ };
932
+ this.selectDate(calDay);
933
+ this.todayClick.emit(today);
934
+ }
935
+ }
936
+ clearValue(event) {
937
+ this.value.set(null);
938
+ this.hours.set(0);
939
+ this.minutes.set(0);
940
+ this.seconds.set(0);
941
+ this.rangeValue.set(null);
942
+ this.multipleValues.set([]);
943
+ this.pendingRangeStart.set(null);
944
+ this.inputDate = '';
945
+ this._onChange(null);
946
+ this.clearClick.emit(event);
947
+ this.closePanel();
948
+ }
949
+ confirm() {
950
+ const current = this.value() ?? new Date();
951
+ const confirmed = new Date(current.getFullYear(), current.getMonth(), current.getDate(), this.hours(), this.minutes(), this.showSeconds() ? this.seconds() : 0);
952
+ this.value.set(confirmed);
953
+ this.inputDate = this.formatForDisplay(confirmed);
954
+ this._onChange(confirmed);
955
+ this.selected.emit(confirmed);
956
+ this.closePanel();
957
+ }
958
+ // ── Time picker ───────────────────────────────────────────────────────
959
+ incrementHours() {
960
+ this.hours.set((this.hours() + this.stepHour()) % this.maxHours);
961
+ this.syncTimeToValue();
962
+ }
963
+ decrementHours() {
964
+ this.hours.set((this.hours() - this.stepHour() + this.maxHours) % this.maxHours);
965
+ this.syncTimeToValue();
966
+ }
967
+ incrementMinutes() {
968
+ this.minutes.set((this.minutes() + this.minuteStep()) % this.maxMinutes);
969
+ this.syncTimeToValue();
970
+ }
971
+ decrementMinutes() {
972
+ this.minutes.set((this.minutes() - this.minuteStep() + this.maxMinutes) % this.maxMinutes);
973
+ this.syncTimeToValue();
974
+ }
975
+ incrementSeconds() {
976
+ this.seconds.set((this.seconds() + this.stepSecond()) % 60);
977
+ this.syncTimeToValue();
978
+ }
979
+ decrementSeconds() {
980
+ this.seconds.set((this.seconds() - this.stepSecond() + 60) % 60);
981
+ this.syncTimeToValue();
982
+ }
983
+ handleHoursKeydown(event) {
984
+ if (event.key === 'ArrowUp') {
985
+ event.preventDefault();
986
+ this.incrementHours();
987
+ }
988
+ else if (event.key === 'ArrowDown') {
989
+ event.preventDefault();
990
+ this.decrementHours();
991
+ }
992
+ }
993
+ handleMinutesKeydown(event) {
994
+ if (event.key === 'ArrowUp') {
995
+ event.preventDefault();
996
+ this.incrementMinutes();
997
+ }
998
+ else if (event.key === 'ArrowDown') {
999
+ event.preventDefault();
1000
+ this.decrementMinutes();
1001
+ }
1002
+ }
1003
+ handleSecondsKeydown(event) {
1004
+ if (event.key === 'ArrowUp') {
1005
+ event.preventDefault();
1006
+ this.incrementSeconds();
1007
+ }
1008
+ else if (event.key === 'ArrowDown') {
1009
+ event.preventDefault();
1010
+ this.decrementSeconds();
1011
+ }
1012
+ }
1013
+ handleHoursInput(event) {
1014
+ const raw = event.target.value;
1015
+ const parsed = parseInt(raw, 10);
1016
+ if (isNaN(parsed))
1017
+ return;
1018
+ if (this.is12HourFormat()) {
1019
+ const half = DatepickerComponent.HOURS_PER_MERIDIEM;
1020
+ const h12 = Math.min(half, Math.max(1, parsed));
1021
+ const base = h12 % half;
1022
+ this.hours.set(this.amPm() === 'PM' ? base + half : base);
1023
+ }
1024
+ else {
1025
+ this.hours.set(Math.min(this.maxHours - 1, Math.max(0, parsed)));
1026
+ }
1027
+ this.syncTimeToValue();
1028
+ }
1029
+ /** Alterna entre AM e PM somando/subtraindo 12 horas da hora interna. */
1030
+ toggleAmPm() {
1031
+ const half = DatepickerComponent.HOURS_PER_MERIDIEM;
1032
+ this.hours.set(this.hours() < half ? this.hours() + half : this.hours() - half);
1033
+ this.syncTimeToValue();
1034
+ }
1035
+ handleMinutesInput(event) {
1036
+ const raw = event.target.value;
1037
+ const parsed = parseInt(raw, 10);
1038
+ if (!isNaN(parsed)) {
1039
+ this.minutes.set(Math.min(this.maxMinutes - 1, Math.max(0, parsed)));
1040
+ this.syncTimeToValue();
1041
+ }
1042
+ }
1043
+ handleSecondsInput(event) {
1044
+ const raw = event.target.value;
1045
+ const parsed = parseInt(raw, 10);
1046
+ if (!isNaN(parsed)) {
1047
+ this.seconds.set(Math.min(59, Math.max(0, parsed)));
1048
+ this.syncTimeToValue();
1049
+ }
1050
+ }
1051
+ onDayHover(date) {
1052
+ if (this.mode() === 'range') {
1053
+ this.hoverDate.set(date);
1054
+ }
1055
+ }
1056
+ // ── Helpers ───────────────────────────────────────────────────────────
1057
+ isDateDisabled(date) {
1058
+ const min = this.min();
1059
+ const max = this.max();
1060
+ if (min && stripTime(date) < stripTime(min))
1061
+ return true;
1062
+ if (max && stripTime(date) > stripTime(max))
1063
+ return true;
1064
+ const disabledDays = this.disabledDays();
1065
+ if (disabledDays.length && disabledDays.includes(date.getDay()))
1066
+ return true;
1067
+ const disabledDates = this.disabledDates();
1068
+ if (disabledDates.length && disabledDates.some(d => isSameDay(d, date)))
1069
+ return true;
1070
+ return false;
1071
+ }
1072
+ isYearSelected(year) {
1073
+ const selected = this.value();
1074
+ return !!selected && selected.getFullYear() === year;
1075
+ }
1076
+ isYearDisabled(year) {
1077
+ const min = this.min();
1078
+ const max = this.max();
1079
+ if (min && year < min.getFullYear())
1080
+ return true;
1081
+ if (max && year > max.getFullYear())
1082
+ return true;
1083
+ return false;
1084
+ }
1085
+ isCurrentYear(year) {
1086
+ return year === new Date().getFullYear();
1087
+ }
1088
+ isMonthSelected(monthIndex) {
1089
+ const selected = this.value();
1090
+ return !!selected && selected.getMonth() === monthIndex && selected.getFullYear() === this.currentYear();
1091
+ }
1092
+ isMonthDisabled(monthIndex) {
1093
+ const year = this.currentYear();
1094
+ const min = this.min();
1095
+ const max = this.max();
1096
+ if (min && (year < min.getFullYear() || (year === min.getFullYear() && monthIndex < min.getMonth()))) {
1097
+ return true;
1098
+ }
1099
+ if (max && (year > max.getFullYear() || (year === max.getFullYear() && monthIndex > max.getMonth()))) {
1100
+ return true;
1101
+ }
1102
+ return false;
1103
+ }
1104
+ padTime(value) {
1105
+ return String(value).padStart(2, '0');
1106
+ }
1107
+ formatDayAriaLabel(date) {
1108
+ return date.toLocaleDateString(undefined, {
1109
+ weekday: 'long',
1110
+ year: 'numeric',
1111
+ month: 'long',
1112
+ day: 'numeric',
1113
+ });
1114
+ }
1115
+ /** Formata o valor para exibição no input, aplicando rangeSeparator, dateFormat e multipleSeparator. */
1116
+ formatForDisplay(value) {
1117
+ return formatValueForInput(this.mode(), value, this.rangeSeparator(), this.dateFormat(), this.multipleSeparator());
1118
+ }
1119
+ syncTimeToValue() {
1120
+ const date = this.value() ?? new Date();
1121
+ const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), this.hours(), this.minutes(), this.showSeconds() ? this.seconds() : 0);
1122
+ this.value.set(newDate);
1123
+ this.inputDate = this.formatForDisplay(newDate);
1124
+ if (this.mode() === 'time') {
1125
+ this._onChange(newDate);
1126
+ }
1127
+ }
1128
+ buildCalendarDay(date, isCurrentMonth, today, selected, range, rangeStartTime, rangeEndTime) {
1129
+ const isRangeStart = range.start ? isSameDay(date, range.start) : false;
1130
+ const isRangeEnd = range.end ? isSameDay(date, range.end) : false;
1131
+ const dateTime = stripTime(date);
1132
+ const isInRange = rangeStartTime !== null && rangeEndTime !== null
1133
+ ? dateTime > rangeStartTime && dateTime < rangeEndTime
1134
+ : false;
1135
+ const isSelected = this.isMultipleMode()
1136
+ ? this.multipleValues().some((d) => isSameDay(d, date))
1137
+ : selected
1138
+ ? isSameDay(date, selected)
1139
+ : false;
1140
+ return {
1141
+ dayOfMonth: date.getDate(),
1142
+ isToday: isSameDay(date, today),
1143
+ isSelected,
1144
+ isDisabled: this.isDateDisabled(date) || (!isCurrentMonth && !this.selectOtherMonths()),
1145
+ date,
1146
+ isCurrentMonth,
1147
+ isRangeStart,
1148
+ isRangeEnd,
1149
+ isInRange,
1150
+ };
1151
+ }
1152
+ computeValidationErrors(value, control) {
1153
+ if (this._hasFormatError)
1154
+ return { invalidFormat: true };
1155
+ if (this._isBadInput)
1156
+ return { badInput: true };
1157
+ if (!value || (Array.isArray(value) && value.length === 0))
1158
+ return Validators.nullValidator(control);
1159
+ const min = this.min();
1160
+ const max = this.max();
1161
+ if (Array.isArray(value)) {
1162
+ for (const d of value) {
1163
+ if (min && stripTime(d) < stripTime(min))
1164
+ return { min: { min, actual: d } };
1165
+ if (max && stripTime(d) > stripTime(max))
1166
+ return { max: { max, actual: d } };
1167
+ }
1168
+ return Validators.nullValidator(control);
1169
+ }
1170
+ if (this.isDateRange(value)) {
1171
+ if (value.start && min && stripTime(value.start) < stripTime(min)) {
1172
+ return { min: { min, actual: value.start } };
1173
+ }
1174
+ if (value.end && max && stripTime(value.end) > stripTime(max)) {
1175
+ return { max: { max, actual: value.end } };
1176
+ }
1177
+ return Validators.nullValidator(control);
1178
+ }
1179
+ const d = value;
1180
+ if (this.mode() === 'year') {
1181
+ if (min && d.getFullYear() < min.getFullYear())
1182
+ return { min: { min, actual: d } };
1183
+ if (max && d.getFullYear() > max.getFullYear())
1184
+ return { max: { max, actual: d } };
1185
+ return Validators.nullValidator(control);
1186
+ }
1187
+ if (min && stripTime(d) < stripTime(min))
1188
+ return { min: { min, actual: d } };
1189
+ if (max && stripTime(d) > stripTime(max))
1190
+ return { max: { max, actual: d } };
1191
+ return Validators.nullValidator(control);
1192
+ }
1193
+ isDateRange(value) {
1194
+ return typeof value === 'object' && value !== null && 'start' in value && 'end' in value;
1195
+ }
1196
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatepickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: DatepickerComponent, isStandalone: true, selector: "s-datepicker", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, multipleSeparator: { classPropertyName: "multipleSeparator", publicName: "multipleSeparator", isSignal: true, isRequired: false, transformFunction: null }, showIcon: { classPropertyName: "showIcon", publicName: "showIcon", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, defaultDate: { classPropertyName: "defaultDate", publicName: "defaultDate", isSignal: true, isRequired: false, transformFunction: null }, dateFormat: { classPropertyName: "dateFormat", publicName: "dateFormat", isSignal: true, isRequired: false, transformFunction: null }, hourFormat: { classPropertyName: "hourFormat", publicName: "hourFormat", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, rangeSeparator: { classPropertyName: "rangeSeparator", publicName: "rangeSeparator", isSignal: true, isRequired: false, transformFunction: null }, showOnFocus: { classPropertyName: "showOnFocus", publicName: "showOnFocus", isSignal: true, isRequired: false, transformFunction: null }, readonlyInput: { classPropertyName: "readonlyInput", publicName: "readonlyInput", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, showButtonBar: { classPropertyName: "showButtonBar", publicName: "showButtonBar", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, showOtherMonths: { classPropertyName: "showOtherMonths", publicName: "showOtherMonths", isSignal: true, isRequired: false, transformFunction: null }, selectOtherMonths: { classPropertyName: "selectOtherMonths", publicName: "selectOtherMonths", isSignal: true, isRequired: false, transformFunction: null }, hideOnDateTimeSelect: { classPropertyName: "hideOnDateTimeSelect", publicName: "hideOnDateTimeSelect", isSignal: true, isRequired: false, transformFunction: null }, stepHour: { classPropertyName: "stepHour", publicName: "stepHour", isSignal: true, isRequired: false, transformFunction: null }, stepSecond: { classPropertyName: "stepSecond", publicName: "stepSecond", isSignal: true, isRequired: false, transformFunction: null }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, disabledDates: { classPropertyName: "disabledDates", publicName: "disabledDates", isSignal: true, isRequired: false, transformFunction: null }, disabledDays: { classPropertyName: "disabledDays", publicName: "disabledDays", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", blurred: "blurred", focused: "focused", selected: "selected", closed: "closed", todayClick: "todayClick", clearClick: "clearClick", monthChange: "monthChange", yearChange: "yearChange", show: "show", valueInput: "valueInput" }, host: { properties: { "class.no-icon": "!showIcon()" } }, providers: [
1198
+ {
1199
+ provide: NG_VALUE_ACCESSOR,
1200
+ useExisting: DatepickerComponent,
1201
+ multi: true,
1202
+ },
1203
+ {
1204
+ provide: NG_VALIDATORS,
1205
+ useExisting: DatepickerComponent,
1206
+ multi: true,
1207
+ },
1208
+ ], 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 focus:outline-1 focus:outline-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()\"\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-full 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 }] });
1209
+ }
1210
+ __decorate([
1211
+ numericKeysOnly()
1212
+ ], DatepickerComponent.prototype, "handleHoursKeydown", null);
1213
+ __decorate([
1214
+ numericKeysOnly()
1215
+ ], DatepickerComponent.prototype, "handleMinutesKeydown", null);
1216
+ __decorate([
1217
+ numericKeysOnly()
1218
+ ], DatepickerComponent.prototype, "handleSecondsKeydown", null);
1219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatepickerComponent, decorators: [{
1220
+ type: Component,
1221
+ args: [{ selector: 's-datepicker', standalone: true, imports: [
1222
+ NgClass,
1223
+ TranslateModule,
1224
+ CdkTrapFocus,
1225
+ CdkOverlayOrigin,
1226
+ CdkConnectedOverlay,
1227
+ FormsModule,
1228
+ ReactiveFormsModule,
1229
+ ], host: {
1230
+ '[class.no-icon]': '!showIcon()',
1231
+ }, providers: [
1232
+ {
1233
+ provide: NG_VALUE_ACCESSOR,
1234
+ useExisting: DatepickerComponent,
1235
+ multi: true,
1236
+ },
1237
+ {
1238
+ provide: NG_VALIDATORS,
1239
+ useExisting: DatepickerComponent,
1240
+ multi: true,
1241
+ },
1242
+ ], 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 focus:outline-1 focus:outline-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()\"\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-full 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"] }]
1243
+ }], propDecorators: { handleHoursKeydown: [], handleMinutesKeydown: [], handleSecondsKeydown: [] } });
1244
+
1245
+ /**
1246
+ * Generated bundle index. Do not edit.
1247
+ */
1248
+
1249
+ export { DatepickerComponent };
1250
+ //# sourceMappingURL=seniorsistemas-angular-components-datepicker.mjs.map