shirkasoft-ui-components 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/fesm2022/shirkasoft-ui-components-breadcrumb.mjs +199 -0
  2. package/fesm2022/shirkasoft-ui-components-breadcrumb.mjs.map +1 -0
  3. package/fesm2022/shirkasoft-ui-components-button.mjs +148 -0
  4. package/fesm2022/shirkasoft-ui-components-button.mjs.map +1 -0
  5. package/fesm2022/shirkasoft-ui-components-chart.mjs +153 -0
  6. package/fesm2022/shirkasoft-ui-components-chart.mjs.map +1 -0
  7. package/fesm2022/shirkasoft-ui-components-confirm-dialog.mjs +143 -0
  8. package/fesm2022/shirkasoft-ui-components-confirm-dialog.mjs.map +1 -0
  9. package/fesm2022/shirkasoft-ui-components-date-picker.mjs +638 -0
  10. package/fesm2022/shirkasoft-ui-components-date-picker.mjs.map +1 -0
  11. package/fesm2022/shirkasoft-ui-components-fieldset.mjs +94 -0
  12. package/fesm2022/shirkasoft-ui-components-fieldset.mjs.map +1 -0
  13. package/fesm2022/shirkasoft-ui-components-file-upload.mjs +242 -0
  14. package/fesm2022/shirkasoft-ui-components-file-upload.mjs.map +1 -0
  15. package/fesm2022/shirkasoft-ui-components-knob.mjs +269 -0
  16. package/fesm2022/shirkasoft-ui-components-knob.mjs.map +1 -0
  17. package/fesm2022/shirkasoft-ui-components-loading.mjs +255 -0
  18. package/fesm2022/shirkasoft-ui-components-loading.mjs.map +1 -0
  19. package/fesm2022/shirkasoft-ui-components-modal.mjs +290 -0
  20. package/fesm2022/shirkasoft-ui-components-modal.mjs.map +1 -0
  21. package/fesm2022/shirkasoft-ui-components-notification.mjs +143 -0
  22. package/fesm2022/shirkasoft-ui-components-notification.mjs.map +1 -0
  23. package/fesm2022/shirkasoft-ui-components-price-input.mjs +64 -0
  24. package/fesm2022/shirkasoft-ui-components-price-input.mjs.map +1 -0
  25. package/fesm2022/shirkasoft-ui-components-rail-sidebar.mjs +205 -0
  26. package/fesm2022/shirkasoft-ui-components-rail-sidebar.mjs.map +1 -0
  27. package/fesm2022/shirkasoft-ui-components-select.mjs +737 -0
  28. package/fesm2022/shirkasoft-ui-components-select.mjs.map +1 -0
  29. package/fesm2022/shirkasoft-ui-components-sidebar.mjs +185 -0
  30. package/fesm2022/shirkasoft-ui-components-sidebar.mjs.map +1 -0
  31. package/fesm2022/shirkasoft-ui-components-skeleton.mjs +74 -0
  32. package/fesm2022/shirkasoft-ui-components-skeleton.mjs.map +1 -0
  33. package/fesm2022/shirkasoft-ui-components-table.mjs +689 -0
  34. package/fesm2022/shirkasoft-ui-components-table.mjs.map +1 -0
  35. package/fesm2022/shirkasoft-ui-components-tabs.mjs +249 -0
  36. package/fesm2022/shirkasoft-ui-components-tabs.mjs.map +1 -0
  37. package/fesm2022/shirkasoft-ui-components-tag.mjs +70 -0
  38. package/fesm2022/shirkasoft-ui-components-tag.mjs.map +1 -0
  39. package/fesm2022/shirkasoft-ui-components-text-field.mjs +370 -0
  40. package/fesm2022/shirkasoft-ui-components-text-field.mjs.map +1 -0
  41. package/fesm2022/shirkasoft-ui-components-timeline.mjs +222 -0
  42. package/fesm2022/shirkasoft-ui-components-timeline.mjs.map +1 -0
  43. package/fesm2022/shirkasoft-ui-components-toggle.mjs +186 -0
  44. package/fesm2022/shirkasoft-ui-components-toggle.mjs.map +1 -0
  45. package/fesm2022/shirkasoft-ui-components-tooltip.mjs +169 -0
  46. package/fesm2022/shirkasoft-ui-components-tooltip.mjs.map +1 -0
  47. package/fesm2022/shirkasoft-ui-components-utils.mjs +64 -0
  48. package/fesm2022/shirkasoft-ui-components-utils.mjs.map +1 -0
  49. package/fesm2022/shirkasoft-ui-components-wizard.mjs +167 -0
  50. package/fesm2022/shirkasoft-ui-components-wizard.mjs.map +1 -0
  51. package/fesm2022/shirkasoft-ui-components.mjs +0 -5729
  52. package/fesm2022/shirkasoft-ui-components.mjs.map +1 -1
  53. package/package.json +101 -1
  54. package/types/shirkasoft-ui-components-breadcrumb.d.ts +68 -0
  55. package/types/shirkasoft-ui-components-button.d.ts +59 -0
  56. package/types/shirkasoft-ui-components-chart.d.ts +65 -0
  57. package/types/shirkasoft-ui-components-confirm-dialog.d.ts +88 -0
  58. package/types/shirkasoft-ui-components-date-picker.d.ts +200 -0
  59. package/types/shirkasoft-ui-components-fieldset.d.ts +72 -0
  60. package/types/shirkasoft-ui-components-file-upload.d.ts +87 -0
  61. package/types/shirkasoft-ui-components-knob.d.ts +69 -0
  62. package/types/shirkasoft-ui-components-loading.d.ts +94 -0
  63. package/types/shirkasoft-ui-components-modal.d.ts +139 -0
  64. package/types/shirkasoft-ui-components-notification.d.ts +82 -0
  65. package/types/shirkasoft-ui-components-price-input.d.ts +41 -0
  66. package/types/shirkasoft-ui-components-rail-sidebar.d.ts +121 -0
  67. package/types/shirkasoft-ui-components-select.d.ts +182 -0
  68. package/types/shirkasoft-ui-components-sidebar.d.ts +106 -0
  69. package/types/shirkasoft-ui-components-skeleton.d.ts +36 -0
  70. package/types/shirkasoft-ui-components-table.d.ts +293 -0
  71. package/types/shirkasoft-ui-components-tabs.d.ts +102 -0
  72. package/types/shirkasoft-ui-components-tag.d.ts +31 -0
  73. package/types/shirkasoft-ui-components-text-field.d.ts +120 -0
  74. package/types/shirkasoft-ui-components-timeline.d.ts +178 -0
  75. package/types/shirkasoft-ui-components-toggle.d.ts +48 -0
  76. package/types/shirkasoft-ui-components-tooltip.d.ts +47 -0
  77. package/types/shirkasoft-ui-components-utils.d.ts +9 -0
  78. package/types/shirkasoft-ui-components-wizard.d.ts +101 -0
  79. package/types/shirkasoft-ui-components.d.ts +1 -2333
@@ -0,0 +1,638 @@
1
+ import * as i1 from '@angular/common';
2
+ import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { inject, ElementRef, ChangeDetectorRef, DestroyRef, input, signal, computed, effect, forwardRef, HostListener, ChangeDetectionStrategy, Component } from '@angular/core';
5
+ import * as i2 from '@angular/forms';
6
+ import { ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
+ import { TranslocoService } from '@jsverse/transloco';
9
+
10
+ /**
11
+ * Shirkasoft UI Components.
12
+ * Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).
13
+ * Belongs to Shirkasoft.
14
+ */
15
+ /**
16
+ * Date/time picker implemented as a ControlValueAccessor. Supports choosing a
17
+ * day, month or year, optional time selection (with seconds/12-hour format),
18
+ * min/max constraints and locale-aware labels.
19
+ */
20
+ class DatePickerComponent {
21
+ constructor() {
22
+ this.elementRef = inject(ElementRef);
23
+ this.cdr = inject(ChangeDetectorRef);
24
+ this.destroyRef = inject(DestroyRef);
25
+ this.transloco = inject(TranslocoService);
26
+ /** HTML id for the input (for label/for pairing). */
27
+ this.id = input('', ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
28
+ /** Granularity of the picker ('date' | 'month' | 'year'). */
29
+ this.view = input('date', ...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
30
+ /** Label shown above the input. */
31
+ this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
32
+ /** Placeholder of the input. */
33
+ this.placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
34
+ /** FormGroup used to resolve the control from `label`. */
35
+ this.formGroup = input(undefined, ...(ngDevMode ? [{ debugName: "formGroup" }] : /* istanbul ignore next */ []));
36
+ /** Directly bound FormControl. */
37
+ this.control = input(undefined, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
38
+ /** Disables the picker. */
39
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
40
+ /** Custom error message displayed (overrides translated ones). */
41
+ this.errorMessage = input('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
42
+ /** Per-validation-key custom error messages. */
43
+ this.errorMessages = input({}, ...(ngDevMode ? [{ debugName: "errorMessages" }] : /* istanbul ignore next */ []));
44
+ /** Minimum allowed date (YYYY-MM-DD) or year/month string. */
45
+ this.minValue = input('', ...(ngDevMode ? [{ debugName: "minValue" }] : /* istanbul ignore next */ []));
46
+ /** Maximum allowed date (YYYY-MM-DD) or year/month string. */
47
+ this.maxValue = input('', ...(ngDevMode ? [{ debugName: "maxValue" }] : /* istanbul ignore next */ []));
48
+ /** Locale used for month/day names and formatted labels. */
49
+ this.locale = input('es', ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
50
+ /** Edits only the time portion of the value. */
51
+ this.timeOnly = input(false, ...(ngDevMode ? [{ debugName: "timeOnly" }] : /* istanbul ignore next */ []));
52
+ /** Shows the time selectors alongside the date. */
53
+ this.showTime = input(false, ...(ngDevMode ? [{ debugName: "showTime" }] : /* istanbul ignore next */ []));
54
+ /** Hour format: '12' or '24'. */
55
+ this.hourFormat = input('24', ...(ngDevMode ? [{ debugName: "hourFormat" }] : /* istanbul ignore next */ []));
56
+ /** Whether the seconds selector is shown. */
57
+ this.showSeconds = input(false, ...(ngDevMode ? [{ debugName: "showSeconds" }] : /* istanbul ignore next */ []));
58
+ /** Step (in minutes) for the minute selector. */
59
+ this.minuteStep = input(1, ...(ngDevMode ? [{ debugName: "minuteStep" }] : /* istanbul ignore next */ []));
60
+ /** Whether the dropdown calendar is open. */
61
+ this.isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
62
+ /** Year currently displayed in the calendar. */
63
+ this.viewYear = signal(new Date().getFullYear(), ...(ngDevMode ? [{ debugName: "viewYear" }] : /* istanbul ignore next */ []));
64
+ /** Month currently displayed in the calendar (0-based). */
65
+ this.viewMonth = signal(new Date().getMonth(), ...(ngDevMode ? [{ debugName: "viewMonth" }] : /* istanbul ignore next */ []));
66
+ /** Starting year of the decade shown in year view. */
67
+ this.viewDecade = signal(Math.floor(new Date().getFullYear() / 10) * 10, ...(ngDevMode ? [{ debugName: "viewDecade" }] : /* istanbul ignore next */ []));
68
+ /** Hour selected in the time selectors. */
69
+ this.viewHour = signal(0, ...(ngDevMode ? [{ debugName: "viewHour" }] : /* istanbul ignore next */ []));
70
+ /** Minute selected in the time selectors. */
71
+ this.viewMinute = signal(0, ...(ngDevMode ? [{ debugName: "viewMinute" }] : /* istanbul ignore next */ []));
72
+ /** Second selected in the time selectors. */
73
+ this.viewSecond = signal(0, ...(ngDevMode ? [{ debugName: "viewSecond" }] : /* istanbul ignore next */ []));
74
+ /** Meridiem selected in 12-hour format. */
75
+ this.viewMeridiem = signal('AM', ...(ngDevMode ? [{ debugName: "viewMeridiem" }] : /* istanbul ignore next */ []));
76
+ /** Date part selected but not yet committed until the time is confirmed. */
77
+ this.pendingDate = signal(null, ...(ngDevMode ? [{ debugName: "pendingDate" }] : /* istanbul ignore next */ []));
78
+ /** Underlying value (mirrors the reactive control). */
79
+ this._value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
80
+ /** Disabled state coming from the reactive form infrastructure. */
81
+ this._disabledState = signal(false, ...(ngDevMode ? [{ debugName: "_disabledState" }] : /* istanbul ignore next */ []));
82
+ /** Whether status subscriptions have been attached to the control. */
83
+ this.subscribed = signal(false, ...(ngDevMode ? [{ debugName: "subscribed" }] : /* istanbul ignore next */ []));
84
+ this.valueSubscribed = signal(false, ...(ngDevMode ? [{ debugName: "valueSubscribed" }] : /* istanbul ignore next */ []));
85
+ /** Counter used to recompute the error helpers reactively. */
86
+ this.errorTracker = signal(0, ...(ngDevMode ? [{ debugName: "errorTracker" }] : /* istanbul ignore next */ []));
87
+ /** Whether an error was displayed at some point. */
88
+ this.hadError = signal(false, ...(ngDevMode ? [{ debugName: "hadError" }] : /* istanbul ignore next */ []));
89
+ /** ControlValueAccessor callbacks. */
90
+ this.onChange = () => { };
91
+ this.onTouched = () => { };
92
+ /** Current value of the picker. */
93
+ this.value = computed(() => this._value(), ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
94
+ /** Whether the picker is disabled (input or reactive form). */
95
+ this.isDisabled = computed(() => this.disabled() || this._disabledState(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
96
+ /** Resolves the control from `control` input or `formGroup`+`label`. */
97
+ this.actualControl = computed(() => {
98
+ const ctrl = this.control();
99
+ if (ctrl)
100
+ return ctrl;
101
+ const fg = this.formGroup();
102
+ const lbl = this.label();
103
+ if (fg && lbl)
104
+ return fg.get(lbl);
105
+ return undefined;
106
+ }, ...(ngDevMode ? [{ debugName: "actualControl" }] : /* istanbul ignore next */ []));
107
+ /** Whether the bound control should show its error message. */
108
+ this.shouldShowError = computed(() => {
109
+ this.errorTracker();
110
+ const ctrl = this.actualControl();
111
+ return ctrl?.invalid && (ctrl?.dirty || ctrl?.touched);
112
+ }, ...(ngDevMode ? [{ debugName: "shouldShowError" }] : /* istanbul ignore next */ []));
113
+ /** Whether an error was fixed (invalid before, now valid and touched). */
114
+ this.wasFixedError = computed(() => {
115
+ this.errorTracker();
116
+ const ctrl = this.actualControl();
117
+ return this.hadError() && ctrl?.valid && ctrl?.touched;
118
+ }, ...(ngDevMode ? [{ debugName: "wasFixedError" }] : /* istanbul ignore next */ []));
119
+ /** Validation error messages to display, custom or translated. */
120
+ this.getErrorMessages = computed(() => {
121
+ this.errorTracker();
122
+ const customError = this.errorMessage();
123
+ if (customError)
124
+ return [customError];
125
+ const ctrl = this.actualControl();
126
+ if (!ctrl?.errors)
127
+ return [];
128
+ const customErrors = this.errorMessages();
129
+ const t = this.transloco;
130
+ return Object.keys(ctrl.errors).map((key) => {
131
+ if (customErrors[key])
132
+ return customErrors[key];
133
+ switch (key) {
134
+ case 'required':
135
+ return t.translate('validation.required');
136
+ default:
137
+ return t.translate('validation.invalid');
138
+ }
139
+ });
140
+ }, ...(ngDevMode ? [{ debugName: "getErrorMessages" }] : /* istanbul ignore next */ []));
141
+ /** Date part displayed in the calendar (pending date or committed value). */
142
+ this.displayedDate = computed(() => {
143
+ if (this.timeOnly())
144
+ return null;
145
+ const value = this.pendingDate();
146
+ if (value)
147
+ return value;
148
+ const current = this._value();
149
+ if (!current)
150
+ return null;
151
+ return this.datePartOf(current);
152
+ }, ...(ngDevMode ? [{ debugName: "displayedDate" }] : /* istanbul ignore next */ []));
153
+ /** Human-readable label for the selected value in the input. */
154
+ this.displayLabel = computed(() => {
155
+ const value = this._value();
156
+ if (!value)
157
+ return '';
158
+ if (this.timeOnly()) {
159
+ return this.formatTimeFromValue(value);
160
+ }
161
+ const datePart = this.datePartOf(value);
162
+ const timePart = value.includes('T') ? value.split('T')[1] : null;
163
+ const v = this.view();
164
+ let label;
165
+ if (v === 'year') {
166
+ label = datePart;
167
+ }
168
+ else if (v === 'month') {
169
+ const [y, m] = datePart.split('-').map(Number);
170
+ const fmt = new Intl.DateTimeFormat(this.locale(), {
171
+ month: 'long',
172
+ year: 'numeric',
173
+ });
174
+ label = fmt.format(new Date(y, m - 1, 1));
175
+ label = label.charAt(0).toUpperCase() + label.slice(1);
176
+ }
177
+ else {
178
+ const fmt = new Intl.DateTimeFormat(this.locale(), {
179
+ dateStyle: 'long',
180
+ });
181
+ label = fmt.format(new Date(datePart + 'T12:00:00'));
182
+ }
183
+ if (this.showTime() && timePart) {
184
+ label += ' ' + this.formatTimeFromValue(timePart);
185
+ }
186
+ return label;
187
+ }, ...(ngDevMode ? [{ debugName: "displayLabel" }] : /* istanbul ignore next */ []));
188
+ /** Localized short month names for the year view. */
189
+ this.monthNames = computed(() => {
190
+ const fmt = new Intl.DateTimeFormat(this.locale(), { month: 'short' });
191
+ return Array.from({ length: 12 }, (_, i) => {
192
+ const name = fmt.format(new Date(2000, i, 1)).replace('.', '');
193
+ return name.charAt(0).toUpperCase() + name.slice(1);
194
+ });
195
+ }, ...(ngDevMode ? [{ debugName: "monthNames" }] : /* istanbul ignore next */ []));
196
+ /** Localized short weekday names for the calendar header. */
197
+ this.dayNames = computed(() => {
198
+ const fmt = new Intl.DateTimeFormat(this.locale(), { weekday: 'short' });
199
+ return Array.from({ length: 7 }, (_, i) => {
200
+ const name = fmt.format(new Date(2024, 0, i + 1));
201
+ return name.charAt(0).toUpperCase() + name.slice(1, 3);
202
+ });
203
+ }, ...(ngDevMode ? [{ debugName: "dayNames" }] : /* istanbul ignore next */ []));
204
+ /** Number of days in the currently viewed month. */
205
+ this.daysInMonth = computed(() => {
206
+ return new Date(this.viewYear(), this.viewMonth() + 1, 0).getDate();
207
+ }, ...(ngDevMode ? [{ debugName: "daysInMonth" }] : /* istanbul ignore next */ []));
208
+ /** Weekday index (0=Sunday) of the first day of the viewed month. */
209
+ this.firstDayOfMonth = computed(() => {
210
+ return new Date(this.viewYear(), this.viewMonth(), 1).getDay();
211
+ }, ...(ngDevMode ? [{ debugName: "firstDayOfMonth" }] : /* istanbul ignore next */ []));
212
+ /** Calendar grid: nulls for leading blanks then the days of the month. */
213
+ this.calendarDays = computed(() => {
214
+ const totalDays = this.daysInMonth();
215
+ const firstDay = this.firstDayOfMonth();
216
+ const days = [];
217
+ for (let i = 0; i < firstDay; i++)
218
+ days.push(null);
219
+ for (let i = 1; i <= totalDays; i++)
220
+ days.push(i);
221
+ return days;
222
+ }, ...(ngDevMode ? [{ debugName: "calendarDays" }] : /* istanbul ignore next */ []));
223
+ /** Whether the prev navigation button is allowed by `minValue`. */
224
+ this.canGoPrev = computed(() => {
225
+ const v = this.view();
226
+ const min = this.minValue();
227
+ if (v === 'date') {
228
+ if (!min)
229
+ return true;
230
+ const prev = new Date(this.viewYear(), this.viewMonth() - 1, 1);
231
+ return prev >= new Date(min + 'T12:00:00');
232
+ }
233
+ if (v === 'month') {
234
+ const minY = min ? Number(min.split('-')[0]) : undefined;
235
+ return minY === undefined || this.viewYear() > minY;
236
+ }
237
+ const minY = min ? Number(min) : undefined;
238
+ return minY === undefined || this.viewYear() > minY;
239
+ }, ...(ngDevMode ? [{ debugName: "canGoPrev" }] : /* istanbul ignore next */ []));
240
+ /** Whether the next navigation button is allowed by `maxValue`. */
241
+ this.canGoNext = computed(() => {
242
+ const v = this.view();
243
+ const max = this.maxValue();
244
+ if (v === 'date') {
245
+ if (!max)
246
+ return true;
247
+ const next = new Date(this.viewYear(), this.viewMonth() + 1, 1);
248
+ return next <= new Date(max + 'T12:00:00');
249
+ }
250
+ if (v === 'month') {
251
+ const maxY = max ? Number(max.split('-')[0]) : undefined;
252
+ return maxY === undefined || this.viewYear() < maxY;
253
+ }
254
+ const maxY = max ? Number(max) : undefined;
255
+ return maxY === undefined || this.viewYear() < maxY;
256
+ }, ...(ngDevMode ? [{ debugName: "canGoNext" }] : /* istanbul ignore next */ []));
257
+ /** Year of the displayed date, if any. */
258
+ this.selectedYear = computed(() => {
259
+ const value = this.displayedDate();
260
+ return value ? Number(value.split('-')[0]) : null;
261
+ }, ...(ngDevMode ? [{ debugName: "selectedYear" }] : /* istanbul ignore next */ []));
262
+ /** Month index (0-based) of the displayed date, if any. */
263
+ this.selectedMonth = computed(() => {
264
+ const value = this.displayedDate();
265
+ return value ? Number(value.split('-')[1]) - 1 : null;
266
+ }, ...(ngDevMode ? [{ debugName: "selectedMonth" }] : /* istanbul ignore next */ []));
267
+ /** Day of the displayed date, if any (date view only). */
268
+ this.selectedDay = computed(() => {
269
+ const value = this.displayedDate();
270
+ return value && this.view() === 'date' ? Number(value.split('-')[2]) : null;
271
+ }, ...(ngDevMode ? [{ debugName: "selectedDay" }] : /* istanbul ignore next */ []));
272
+ /** 12 years of the viewed decade for the year view. */
273
+ this.decadeYears = computed(() => {
274
+ const start = this.viewDecade();
275
+ return Array.from({ length: 12 }, (_, i) => start + i);
276
+ }, ...(ngDevMode ? [{ debugName: "decadeYears" }] : /* istanbul ignore next */ []));
277
+ /** (Unused legacy placeholder) plain year list. */
278
+ this.years = [];
279
+ // Attach error-state subscriptions to the validated control once found.
280
+ effect(() => {
281
+ const ctrl = this.actualControl();
282
+ if (!ctrl) {
283
+ this.subscribed.set(false);
284
+ return;
285
+ }
286
+ if (this.subscribed())
287
+ return;
288
+ if (ctrl.invalid && ctrl.touched)
289
+ this.hadError.set(true);
290
+ ctrl.statusChanges
291
+ .pipe(takeUntilDestroyed(this.destroyRef))
292
+ .subscribe(() => {
293
+ this.errorTracker.update((v) => v + 1);
294
+ if (ctrl.invalid && ctrl.touched)
295
+ this.hadError.set(true);
296
+ });
297
+ this.subscribed.set(true);
298
+ });
299
+ // One-way sync from the reactive control to the internal value signal.
300
+ effect(() => {
301
+ const ctrl = this.actualControl();
302
+ if (!ctrl)
303
+ return;
304
+ if (!this.valueSubscribed()) {
305
+ this.writeValue(ctrl.value);
306
+ ctrl.valueChanges
307
+ .pipe(takeUntilDestroyed(this.destroyRef))
308
+ .subscribe((value) => {
309
+ this.writeValue(value);
310
+ });
311
+ this.valueSubscribed.set(true);
312
+ }
313
+ });
314
+ }
315
+ /** Closes the dropdown when clicking outside the component. */
316
+ onDocumentClick(event) {
317
+ if (!this.isOpen())
318
+ return;
319
+ if (!this.elementRef.nativeElement.contains(event.target)) {
320
+ this.close();
321
+ }
322
+ }
323
+ /** Closes the dropdown on Escape. */
324
+ onEscape() {
325
+ this.close();
326
+ }
327
+ /** Opens/closes the dropdown, syncing the calendar to the current value. */
328
+ toggle() {
329
+ if (this.isDisabled())
330
+ return;
331
+ if (this.isOpen()) {
332
+ this.close();
333
+ }
334
+ else {
335
+ this.syncViewToValue();
336
+ this.isOpen.set(true);
337
+ }
338
+ }
339
+ /** Aligns the calendar view signals with the current value (or today). */
340
+ syncViewToValue() {
341
+ const val = this._value();
342
+ const v = this.view();
343
+ if (!val) {
344
+ const now = new Date();
345
+ this.viewYear.set(now.getFullYear());
346
+ this.viewMonth.set(now.getMonth());
347
+ this.viewDecade.set(Math.floor(now.getFullYear() / 10) * 10);
348
+ this.viewHour.set(now.getHours());
349
+ this.viewMinute.set(now.getMinutes());
350
+ this.viewSecond.set(now.getSeconds());
351
+ this.pendingDate.set(this.toISODate(now));
352
+ return;
353
+ }
354
+ const datePart = this.datePartOf(val);
355
+ const timePart = val.includes('T') ? val.split('T')[1] : undefined;
356
+ if (v === 'year') {
357
+ this.viewYear.set(Number(datePart));
358
+ }
359
+ else if (v === 'month') {
360
+ this.viewYear.set(Number(datePart.split('-')[0]));
361
+ }
362
+ else {
363
+ const d = new Date(datePart + 'T12:00:00');
364
+ this.viewYear.set(d.getFullYear());
365
+ this.viewMonth.set(d.getMonth());
366
+ }
367
+ const time = timePart ?? (this.timeOnly() ? val : undefined);
368
+ if (time) {
369
+ const [h, m, ss] = time.split(':').map(Number);
370
+ this.viewHour.set(h || 0);
371
+ this.viewMinute.set(m || 0);
372
+ this.viewSecond.set(ss || 0);
373
+ if (this.hourFormat() === '12') {
374
+ this.viewMeridiem.set(h >= 12 ? 'PM' : 'AM');
375
+ }
376
+ }
377
+ this.pendingDate.set(datePart);
378
+ }
379
+ /** Closes the dropdown, marks the control as touched and refreshes errors. */
380
+ close() {
381
+ if (!this.isOpen())
382
+ return;
383
+ this.isOpen.set(false);
384
+ this.onTouched();
385
+ const ctrl = this.actualControl();
386
+ if (ctrl)
387
+ ctrl.markAsTouched();
388
+ this.errorTracker.update((v) => v + 1);
389
+ if (ctrl?.invalid && ctrl?.touched)
390
+ this.hadError.set(true);
391
+ }
392
+ /** Navigates the calendar to the previous year/month/decade. */
393
+ prev() {
394
+ const v = this.view();
395
+ if (v === 'year') {
396
+ this.viewDecade.update((d) => d - 12);
397
+ this.viewYear.update((y) => y - 12);
398
+ }
399
+ else if (v === 'month') {
400
+ this.viewYear.update((y) => y - 1);
401
+ }
402
+ else {
403
+ this.viewMonth.update((m) => {
404
+ if (m === 0) {
405
+ this.viewYear.update((y) => y - 1);
406
+ return 11;
407
+ }
408
+ return m - 1;
409
+ });
410
+ }
411
+ }
412
+ /** Navigates the calendar to the next year/month/decade. */
413
+ next() {
414
+ const v = this.view();
415
+ if (v === 'year') {
416
+ this.viewDecade.update((d) => d + 12);
417
+ this.viewYear.update((y) => y + 12);
418
+ }
419
+ else if (v === 'month') {
420
+ this.viewYear.update((y) => y + 1);
421
+ }
422
+ else {
423
+ this.viewMonth.update((m) => {
424
+ if (m === 11) {
425
+ this.viewYear.update((y) => y + 1);
426
+ return 0;
427
+ }
428
+ return m + 1;
429
+ });
430
+ }
431
+ }
432
+ /** Selects a day of the month (pending if time is shown). */
433
+ selectDay(day) {
434
+ const y = this.viewYear();
435
+ const m = String(this.viewMonth() + 1).padStart(2, '0');
436
+ const d = String(day).padStart(2, '0');
437
+ const value = `${y}-${m}-${d}`;
438
+ if (this.showTime()) {
439
+ this.pendingDate.set(value);
440
+ return;
441
+ }
442
+ this.setValue(value);
443
+ }
444
+ /** Selects a month of the year (pending if time is shown). */
445
+ selectMonth(monthIndex) {
446
+ const value = `${this.viewYear()}-${String(monthIndex + 1).padStart(2, '0')}`;
447
+ if (this.showTime()) {
448
+ this.pendingDate.set(value);
449
+ return;
450
+ }
451
+ this.setValue(value);
452
+ }
453
+ /** Selects a year (pending if time is shown). */
454
+ selectYear(year) {
455
+ const value = String(year);
456
+ if (this.showTime()) {
457
+ this.pendingDate.set(value);
458
+ return;
459
+ }
460
+ this.setValue(value);
461
+ }
462
+ /** Commits a value, updates the component, the control and the outputs. */
463
+ setValue(value, close = true) {
464
+ this._value.set(value);
465
+ this.onChange(value);
466
+ const ctrl = this.actualControl();
467
+ if (ctrl) {
468
+ ctrl.setValue(value);
469
+ ctrl.markAsDirty();
470
+ }
471
+ if (close)
472
+ this.close();
473
+ }
474
+ /** Confirms the selected time and commits the combined date+time value. */
475
+ confirmTime() {
476
+ const h = this.viewHour();
477
+ const m = this.viewMinute();
478
+ const s = this.viewSecond();
479
+ let time = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
480
+ if (this.showSeconds())
481
+ time += `:${String(s).padStart(2, '0')}`;
482
+ if (this.timeOnly()) {
483
+ this.setValue(time);
484
+ return;
485
+ }
486
+ const datePart = this.pendingDate() ??
487
+ `${this.viewYear()}-${String(this.viewMonth() + 1).padStart(2, '0')}-01`;
488
+ this.setValue(`${datePart}T${time}`);
489
+ }
490
+ /** Reads the hour selected in the time selector. */
491
+ onHourChange(event) {
492
+ this.viewHour.set(Number(event.target.value));
493
+ }
494
+ /** Reads the minute selected in the time selector. */
495
+ onMinuteChange(event) {
496
+ this.viewMinute.set(Number(event.target.value));
497
+ }
498
+ /** Reads the second selected in the time selector. */
499
+ onSecondChange(event) {
500
+ this.viewSecond.set(Number(event.target.value));
501
+ }
502
+ /** Current meridiem ("AM"/"PM") for the 12-hour format. */
503
+ meridiem() {
504
+ return this.viewMeridiem();
505
+ }
506
+ /** Reads the meridiem selected in the time selector. */
507
+ onMeridiemChange(event) {
508
+ this.viewMeridiem.set(event.target.value);
509
+ }
510
+ /** Splits the date part from a full ISO "date" or "dateTtime" value. */
511
+ datePartOf(value) {
512
+ const i = value.indexOf('T');
513
+ return i >= 0 ? value.slice(0, i) : value;
514
+ }
515
+ /** Formats a raw time string ("HH:mm[:ss]") using the configured format. */
516
+ formatTimeFromValue(time) {
517
+ const [h, m, ss] = time.split(':').map(Number);
518
+ return this.formatTime(h || 0, m || 0, ss || 0);
519
+ }
520
+ /** Formats hour/minute/second according to hour format and seconds option. */
521
+ formatTime(h, m, s) {
522
+ if (this.hourFormat() === '12') {
523
+ const h12 = h % 12 || 12;
524
+ const suffix = h < 12 ? ' AM' : ' PM';
525
+ return `${String(h12).padStart(2, '0')}:${String(m).padStart(2, '0')}${suffix}`;
526
+ }
527
+ let label = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
528
+ if (this.showSeconds())
529
+ label += `:${String(s).padStart(2, '0')}`;
530
+ return label;
531
+ }
532
+ /** Formats a Date as a YYYY-MM-DD ISO string. */
533
+ toISODate(date) {
534
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
535
+ }
536
+ /** Whether the given day matches the currently selected date. */
537
+ isSelectedDay(day) {
538
+ return (this.selectedYear() === this.viewYear() &&
539
+ this.selectedMonth() === this.viewMonth() &&
540
+ this.selectedDay() === day);
541
+ }
542
+ /** Hours available in the time selector ('12' maps to 0..11 or 12..23). */
543
+ hoursRange() {
544
+ if (this.hourFormat() === '12') {
545
+ const oneToTwelve = Array.from({ length: 12 }, (_, i) => i + 1);
546
+ return this.viewMeridiem() === 'PM'
547
+ ? oneToTwelve.map((h) => (h === 12 ? 12 : h + 12))
548
+ : oneToTwelve.map((h) => (h === 12 ? 0 : h));
549
+ }
550
+ return Array.from({ length: 24 }, (_, i) => i);
551
+ }
552
+ /** Minutes available in the time selector, stepped by `minuteStep`. */
553
+ minutesRange() {
554
+ const step = this.minuteStep();
555
+ return Array.from({ length: Math.ceil(60 / step) }, (_, i) => i * step);
556
+ }
557
+ /** Seconds available in the time selector (0..59). */
558
+ secondsRange() {
559
+ return Array.from({ length: 60 }, (_, i) => i);
560
+ }
561
+ /** Formats an hour for display in the selector (12-hour aware). */
562
+ hourDisplay(hour) {
563
+ if (this.hourFormat() === '12') {
564
+ return String(hour % 12 || 12).padStart(2, '0');
565
+ }
566
+ return String(hour).padStart(2, '0');
567
+ }
568
+ /** Pads a minute value with a leading zero. */
569
+ minutesLabel(value) {
570
+ return String(value).padStart(2, '0');
571
+ }
572
+ /** Whether the given day is today's date. */
573
+ isToday(day) {
574
+ const now = new Date();
575
+ return (this.viewYear() === now.getFullYear() &&
576
+ this.viewMonth() === now.getMonth() &&
577
+ day === now.getDate());
578
+ }
579
+ /** Whether the given month matches the selected month and year. */
580
+ isSelectedMonth(monthIndex) {
581
+ return (this.selectedYear() === this.viewYear() &&
582
+ this.selectedMonth() === monthIndex);
583
+ }
584
+ isCurrentMonth(monthIndex) {
585
+ const now = new Date();
586
+ return (this.viewYear() === now.getFullYear() && monthIndex === now.getMonth());
587
+ }
588
+ isSelectedYear(year) {
589
+ return this.selectedYear() === year;
590
+ }
591
+ isCurrentYear(year) {
592
+ return new Date().getFullYear() === year;
593
+ }
594
+ writeValue(value) {
595
+ this._value.set(value || null);
596
+ }
597
+ registerOnChange(fn) {
598
+ this.onChange = fn;
599
+ }
600
+ registerOnTouched(fn) {
601
+ this.onTouched = fn;
602
+ }
603
+ setDisabledState(isDisabled) {
604
+ this._disabledState.set(isDisabled);
605
+ this.cdr.markForCheck();
606
+ }
607
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
608
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: DatePickerComponent, isStandalone: true, selector: "shk-date-picker", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: false, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, timeOnly: { classPropertyName: "timeOnly", publicName: "timeOnly", isSignal: true, isRequired: false, transformFunction: null }, showTime: { classPropertyName: "showTime", publicName: "showTime", isSignal: true, isRequired: false, transformFunction: null }, hourFormat: { classPropertyName: "hourFormat", publicName: "hourFormat", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, providers: [
609
+ {
610
+ provide: NG_VALUE_ACCESSOR,
611
+ useExisting: forwardRef(() => DatePickerComponent),
612
+ multi: true,
613
+ },
614
+ ], ngImport: i0, template: "<div class=\"relative mb-4\">\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n </div>\n }\n\n <div class=\"relative\">\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n class=\"flex items-center justify-between w-full px-3 py-2 text-left _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-lg transition-colors sm:text-sm focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:[color:var(--shk-surface-400)] dark:disabled:[color:var(--shk-surface-500)]\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': shouldShowError(),\n 'border-green-500 focus:border-green-500 focus:ring-green-500': wasFixedError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !shouldShowError() && !wasFixedError(),\n }\"\n >\n <span\n [ngClass]=\"value() ? '_shk-text-surface-700 dark:[color:var(--shk-surface-700)]' : '_shk-text-surface-400 dark:[color:var(--shk-surface-400)]'\"\n >\n {{ displayLabel() || placeholder() || label() }}\n </span>\n <svg\n class=\"w-4 h-4 _shk-text-surface-400 dark:[color:var(--shk-surface-400)] shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"></rect>\n <path d=\"M16 2v4M8 2v4M3 10h18\"></path>\n </svg>\n </button>\n\n @if (isOpen()) {\n <div class=\"fixed inset-0 z-40 bg-black/30\" (click)=\"close()\"></div>\n <div\n class=\"fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-72 p-4 bg-white dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-lg shadow-xl\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (!timeOnly() && view() === 'date') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ monthNames()[viewMonth()] }} {{ viewYear() }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0 mb-1\">\n @for (name of dayNames(); track $index) {\n <div class=\"text-center text-xs font-medium _shk-text-surface-400 dark:[color:var(--shk-surface-400)] py-1\">\n {{ name }}\n </div>\n }\n </div>\n\n <div class=\"grid grid-cols-7 gap-0\">\n @for (day of calendarDays(); track $index) {\n @if (day !== null) {\n <button\n type=\"button\"\n (click)=\"selectDay(day)\"\n class=\"py-1.5 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedDay(day),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isToday(day) && !isSelectedDay(day),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedDay(day) && !isToday(day),\n }\"\n >\n {{ day }}\n </button>\n } @else {\n <div></div>\n }\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'month') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">{{ viewYear() }}</span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (name of monthNames(); track $index) {\n <button\n type=\"button\"\n (click)=\"selectMonth($index)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedMonth($index),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentMonth($index) && !isSelectedMonth($index),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedMonth($index) && !isCurrentMonth($index),\n }\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'year') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ viewDecade() }} - {{ viewDecade() + 11 }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (year of decadeYears(); track year) {\n <button\n type=\"button\"\n (click)=\"selectYear(year)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedYear(year),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentYear(year) && !isSelectedYear(year),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedYear(year) && !isCurrentYear(year),\n }\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n\n @if (timeOnly() || showTime()) {\n <div class=\"mt-3 pt-3 _shk-border dark:[border-color:var(--shk-surface-200)]\">\n <div class=\"flex items-center justify-center gap-1\">\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewHour()\"\n (change)=\"onHourChange($event)\"\n aria-label=\"Hora\"\n >\n @for (h of hoursRange(); track h) {\n <option [value]=\"h\">{{ hourDisplay(h) }}</option>\n }\n </select>\n <span class=\"_shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewMinute()\"\n (change)=\"onMinuteChange($event)\"\n aria-label=\"Minutos\"\n >\n @for (minutes of minutesRange(); track minutes) {\n <option [value]=\"minutes\">{{ minutesLabel(minutes) }}</option>\n }\n </select>\n @if (showSeconds()) {\n <span class=\"text-sm _shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewSecond()\"\n (change)=\"onSecondChange($event)\"\n aria-label=\"Segundos\"\n >\n @for (seconds of secondsRange(); track seconds) {\n <option [value]=\"seconds\">{{ minutesLabel(seconds) }}</option>\n }\n </select>\n }\n @if (hourFormat() === '12') {\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"meridiem()\"\n (change)=\"onMeridiemChange($event)\"\n aria-label=\"AM/PM\"\n >\n <option value=\"AM\">AM</option>\n <option value=\"PM\">PM</option>\n </select>\n }\n </div>\n <button\n type=\"button\"\n (click)=\"confirmTime()\"\n class=\"mt-3 w-full py-2 text-sm _shk-bg-primary _shk-text-white rounded-lg hover:[background-color:var(--shk-primary-700)] transition-colors\"\n >\n {{ timeOnly() ? 'Confirmar' : 'Introducir hora' }}\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n @if (shouldShowError()) {\n <div class=\"mt-1 text-xs text-red-500 max-h-20 overflow-y-auto _shk-scrollbar\">\n <ul class=\"list-disc pl-4 space-y-1\">\n @for (error of getErrorMessages(); track error) {\n <li>{{ error }}</li>\n }\n </ul>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
615
+ }
616
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DatePickerComponent, decorators: [{
617
+ type: Component,
618
+ args: [{ selector: 'shk-date-picker', standalone: true, imports: [CommonModule, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
619
+ {
620
+ provide: NG_VALUE_ACCESSOR,
621
+ useExisting: forwardRef(() => DatePickerComponent),
622
+ multi: true,
623
+ },
624
+ ], template: "<div class=\"relative mb-4\">\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n </div>\n }\n\n <div class=\"relative\">\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n class=\"flex items-center justify-between w-full px-3 py-2 text-left _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-lg transition-colors sm:text-sm focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:[color:var(--shk-surface-400)] dark:disabled:[color:var(--shk-surface-500)]\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': shouldShowError(),\n 'border-green-500 focus:border-green-500 focus:ring-green-500': wasFixedError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !shouldShowError() && !wasFixedError(),\n }\"\n >\n <span\n [ngClass]=\"value() ? '_shk-text-surface-700 dark:[color:var(--shk-surface-700)]' : '_shk-text-surface-400 dark:[color:var(--shk-surface-400)]'\"\n >\n {{ displayLabel() || placeholder() || label() }}\n </span>\n <svg\n class=\"w-4 h-4 _shk-text-surface-400 dark:[color:var(--shk-surface-400)] shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"></rect>\n <path d=\"M16 2v4M8 2v4M3 10h18\"></path>\n </svg>\n </button>\n\n @if (isOpen()) {\n <div class=\"fixed inset-0 z-40 bg-black/30\" (click)=\"close()\"></div>\n <div\n class=\"fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-72 p-4 bg-white dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-lg shadow-xl\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (!timeOnly() && view() === 'date') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ monthNames()[viewMonth()] }} {{ viewYear() }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0 mb-1\">\n @for (name of dayNames(); track $index) {\n <div class=\"text-center text-xs font-medium _shk-text-surface-400 dark:[color:var(--shk-surface-400)] py-1\">\n {{ name }}\n </div>\n }\n </div>\n\n <div class=\"grid grid-cols-7 gap-0\">\n @for (day of calendarDays(); track $index) {\n @if (day !== null) {\n <button\n type=\"button\"\n (click)=\"selectDay(day)\"\n class=\"py-1.5 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedDay(day),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isToday(day) && !isSelectedDay(day),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedDay(day) && !isToday(day),\n }\"\n >\n {{ day }}\n </button>\n } @else {\n <div></div>\n }\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'month') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">{{ viewYear() }}</span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (name of monthNames(); track $index) {\n <button\n type=\"button\"\n (click)=\"selectMonth($index)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedMonth($index),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentMonth($index) && !isSelectedMonth($index),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedMonth($index) && !isCurrentMonth($index),\n }\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'year') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ viewDecade() }} - {{ viewDecade() + 11 }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (year of decadeYears(); track year) {\n <button\n type=\"button\"\n (click)=\"selectYear(year)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedYear(year),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentYear(year) && !isSelectedYear(year),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedYear(year) && !isCurrentYear(year),\n }\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n\n @if (timeOnly() || showTime()) {\n <div class=\"mt-3 pt-3 _shk-border dark:[border-color:var(--shk-surface-200)]\">\n <div class=\"flex items-center justify-center gap-1\">\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewHour()\"\n (change)=\"onHourChange($event)\"\n aria-label=\"Hora\"\n >\n @for (h of hoursRange(); track h) {\n <option [value]=\"h\">{{ hourDisplay(h) }}</option>\n }\n </select>\n <span class=\"_shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewMinute()\"\n (change)=\"onMinuteChange($event)\"\n aria-label=\"Minutos\"\n >\n @for (minutes of minutesRange(); track minutes) {\n <option [value]=\"minutes\">{{ minutesLabel(minutes) }}</option>\n }\n </select>\n @if (showSeconds()) {\n <span class=\"text-sm _shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewSecond()\"\n (change)=\"onSecondChange($event)\"\n aria-label=\"Segundos\"\n >\n @for (seconds of secondsRange(); track seconds) {\n <option [value]=\"seconds\">{{ minutesLabel(seconds) }}</option>\n }\n </select>\n }\n @if (hourFormat() === '12') {\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"meridiem()\"\n (change)=\"onMeridiemChange($event)\"\n aria-label=\"AM/PM\"\n >\n <option value=\"AM\">AM</option>\n <option value=\"PM\">PM</option>\n </select>\n }\n </div>\n <button\n type=\"button\"\n (click)=\"confirmTime()\"\n class=\"mt-3 w-full py-2 text-sm _shk-bg-primary _shk-text-white rounded-lg hover:[background-color:var(--shk-primary-700)] transition-colors\"\n >\n {{ timeOnly() ? 'Confirmar' : 'Introducir hora' }}\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n @if (shouldShowError()) {\n <div class=\"mt-1 text-xs text-red-500 max-h-20 overflow-y-auto _shk-scrollbar\">\n <ul class=\"list-disc pl-4 space-y-1\">\n @for (error of getErrorMessages(); track error) {\n <li>{{ error }}</li>\n }\n </ul>\n </div>\n }\n</div>\n" }]
625
+ }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], minValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "minValue", required: false }] }], maxValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxValue", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], timeOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeOnly", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], hourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "hourFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], onDocumentClick: [{
626
+ type: HostListener,
627
+ args: ['document:click', ['$event']]
628
+ }], onEscape: [{
629
+ type: HostListener,
630
+ args: ['document:keydown.escape']
631
+ }] } });
632
+
633
+ /**
634
+ * Generated bundle index. Do not edit.
635
+ */
636
+
637
+ export { DatePickerComponent };
638
+ //# sourceMappingURL=shirkasoft-ui-components-date-picker.mjs.map