ngx-datex 1.0.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.
@@ -0,0 +1,3562 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, inject, PLATFORM_ID, ViewContainerRef, input, computed, output, signal, afterNextRender, forwardRef, ViewChild, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { isPlatformBrowser, NgClass } from '@angular/common';
4
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
+ import { MatFormFieldModule } from '@angular/material/form-field';
6
+ import * as i2 from '@angular/material/input';
7
+ import { MatInputModule } from '@angular/material/input';
8
+ import * as i1 from '@angular/material/icon';
9
+ import { MatIconModule } from '@angular/material/icon';
10
+ import { MatButtonModule } from '@angular/material/button';
11
+ import * as i3 from '@angular/material/checkbox';
12
+ import { MatCheckboxModule } from '@angular/material/checkbox';
13
+ import { addDay, addMonth, sameDay, sameYear, format, parse, isBefore } from '@formkit/tempo';
14
+ import { Overlay, OverlayConfig } from '@angular/cdk/overlay';
15
+ import { TemplatePortal } from '@angular/cdk/portal';
16
+
17
+ /**
18
+ * @fileoverview Date utility functions for NgxDatex component.
19
+ * Provides date manipulation, formatting, and validation utilities.
20
+ * Built on top of @formkit/tempo for reliable date operations.
21
+ */
22
+ /**
23
+ * Adds the specified number of days to a date.
24
+ *
25
+ * @param date - The base date
26
+ * @param days - Number of days to add (can be negative)
27
+ * @returns New date with days added
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const tomorrow = addDays(new Date(), 1);
32
+ * const lastWeek = addDays(new Date(), -7);
33
+ * ```
34
+ */
35
+ function addDays(date, days) {
36
+ return addDay(date, days);
37
+ }
38
+ /**
39
+ * Adds the specified number of months to a date.
40
+ *
41
+ * @param date - The base date
42
+ * @param months - Number of months to add (can be negative)
43
+ * @returns New date with months added
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const nextMonth = addMonths(new Date(), 1);
48
+ * const lastYear = addMonths(new Date(), -12);
49
+ * ```
50
+ */
51
+ function addMonths(date, months) {
52
+ return addMonth(date, months);
53
+ }
54
+ /**
55
+ * Returns a new date set to the start of the day (00:00:00.000).
56
+ *
57
+ * @param date - The input date
58
+ * @returns New date at start of day
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const today = startOfDay(new Date()); // Today at 00:00:00
63
+ * ```
64
+ */
65
+ function startOfDay(date) {
66
+ const result = new Date(date);
67
+ result.setHours(0, 0, 0, 0);
68
+ return result;
69
+ }
70
+ /**
71
+ * Returns a new date set to the end of the day (23:59:59.999).
72
+ *
73
+ * @param date - The input date
74
+ * @returns New date at end of day
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const endToday = endOfDay(new Date()); // Today at 23:59:59.999
79
+ * ```
80
+ */
81
+ function endOfDay(date) {
82
+ const result = new Date(date);
83
+ result.setHours(23, 59, 59, 999);
84
+ return result;
85
+ }
86
+ /**
87
+ * Returns a new date set to the first day of the month.
88
+ *
89
+ * @param date - The input date
90
+ * @returns New date at start of month
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const monthStart = startOfMonth(new Date()); // 1st day of current month
95
+ * ```
96
+ */
97
+ function startOfMonth(date) {
98
+ return new Date(date.getFullYear(), date.getMonth(), 1);
99
+ }
100
+ /**
101
+ * Returns a new date set to the start of the week.
102
+ *
103
+ * @param date - The input date
104
+ * @param firstDay - First day of week (0 = Sunday, 1 = Monday, etc.)
105
+ * @returns New date at start of week
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * const mondayStart = startOfWeek(new Date(), 1); // Start of week (Monday)
110
+ * const sundayStart = startOfWeek(new Date(), 0); // Start of week (Sunday)
111
+ * ```
112
+ */
113
+ function startOfWeek(date, firstDay = 1) {
114
+ const day = date.getDay();
115
+ const daysBack = (day - firstDay + 7) % 7;
116
+ const result = new Date(date);
117
+ result.setDate(date.getDate() - daysBack);
118
+ return result;
119
+ }
120
+ /**
121
+ * Checks if two dates represent the same day.
122
+ *
123
+ * @param date1 - First date to compare
124
+ * @param date2 - Second date to compare
125
+ * @returns True if dates are on the same day
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * const today = new Date();
130
+ * const todayEvening = new Date();
131
+ * todayEvening.setHours(23, 59, 59);
132
+ *
133
+ * isSameDay(today, todayEvening); // true
134
+ * ```
135
+ */
136
+ function isSameDay(date1, date2) {
137
+ return sameDay(date1, date2);
138
+ }
139
+ /**
140
+ * Checks if two dates are in the same month and year.
141
+ *
142
+ * @param date1 - First date to compare
143
+ * @param date2 - Second date to compare
144
+ * @returns True if dates are in the same month
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * const jan1 = new Date(2024, 0, 1);
149
+ * const jan31 = new Date(2024, 0, 31);
150
+ *
151
+ * isSameMonth(jan1, jan31); // true
152
+ * ```
153
+ */
154
+ function isSameMonth(date1, date2) {
155
+ return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth();
156
+ }
157
+ /**
158
+ * Checks if two dates are in the same year.
159
+ *
160
+ * @param date1 - First date to compare
161
+ * @param date2 - Second date to compare
162
+ * @returns True if dates are in the same year
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const jan2024 = new Date(2024, 0, 1);
167
+ * const dec2024 = new Date(2024, 11, 31);
168
+ *
169
+ * isSameYear(jan2024, dec2024); // true
170
+ * ```
171
+ */
172
+ function isSameYear(date1, date2) {
173
+ return sameYear(date1, date2);
174
+ }
175
+ function formatDateValue(date, formatStr) {
176
+ try {
177
+ return format(date, formatStr);
178
+ }
179
+ catch {
180
+ const year = date.getFullYear();
181
+ const month = String(date.getMonth() + 1).padStart(2, '0');
182
+ const day = String(date.getDate()).padStart(2, '0');
183
+ const hours24 = date.getHours();
184
+ const hours12 = hours24 === 0 ? 12 : hours24 > 12 ? hours24 - 12 : hours24;
185
+ const minutes = String(date.getMinutes()).padStart(2, '0');
186
+ const seconds = String(date.getSeconds()).padStart(2, '0');
187
+ const ampm = hours24 >= 12 ? 'PM' : 'AM';
188
+ return formatStr
189
+ .replace(/YYYY/g, String(year))
190
+ .replace(/MM/g, month)
191
+ .replace(/DD/g, day)
192
+ .replace(/HH/g, String(hours24).padStart(2, '0'))
193
+ .replace(/hh/g, String(hours12).padStart(2, '0'))
194
+ .replace(/mm/g, minutes)
195
+ .replace(/ss/g, seconds)
196
+ .replace(/A/g, ampm);
197
+ }
198
+ }
199
+ function parseDateValue(dateStr, formatStr) {
200
+ try {
201
+ const parsed = parse(dateStr, formatStr);
202
+ if (isValidDate(parsed)) {
203
+ return parsed;
204
+ }
205
+ }
206
+ catch {
207
+ // Fallback to manual parsing
208
+ }
209
+ if (formatStr === 'YYYY-MM-DD') {
210
+ const parts = dateStr.split('-');
211
+ if (parts.length === 3) {
212
+ const year = parseInt(parts[0], 10);
213
+ const month = parseInt(parts[1], 10) - 1;
214
+ const day = parseInt(parts[2], 10);
215
+ return new Date(year, month, day);
216
+ }
217
+ return new Date(dateStr);
218
+ }
219
+ if (formatStr === 'DD/MM/YYYY' || formatStr.startsWith('DD/MM/YYYY')) {
220
+ const parts = dateStr.split(' ');
221
+ const datePart = parts[0];
222
+ const timePart = parts[1];
223
+ const dateComponents = datePart.split('/');
224
+ if (dateComponents.length === 3) {
225
+ const day = parseInt(dateComponents[0], 10);
226
+ const month = parseInt(dateComponents[1], 10) - 1;
227
+ const year = parseInt(dateComponents[2], 10);
228
+ const date = new Date(year, month, day);
229
+ if (timePart) {
230
+ const timeComponents = timePart.split(':');
231
+ if (timeComponents.length >= 2) {
232
+ let hours = parseInt(timeComponents[0], 10);
233
+ const minutes = parseInt(timeComponents[1], 10);
234
+ if (timePart.includes('PM') && hours !== 12) {
235
+ hours += 12;
236
+ }
237
+ else if (timePart.includes('AM') && hours === 12) {
238
+ hours = 0;
239
+ }
240
+ date.setHours(hours, minutes, 0, 0);
241
+ }
242
+ }
243
+ return date;
244
+ }
245
+ }
246
+ return new Date(dateStr);
247
+ }
248
+ function isSameDate(date1, date2, unit = 'day') {
249
+ if (!date1 || !date2)
250
+ return false;
251
+ switch (unit) {
252
+ case 'day':
253
+ return isSameDay(date1, date2);
254
+ case 'month':
255
+ return isSameMonth(date1, date2);
256
+ case 'year':
257
+ return isSameYear(date1, date2);
258
+ default:
259
+ return false;
260
+ }
261
+ }
262
+ function isValidDate(date) {
263
+ return date instanceof Date && !isNaN(date.getTime());
264
+ }
265
+ function isBeforeDate(date1, date2) {
266
+ return isBefore(date1, date2);
267
+ }
268
+ function getStartOfMonth(date) {
269
+ return startOfMonth(date);
270
+ }
271
+ function getStartOfWeek(date, firstDay = 1) {
272
+ return startOfWeek(date, firstDay);
273
+ }
274
+ function isMobileDevice() {
275
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
276
+ }
277
+
278
+ /**
279
+ * @fileoverview Default configurations for NgxDatex component.
280
+ *
281
+ * This file contains predefined locales, themes, and date ranges
282
+ * that can be used out-of-the-box or as starting points for customization.
283
+ */
284
+ /**
285
+ * Default Spanish locale configuration.
286
+ *
287
+ * Provides Spanish language support with DD/MM/YYYY format,
288
+ * Spanish day/month names, and Monday as the first day of the week.
289
+ *
290
+ * @example
291
+ * ```typescript
292
+ * <ngx-datex [locale]="SPANISH_LOCALE"></ngx-datex>
293
+ * ```
294
+ */
295
+ const SPANISH_LOCALE = {
296
+ direction: 'ltr',
297
+ format: 'DD/MM/YYYY',
298
+ separator: ' - ',
299
+ applyLabel: 'Aplicar',
300
+ cancelLabel: 'Cancelar',
301
+ weekLabel: 'S',
302
+ customRangeLabel: 'Personalizado',
303
+ daysOfWeek: ['Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa', 'Do'],
304
+ monthNames: [
305
+ 'Enero',
306
+ 'Febrero',
307
+ 'Marzo',
308
+ 'Abril',
309
+ 'Mayo',
310
+ 'Junio',
311
+ 'Julio',
312
+ 'Agosto',
313
+ 'Septiembre',
314
+ 'Octubre',
315
+ 'Noviembre',
316
+ 'Diciembre',
317
+ ],
318
+ firstDay: 1, // Lunes como primer día
319
+ };
320
+ /**
321
+ * Default Material Design light theme.
322
+ *
323
+ * A clean, modern theme following Material Design principles
324
+ * with blue primary colors and proper contrast ratios.
325
+ *
326
+ * @example
327
+ * ```typescript
328
+ * <ngx-datex [theme]="MATERIAL_LIGHT_THEME"></ngx-datex>
329
+ * ```
330
+ */
331
+ const MATERIAL_LIGHT_THEME = {
332
+ name: 'material-light',
333
+ colors: {
334
+ primary: '#1976d2',
335
+ secondary: '#424242',
336
+ accent: '#82b1ff',
337
+ background: '#fafafa',
338
+ surface: '#ffffff',
339
+ text: '#212121',
340
+ textSecondary: '#757575',
341
+ border: '#e0e0e0',
342
+ hover: '#f5f5f5',
343
+ selected: '#1976d2',
344
+ selectedText: '#ffffff',
345
+ disabled: '#bdbdbd',
346
+ error: '#f44336',
347
+ success: '#4caf50',
348
+ warning: '#ff9800',
349
+ },
350
+ typography: {
351
+ fontFamily: 'Roboto, sans-serif',
352
+ fontSize: '14px',
353
+ fontWeight: '400',
354
+ lineHeight: '1.5',
355
+ },
356
+ spacing: {
357
+ xs: '4px',
358
+ sm: '8px',
359
+ md: '16px',
360
+ lg: '24px',
361
+ xl: '32px',
362
+ },
363
+ borderRadius: {
364
+ sm: '4px',
365
+ md: '8px',
366
+ lg: '12px',
367
+ },
368
+ shadows: {
369
+ sm: '0 1px 3px rgba(0,0,0,0.12)',
370
+ md: '0 4px 6px rgba(0,0,0,0.16)',
371
+ lg: '0 10px 20px rgba(0,0,0,0.19)',
372
+ },
373
+ };
374
+ /**
375
+ * Default predefined date ranges in Spanish.
376
+ *
377
+ * Provides commonly used date ranges like "Today", "Last 7 days", etc.
378
+ * All ranges are calculated dynamically based on the current date.
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * <ngx-datex [ranges]="DEFAULT_RANGES"></ngx-datex>
383
+ *
384
+ * // Or customize with your own ranges
385
+ * const customRanges = {
386
+ * ...DEFAULT_RANGES,
387
+ * 'Custom Range': [startDate, endDate]
388
+ * };
389
+ * ```
390
+ */
391
+ const DEFAULT_RANGES = {
392
+ Hoy: [startOfDay(new Date()), endOfDay(new Date())],
393
+ Ayer: [
394
+ (() => {
395
+ const yesterday = new Date();
396
+ yesterday.setDate(yesterday.getDate() - 1);
397
+ return startOfDay(yesterday);
398
+ })(),
399
+ (() => {
400
+ const yesterday = new Date();
401
+ yesterday.setDate(yesterday.getDate() - 1);
402
+ return endOfDay(yesterday);
403
+ })(),
404
+ ],
405
+ 'Últimos 5 días': [
406
+ (() => {
407
+ const date = new Date();
408
+ date.setDate(date.getDate() - 4);
409
+ return startOfDay(date);
410
+ })(),
411
+ endOfDay(new Date()),
412
+ ],
413
+ 'Últimos 7 días': [
414
+ (() => {
415
+ const date = new Date();
416
+ date.setDate(date.getDate() - 6);
417
+ return startOfDay(date);
418
+ })(),
419
+ endOfDay(new Date()),
420
+ ],
421
+ 'Últimos 10 días': [
422
+ (() => {
423
+ const date = new Date();
424
+ date.setDate(date.getDate() - 9);
425
+ return startOfDay(date);
426
+ })(),
427
+ endOfDay(new Date()),
428
+ ],
429
+ 'Últimos 15 días': [
430
+ (() => {
431
+ const date = new Date();
432
+ date.setDate(date.getDate() - 14);
433
+ return startOfDay(date);
434
+ })(),
435
+ endOfDay(new Date()),
436
+ ],
437
+ 'Últimos 30 días': [
438
+ (() => {
439
+ const date = new Date();
440
+ date.setDate(date.getDate() - 29);
441
+ return startOfDay(date);
442
+ })(),
443
+ endOfDay(new Date()),
444
+ ],
445
+ 'Esta semana': [
446
+ (() => {
447
+ const today = new Date();
448
+ const dayOfWeek = today.getDay();
449
+ const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // Monday as first day
450
+ const monday = new Date(today);
451
+ monday.setDate(today.getDate() + mondayOffset);
452
+ return startOfDay(monday);
453
+ })(),
454
+ endOfDay(new Date()),
455
+ ],
456
+ 'Este mes': [
457
+ startOfDay(new Date(new Date().getFullYear(), new Date().getMonth(), 1)),
458
+ (() => {
459
+ const lastDayOfCurrentMonth = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0);
460
+ return endOfDay(lastDayOfCurrentMonth);
461
+ })(),
462
+ ],
463
+ 'El mes pasado': [
464
+ startOfDay(new Date(new Date().getFullYear(), new Date().getMonth() - 1, 1)),
465
+ (() => {
466
+ const lastDayOfPrevMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 0);
467
+ return endOfDay(lastDayOfPrevMonth);
468
+ })(),
469
+ ],
470
+ };
471
+
472
+ /**
473
+ * Core service for NgxDatex component operations.
474
+ *
475
+ * Provides essential functionality for:
476
+ * - Configuration management
477
+ * - Locale handling
478
+ * - Date formatting
479
+ * - Calendar matrix generation
480
+ * - Date validation
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * // Inject the service
485
+ * constructor(private datexService: NgxDatexService) {}
486
+ *
487
+ * // Update configuration
488
+ * this.datexService.updateConfig({
489
+ * dateFormat: 'DD/MM/YYYY',
490
+ * firstDayOfWeek: 1
491
+ * });
492
+ *
493
+ * // Format a date range
494
+ * const formatted = this.datexService.formatDateValue(dateRange, 'DD/MM/YYYY');
495
+ * ```
496
+ */
497
+ class NgxDatexService {
498
+ config = {};
499
+ locale = SPANISH_LOCALE;
500
+ /**
501
+ * Updates the component configuration.
502
+ *
503
+ * @param config - Partial configuration object to merge with existing config
504
+ *
505
+ * @example
506
+ * ```typescript
507
+ * this.datexService.updateConfig({
508
+ * dateFormat: 'YYYY-MM-DD',
509
+ * firstDayOfWeek: 0, // Sunday
510
+ * businessDaysOnly: true
511
+ * });
512
+ * ```
513
+ */
514
+ updateConfig(config) {
515
+ this.config = { ...this.config, ...config };
516
+ }
517
+ /**
518
+ * Sets the locale configuration for internationalization.
519
+ *
520
+ * @param locale - Locale configuration object
521
+ *
522
+ * @example
523
+ * ```typescript
524
+ * this.datexService.setLocale({
525
+ * format: 'DD/MM/YYYY',
526
+ * daysOfWeek: ['Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa', 'Do'],
527
+ * monthNames: ['Enero', 'Febrero', ...],
528
+ * applyLabel: 'Aplicar',
529
+ * cancelLabel: 'Cancelar'
530
+ * });
531
+ * ```
532
+ */
533
+ setLocale(locale) {
534
+ this.locale = { ...this.locale, ...locale };
535
+ }
536
+ /**
537
+ * Formats a date range value into a display string.
538
+ *
539
+ * @param value - The date range value to format
540
+ * @param format - Optional format string (uses locale format if not provided)
541
+ * @returns Formatted date string
542
+ *
543
+ * @example
544
+ * ```typescript
545
+ * const dateRange: NgxDatexValue = {
546
+ * startDate: new Date('2024-01-01'),
547
+ * endDate: new Date('2024-01-31')
548
+ * };
549
+ *
550
+ * const formatted = this.datexService.formatDateValue(dateRange, 'DD/MM/YYYY');
551
+ * // Returns: "01/01/2024 - 31/01/2024"
552
+ * ```
553
+ */
554
+ formatDateValue(value, format) {
555
+ if (!value)
556
+ return '';
557
+ const dateFormat = format || this.locale.format || 'DD/MM/YYYY';
558
+ const start = formatDateValue(value.startDate, dateFormat);
559
+ if (!value.endDate || value.startDate.getTime() === value.endDate.getTime()) {
560
+ return start;
561
+ }
562
+ const end = formatDateValue(value.endDate, dateFormat);
563
+ return `${start}${this.locale.separator || ' - '}${end}`;
564
+ }
565
+ buildCalendarMatrix(month) {
566
+ const firstDay = getStartOfMonth(month);
567
+ const firstDayOfWeek = this.locale.firstDay || 1;
568
+ const startCalendar = getStartOfWeek(firstDay, firstDayOfWeek);
569
+ const calendarDays = [];
570
+ let currentWeek = [];
571
+ const startDate = new Date(startCalendar);
572
+ for (let i = 0; i < 42; i++) {
573
+ if (i > 0 && i % 7 === 0) {
574
+ calendarDays.push(currentWeek);
575
+ currentWeek = [];
576
+ }
577
+ const currentDate = new Date(startDate);
578
+ currentDate.setDate(startDate.getDate() + i);
579
+ currentDate.setHours(12, 0, 0, 0);
580
+ currentWeek.push(currentDate);
581
+ }
582
+ if (currentWeek.length > 0) {
583
+ calendarDays.push(currentWeek);
584
+ }
585
+ return calendarDays;
586
+ }
587
+ validateDate(date) {
588
+ if (!date || isNaN(date.getTime())) {
589
+ return { isValid: false, error: 'Fecha inválida', errorCode: 'INVALID_DATE' };
590
+ }
591
+ return { isValid: true };
592
+ }
593
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
594
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexService, providedIn: 'root' });
595
+ }
596
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexService, decorators: [{
597
+ type: Injectable,
598
+ args: [{
599
+ providedIn: 'root',
600
+ }]
601
+ }] });
602
+
603
+ /**
604
+ * @fileoverview NgxDatex Overlay Service
605
+ *
606
+ * Service responsible for managing the calendar overlay positioning, creation, and lifecycle.
607
+ * Handles both desktop and mobile overlay strategies with proper event management.
608
+ */
609
+ /**
610
+ * Service for managing calendar overlay positioning and lifecycle.
611
+ *
612
+ * Provides methods to create, position, and manage the calendar overlay
613
+ * with support for both desktop and mobile layouts. Handles click outside
614
+ * detection, keyboard events, and responsive positioning.
615
+ *
616
+ * @example
617
+ * ```typescript
618
+ * // In component
619
+ * constructor(private overlayService: NgxDatexOverlayService) {}
620
+ *
621
+ * openCalendar() {
622
+ * this.overlayService.createOverlay(
623
+ * this.inputElement,
624
+ * this.calendarTemplate,
625
+ * this.viewContainerRef,
626
+ * this.locale,
627
+ * 'center',
628
+ * 'auto',
629
+ * () => this.closeCalendar(),
630
+ * (event) => this.handleKeydown(event),
631
+ * (position) => this.updatePosition(position)
632
+ * );
633
+ * }
634
+ * ```
635
+ */
636
+ class NgxDatexOverlayService {
637
+ overlayRef;
638
+ overlay = inject(Overlay);
639
+ /**
640
+ * Creates and configures the calendar overlay.
641
+ *
642
+ * Sets up the overlay with appropriate positioning strategy, event handlers,
643
+ * and responsive behavior. Reuses existing overlay if already created.
644
+ *
645
+ * @param inputElement - Reference to the input element for positioning
646
+ * @param calendarTemplate - Template reference for the calendar content
647
+ * @param viewContainerRef - View container for template portal
648
+ * @param locale - Localization settings for RTL/LTR direction
649
+ * @param opens - Horizontal alignment preference
650
+ * @param drops - Vertical alignment preference
651
+ * @param onOutsideClick - Callback for clicks outside the overlay
652
+ * @param onKeydown - Callback for keyboard events
653
+ * @param onPositionChange - Callback for position updates
654
+ * @returns The created or existing overlay reference
655
+ *
656
+ * @example
657
+ * ```typescript
658
+ * const overlayRef = this.overlayService.createOverlay(
659
+ * this.inputElement,
660
+ * this.calendarTemplate,
661
+ * this.viewContainerRef,
662
+ * this.locale(),
663
+ * this.opens(),
664
+ * this.drops(),
665
+ * () => this.closeCalendar(),
666
+ * (event) => {
667
+ * if (event.key === 'Escape') {
668
+ * this.closeCalendar();
669
+ * }
670
+ * },
671
+ * (position) => this.updateArrowPosition(position)
672
+ * );
673
+ * ```
674
+ */
675
+ createOverlay(inputElement, calendarTemplate, viewContainerRef, locale, opens, drops, onOutsideClick, onKeydown, onPositionChange) {
676
+ if (this.overlayRef && this.overlayRef.hasAttached()) {
677
+ return this.overlayRef;
678
+ }
679
+ if (!this.overlayRef) {
680
+ const config = this.getOverlayConfig(inputElement, locale, opens, drops, onPositionChange);
681
+ this.overlayRef = this.overlay.create(config);
682
+ this.setupOverlayEvents(inputElement, onOutsideClick, onKeydown);
683
+ }
684
+ if (!this.overlayRef.hasAttached()) {
685
+ const portal = new TemplatePortal(calendarTemplate, viewContainerRef);
686
+ this.overlayRef.attach(portal);
687
+ }
688
+ return this.overlayRef;
689
+ }
690
+ /**
691
+ * Closes the calendar overlay without disposing it.
692
+ *
693
+ * Detaches the overlay content while keeping the overlay reference
694
+ * for potential reuse. The overlay can be reopened without recreation.
695
+ *
696
+ * @example
697
+ * ```typescript
698
+ * // Close the overlay
699
+ * this.overlayService.closeOverlay();
700
+ * ```
701
+ */
702
+ closeOverlay() {
703
+ if (this.overlayRef && this.overlayRef.hasAttached()) {
704
+ this.overlayRef.detach();
705
+ }
706
+ }
707
+ /**
708
+ * Completely disposes of the overlay and cleans up resources.
709
+ *
710
+ * Detaches content, disposes the overlay reference, and cleans up
711
+ * all associated resources. Should be called during component destruction.
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * ngOnDestroy() {
716
+ * this.overlayService.disposeOverlay();
717
+ * }
718
+ * ```
719
+ */
720
+ disposeOverlay() {
721
+ if (this.overlayRef) {
722
+ if (this.overlayRef.hasAttached()) {
723
+ this.overlayRef.detach();
724
+ }
725
+ this.overlayRef.dispose();
726
+ this.overlayRef = undefined;
727
+ }
728
+ }
729
+ /**
730
+ * Sets up event listeners for the overlay.
731
+ *
732
+ * Configures click outside detection and keyboard event handling
733
+ * with different strategies for desktop and mobile devices.
734
+ *
735
+ * @private
736
+ * @param inputElement - Reference to the input element
737
+ * @param onOutsideClick - Callback for outside clicks
738
+ * @param onKeydown - Callback for keyboard events
739
+ */
740
+ setupOverlayEvents(inputElement, onOutsideClick, onKeydown) {
741
+ if (!this.overlayRef)
742
+ return;
743
+ if (!isMobileDevice()) {
744
+ this.overlayRef.outsidePointerEvents().subscribe((event) => {
745
+ const target = event.target;
746
+ const inputEl = inputElement?.nativeElement;
747
+ // Check if click was inside calendar or input
748
+ if ((inputEl && inputEl.contains(target)) ||
749
+ target.closest('.ngx-datex-overlay') ||
750
+ target.closest('.ngx-datex-calendar-grid') ||
751
+ target.closest('.ngx-datex-time-picker') ||
752
+ target.closest('.ngx-datex-ranges-sidebar') ||
753
+ target.closest('.ngx-datex-footer-buttons') ||
754
+ target.closest('.ngx-datex-mobile-header-buttons')) {
755
+ return;
756
+ }
757
+ onOutsideClick();
758
+ });
759
+ }
760
+ else {
761
+ this.overlayRef.backdropClick().subscribe(() => {
762
+ onOutsideClick();
763
+ });
764
+ }
765
+ this.overlayRef.keydownEvents().subscribe((event) => {
766
+ onKeydown(event);
767
+ });
768
+ }
769
+ /**
770
+ * Creates the overlay configuration based on device type and preferences.
771
+ *
772
+ * Returns different configurations for mobile and desktop devices,
773
+ * with appropriate positioning strategies and styling.
774
+ *
775
+ * @private
776
+ * @param inputElement - Reference to the input element
777
+ * @param locale - Localization settings
778
+ * @param opens - Horizontal alignment preference
779
+ * @param drops - Vertical alignment preference
780
+ * @param onPositionChange - Callback for position updates
781
+ * @returns Overlay configuration object
782
+ */
783
+ getOverlayConfig(inputElement, locale, opens, drops, onPositionChange) {
784
+ const positionStrategy = this.getPositionStrategy(inputElement, opens, drops, onPositionChange);
785
+ if (isMobileDevice()) {
786
+ return new OverlayConfig({
787
+ positionStrategy: this.overlay.position().global().bottom('0').centerHorizontally(),
788
+ hasBackdrop: true,
789
+ backdropClass: 'cdk-overlay-transparent-backdrop',
790
+ panelClass: ['ngx-datex-overlay-panel', 'ngx-datex-mobile-overlay'],
791
+ scrollStrategy: this.overlay.scrollStrategies.block(),
792
+ direction: locale.direction || 'ltr',
793
+ width: '100vw',
794
+ maxHeight: '85vh',
795
+ });
796
+ }
797
+ return new OverlayConfig({
798
+ positionStrategy,
799
+ hasBackdrop: false,
800
+ backdropClass: '',
801
+ panelClass: 'ngx-datex-overlay-panel',
802
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
803
+ direction: locale.direction || 'ltr',
804
+ });
805
+ }
806
+ /**
807
+ * Creates the flexible positioning strategy for desktop overlays.
808
+ *
809
+ * Configures multiple fallback positions based on user preferences
810
+ * and available space, with automatic repositioning on scroll.
811
+ *
812
+ * @private
813
+ * @param inputElement - Reference to the input element
814
+ * @param opens - Horizontal alignment preference
815
+ * @param drops - Vertical alignment preference
816
+ * @param onPositionChange - Callback for position updates
817
+ * @returns Flexible connected position strategy
818
+ */
819
+ getPositionStrategy(inputElement, opens, drops, onPositionChange) {
820
+ const positions = this.getPositions(opens, drops);
821
+ const strategy = this.overlay
822
+ .position()
823
+ .flexibleConnectedTo(inputElement)
824
+ .withPositions(positions)
825
+ .withFlexibleDimensions(true)
826
+ .withPush(true)
827
+ .withGrowAfterOpen(true)
828
+ .withViewportMargin(8);
829
+ strategy.positionChanges.subscribe((change) => {
830
+ if (change.connectionPair) {
831
+ onPositionChange({
832
+ originX: change.connectionPair.originX,
833
+ originY: change.connectionPair.originY,
834
+ overlayX: change.connectionPair.overlayX,
835
+ overlayY: change.connectionPair.overlayY,
836
+ });
837
+ }
838
+ });
839
+ return strategy;
840
+ }
841
+ /**
842
+ * Generates position configurations based on user preferences.
843
+ *
844
+ * Creates an array of connected positions with fallbacks,
845
+ * prioritizing user preferences while providing alternatives
846
+ * when space is limited.
847
+ *
848
+ * @private
849
+ * @param opens - Horizontal alignment preference
850
+ * @param drops - Vertical alignment preference
851
+ * @returns Array of connected position configurations
852
+ */
853
+ getPositions(opens, drops) {
854
+ let positions = [];
855
+ if (drops === 'auto') {
856
+ if (opens === 'left') {
857
+ positions = [
858
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
859
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
860
+ ];
861
+ }
862
+ else if (opens === 'right') {
863
+ positions = [
864
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
865
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
866
+ ];
867
+ }
868
+ else {
869
+ positions = [
870
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 4 },
871
+ {
872
+ originX: 'center',
873
+ originY: 'top',
874
+ overlayX: 'center',
875
+ overlayY: 'bottom',
876
+ offsetY: -4,
877
+ },
878
+ ];
879
+ }
880
+ }
881
+ else if (drops === 'up') {
882
+ if (opens === 'left') {
883
+ positions = [
884
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
885
+ ];
886
+ }
887
+ else if (opens === 'right') {
888
+ positions = [
889
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
890
+ ];
891
+ }
892
+ else {
893
+ positions = [
894
+ {
895
+ originX: 'center',
896
+ originY: 'top',
897
+ overlayX: 'center',
898
+ overlayY: 'bottom',
899
+ offsetY: -4,
900
+ },
901
+ ];
902
+ }
903
+ }
904
+ else {
905
+ if (opens === 'left') {
906
+ positions = [
907
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
908
+ ];
909
+ }
910
+ else if (opens === 'right') {
911
+ positions = [
912
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
913
+ ];
914
+ }
915
+ else {
916
+ positions = [
917
+ { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 4 },
918
+ ];
919
+ }
920
+ }
921
+ return positions;
922
+ }
923
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexOverlayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
924
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexOverlayService });
925
+ }
926
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexOverlayService, decorators: [{
927
+ type: Injectable
928
+ }] });
929
+
930
+ /**
931
+ * @fileoverview NgxDatex Time Picker Service
932
+ *
933
+ * Service responsible for managing time picker functionality including time validation,
934
+ * time component updates, and availability calculations based on date constraints.
935
+ */
936
+ /**
937
+ * Service for managing time picker operations and validations.
938
+ *
939
+ * Handles time component updates, validates time selections against date constraints,
940
+ * and provides available time options based on minimum/maximum date restrictions.
941
+ * Supports both 12-hour and 24-hour time formats with proper AM/PM handling.
942
+ *
943
+ * @example
944
+ * ```typescript
945
+ * // In component
946
+ * constructor(private timePickerService: NgxDatexTimePickerService) {}
947
+ *
948
+ * onTimeChange(side: 'start' | 'end', component: 'hour' | 'minute', value: number) {
949
+ * this.timePickerService.updateTimeFromPicker(
950
+ * this.currentStartDate,
951
+ * this.currentEndDate,
952
+ * side,
953
+ * component,
954
+ * value,
955
+ * this.startTime,
956
+ * this.endTime,
957
+ * this.timePicker24Hour,
958
+ * this.singleDatePicker,
959
+ * (date) => this.setStartDate(date),
960
+ * (date) => this.setEndDate(date),
961
+ * (side, time) => this.updateTimeSignal(side, time)
962
+ * );
963
+ * }
964
+ * ```
965
+ */
966
+ class NgxDatexTimePickerService {
967
+ /**
968
+ * Updates time from picker component changes and applies to dates.
969
+ *
970
+ * Handles time component updates (hour, minute, AM/PM) and applies the changes
971
+ * to the corresponding dates. Manages single date picker logic and ensures
972
+ * end dates don't precede start dates on the same day.
973
+ *
974
+ * @param currentStartDate - Current start date
975
+ * @param currentEndDate - Current end date (can be null)
976
+ * @param side - Which time picker is being updated ('start' or 'end')
977
+ * @param component - Which time component is changing ('hour', 'minute', or 'ampm')
978
+ * @param value - New value for the time component
979
+ * @param startTime - Current start time value
980
+ * @param endTime - Current end time value
981
+ * @param timePicker24Hour - Whether using 24-hour format
982
+ * @param singleDatePicker - Whether in single date picker mode
983
+ * @param onStartDateChange - Callback for start date changes
984
+ * @param onEndDateChange - Callback for end date changes
985
+ * @param onTimeChange - Callback for time signal updates
986
+ *
987
+ * @example
988
+ * ```typescript
989
+ * // Update start hour to 14 (2 PM)
990
+ * this.timePickerService.updateTimeFromPicker(
991
+ * this.startDate,
992
+ * this.endDate,
993
+ * 'start',
994
+ * 'hour',
995
+ * 14,
996
+ * this.startTime,
997
+ * this.endTime,
998
+ * true, // 24-hour format
999
+ * false, // range picker
1000
+ * (date) => this.updateStartDate(date),
1001
+ * (date) => this.updateEndDate(date),
1002
+ * (side, time) => this.updateTimeSignal(side, time)
1003
+ * );
1004
+ * ```
1005
+ */
1006
+ updateTimeFromPicker(currentStartDate, currentEndDate, side, component, value, startTime, endTime, timePicker24Hour, singleDatePicker, onStartDateChange, onEndDateChange, onTimeChange) {
1007
+ if (!currentStartDate)
1008
+ return;
1009
+ // Update the time signal
1010
+ if (side === 'start') {
1011
+ const newStartTime = { ...startTime, [component]: value };
1012
+ onTimeChange('start', newStartTime);
1013
+ // Apply time to date
1014
+ const newStartDate = this.applyTimeToDate(currentStartDate, newStartTime, timePicker24Hour);
1015
+ onStartDateChange(newStartDate);
1016
+ // Handle single date picker
1017
+ if (singleDatePicker) {
1018
+ onEndDateChange(new Date(newStartDate));
1019
+ }
1020
+ else if (currentEndDate &&
1021
+ isSameDate(currentEndDate, newStartDate, 'day') &&
1022
+ currentEndDate < newStartDate) {
1023
+ onEndDateChange(new Date(newStartDate));
1024
+ }
1025
+ }
1026
+ else if (currentEndDate) {
1027
+ const newEndTime = { ...endTime, [component]: value };
1028
+ onTimeChange('end', newEndTime);
1029
+ // Apply time to date
1030
+ const newEndDate = this.applyTimeToDate(currentEndDate, newEndTime, timePicker24Hour);
1031
+ onEndDateChange(newEndDate);
1032
+ }
1033
+ }
1034
+ /**
1035
+ * Applies time values to a date object.
1036
+ *
1037
+ * Creates a new date with the specified time components applied.
1038
+ * Handles 12-hour to 24-hour conversion and ensures proper time formatting.
1039
+ *
1040
+ * @private
1041
+ * @param date - Base date to apply time to
1042
+ * @param time - Time value with hour, minute, and AM/PM
1043
+ * @param is24Hour - Whether using 24-hour format
1044
+ * @returns New date with applied time
1045
+ *
1046
+ * @example
1047
+ * ```typescript
1048
+ * const date = new Date('2024-01-15');
1049
+ * const time = { hour: 2, minute: 30, ampm: 'PM' };
1050
+ * const result = this.applyTimeToDate(date, time, false);
1051
+ * // Result: 2024-01-15 14:30:00
1052
+ * ```
1053
+ */
1054
+ applyTimeToDate(date, time, is24Hour) {
1055
+ const newDate = new Date(date);
1056
+ let hour24 = time.hour;
1057
+ if (!is24Hour) {
1058
+ if (time.ampm === 'PM' && time.hour < 12)
1059
+ hour24 += 12;
1060
+ if (time.ampm === 'AM' && time.hour === 12)
1061
+ hour24 = 0;
1062
+ }
1063
+ newDate.setHours(hour24, time.minute, 0, 0);
1064
+ return newDate;
1065
+ }
1066
+ /**
1067
+ * Extracts time components from a date object.
1068
+ *
1069
+ * Converts a date to time picker format, handling both 12-hour and 24-hour
1070
+ * formats with proper AM/PM designation.
1071
+ *
1072
+ * @param date - Date to extract time from
1073
+ * @param timePicker24Hour - Whether using 24-hour format
1074
+ * @returns Time value object with hour, minute, and AM/PM
1075
+ *
1076
+ * @example
1077
+ * ```typescript
1078
+ * const date = new Date('2024-01-15 14:30:00');
1079
+ *
1080
+ * // 24-hour format
1081
+ * const time24 = this.updateTimeSignalsFromDate(date, true);
1082
+ * // Result: { hour: 14, minute: 30, ampm: 'AM' }
1083
+ *
1084
+ * // 12-hour format
1085
+ * const time12 = this.updateTimeSignalsFromDate(date, false);
1086
+ * // Result: { hour: 2, minute: 30, ampm: 'PM' }
1087
+ * ```
1088
+ */
1089
+ updateTimeSignalsFromDate(date, timePicker24Hour) {
1090
+ const hour24 = date.getHours();
1091
+ const minute = date.getMinutes();
1092
+ if (timePicker24Hour) {
1093
+ return { hour: hour24, minute, ampm: 'AM' };
1094
+ }
1095
+ else {
1096
+ const hour12 = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
1097
+ const ampm = hour24 >= 12 ? 'PM' : 'AM';
1098
+ return { hour: hour12, minute, ampm };
1099
+ }
1100
+ }
1101
+ /**
1102
+ * Gets available hours based on date constraints.
1103
+ *
1104
+ * Returns an array of hour options with disabled state based on minimum/maximum
1105
+ * date restrictions and start date constraints for end time selection.
1106
+ *
1107
+ * @param date - Date to get hours for
1108
+ * @param minDate - Minimum allowed date
1109
+ * @param maxDate - Maximum allowed date
1110
+ * @param timePicker24Hour - Whether using 24-hour format
1111
+ * @param timeValue - Current time value for AM/PM context
1112
+ * @param isEndTime - Whether this is for end time selection
1113
+ * @param startDate - Start date for end time validation
1114
+ * @returns Array of hour options with availability status
1115
+ *
1116
+ * @example
1117
+ * ```typescript
1118
+ * const availableHours = this.timePickerService.getAvailableHours(
1119
+ * new Date('2024-01-15'),
1120
+ * new Date('2024-01-15 10:00:00'), // minDate
1121
+ * new Date('2024-01-15 18:00:00'), // maxDate
1122
+ * false, // 12-hour format
1123
+ * { hour: 2, minute: 0, ampm: 'PM' },
1124
+ * false, // start time
1125
+ * );
1126
+ * // Result: [
1127
+ * // { value: 1, disabled: true }, // Before minDate
1128
+ * // { value: 2, disabled: false }, // Available
1129
+ * // { value: 6, disabled: true }, // After maxDate
1130
+ * // ]
1131
+ * ```
1132
+ */
1133
+ getAvailableHours(date, minDate, maxDate, timePicker24Hour, timeValue, isEndTime = false, startDate) {
1134
+ if (!date)
1135
+ return [];
1136
+ const hours = timePicker24Hour
1137
+ ? Array.from({ length: 24 }, (_, i) => i)
1138
+ : Array.from({ length: 12 }, (_, i) => i + 1);
1139
+ return hours.map((hour) => {
1140
+ let hour24 = hour;
1141
+ if (!timePicker24Hour) {
1142
+ hour24 =
1143
+ timeValue.ampm === 'PM' && hour !== 12
1144
+ ? hour + 12
1145
+ : timeValue.ampm === 'AM' && hour === 12
1146
+ ? 0
1147
+ : hour;
1148
+ }
1149
+ const testDate = new Date(date);
1150
+ testDate.setHours(hour24, 59, 59, 999);
1151
+ const testDateMin = new Date(date);
1152
+ testDateMin.setHours(hour24, 0, 0, 0);
1153
+ let disabled = false;
1154
+ if (minDate && testDate < minDate)
1155
+ disabled = true;
1156
+ if (maxDate && testDateMin > maxDate)
1157
+ disabled = true;
1158
+ // For end time, check against start date
1159
+ if (isEndTime && startDate && isSameDate(startDate, date, 'day')) {
1160
+ if (testDate < startDate)
1161
+ disabled = true;
1162
+ }
1163
+ return { value: hour, disabled };
1164
+ });
1165
+ }
1166
+ /**
1167
+ * Gets available minutes based on date and hour constraints.
1168
+ *
1169
+ * Returns an array of minute options with disabled state based on minimum/maximum
1170
+ * date restrictions, time picker increment, and start date constraints.
1171
+ *
1172
+ * @param date - Date to get minutes for
1173
+ * @param minDate - Minimum allowed date
1174
+ * @param maxDate - Maximum allowed date
1175
+ * @param timeValue - Current time value for hour context
1176
+ * @param timePickerIncrement - Minute increment step
1177
+ * @param timePicker24Hour - Whether using 24-hour format
1178
+ * @param isEndTime - Whether this is for end time selection
1179
+ * @param startDate - Start date for end time validation
1180
+ * @returns Array of minute options with availability status
1181
+ *
1182
+ * @example
1183
+ * ```typescript
1184
+ * const availableMinutes = this.timePickerService.getAvailableMinutes(
1185
+ * new Date('2024-01-15'),
1186
+ * new Date('2024-01-15 14:15:00'), // minDate
1187
+ * new Date('2024-01-15 14:45:00'), // maxDate
1188
+ * { hour: 14, minute: 30, ampm: 'PM' },
1189
+ * 15, // 15-minute increments
1190
+ * true, // 24-hour format
1191
+ * false, // start time
1192
+ * );
1193
+ * // Result: [
1194
+ * // { value: 0, disabled: true }, // Before minDate
1195
+ * // { value: 15, disabled: false }, // Available
1196
+ * // { value: 30, disabled: false }, // Available
1197
+ * // { value: 45, disabled: false }, // Available
1198
+ * // ]
1199
+ * ```
1200
+ */
1201
+ getAvailableMinutes(date, minDate, maxDate, timeValue, timePickerIncrement, timePicker24Hour, isEndTime = false, startDate) {
1202
+ if (!date)
1203
+ return [];
1204
+ const minutes = [];
1205
+ for (let i = 0; i < 60; i += timePickerIncrement) {
1206
+ minutes.push(i);
1207
+ }
1208
+ // Convert current hour to 24h format
1209
+ let hour24 = timeValue.hour;
1210
+ if (!timePicker24Hour) {
1211
+ hour24 =
1212
+ timeValue.ampm === 'PM' && timeValue.hour !== 12
1213
+ ? timeValue.hour + 12
1214
+ : timeValue.ampm === 'AM' && timeValue.hour === 12
1215
+ ? 0
1216
+ : timeValue.hour;
1217
+ }
1218
+ return minutes.map((minute) => {
1219
+ const testDate = new Date(date);
1220
+ testDate.setHours(hour24, minute, 59, 999);
1221
+ const testDateMin = new Date(date);
1222
+ testDateMin.setHours(hour24, minute, 0, 0);
1223
+ let disabled = false;
1224
+ if (minDate && testDate < minDate)
1225
+ disabled = true;
1226
+ if (maxDate && testDateMin > maxDate)
1227
+ disabled = true;
1228
+ // For end time, check against start date
1229
+ if (isEndTime && startDate && isSameDate(startDate, date, 'day')) {
1230
+ const startHour = startDate.getHours();
1231
+ if (hour24 === startHour) {
1232
+ if (minute < startDate.getMinutes())
1233
+ disabled = true;
1234
+ }
1235
+ else if (hour24 < startHour) {
1236
+ disabled = true;
1237
+ }
1238
+ }
1239
+ return { value: minute, disabled };
1240
+ });
1241
+ }
1242
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexTimePickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1243
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexTimePickerService });
1244
+ }
1245
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexTimePickerService, decorators: [{
1246
+ type: Injectable
1247
+ }] });
1248
+
1249
+ class NgxDatexCalendarService {
1250
+ updateMonthsInView(startDate, endDate, currentLeftMonth, currentRightMonth, singleDatePicker, linkedCalendars, maxDate, onLeftMonthChange, onRightMonthChange) {
1251
+ if (!startDate)
1252
+ return;
1253
+ if (endDate) {
1254
+ // Check if both dates are already visible
1255
+ if (!singleDatePicker && currentLeftMonth && currentRightMonth) {
1256
+ const startYearMonth = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}`;
1257
+ const endYearMonth = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}`;
1258
+ const leftYearMonth = `${currentLeftMonth.getFullYear()}-${String(currentLeftMonth.getMonth() + 1).padStart(2, '0')}`;
1259
+ const rightYearMonth = `${currentRightMonth.getFullYear()}-${String(currentRightMonth.getMonth() + 1).padStart(2, '0')}`;
1260
+ if ((startYearMonth === leftYearMonth || startYearMonth === rightYearMonth) &&
1261
+ (endYearMonth === leftYearMonth || endYearMonth === rightYearMonth)) {
1262
+ return;
1263
+ }
1264
+ }
1265
+ // Set left calendar to start date month
1266
+ const newLeftMonth = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
1267
+ onLeftMonthChange(newLeftMonth);
1268
+ if (!linkedCalendars &&
1269
+ (endDate.getMonth() !== startDate.getMonth() ||
1270
+ endDate.getFullYear() !== startDate.getFullYear())) {
1271
+ // Show end date month on right
1272
+ const newRightMonth = new Date(endDate.getFullYear(), endDate.getMonth(), 1);
1273
+ onRightMonthChange(newRightMonth);
1274
+ }
1275
+ else {
1276
+ // Show next month on right
1277
+ const nextMonth = addMonths(newLeftMonth, 1);
1278
+ onRightMonthChange(nextMonth);
1279
+ }
1280
+ }
1281
+ else {
1282
+ // Only start date - navigate if not visible
1283
+ const startYearMonth = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}`;
1284
+ const leftYearMonth = currentLeftMonth
1285
+ ? `${currentLeftMonth.getFullYear()}-${String(currentLeftMonth.getMonth() + 1).padStart(2, '0')}`
1286
+ : '';
1287
+ const rightYearMonth = currentRightMonth
1288
+ ? `${currentRightMonth.getFullYear()}-${String(currentRightMonth.getMonth() + 1).padStart(2, '0')}`
1289
+ : '';
1290
+ if (leftYearMonth !== startYearMonth && rightYearMonth !== startYearMonth) {
1291
+ const newLeftMonth = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
1292
+ onLeftMonthChange(newLeftMonth);
1293
+ const nextMonth = addMonths(newLeftMonth, 1);
1294
+ onRightMonthChange(nextMonth);
1295
+ }
1296
+ }
1297
+ // Check max date limits
1298
+ if (maxDate && linkedCalendars && !singleDatePicker && currentRightMonth > maxDate) {
1299
+ const maxRightMonth = new Date(maxDate.getFullYear(), maxDate.getMonth(), 1);
1300
+ const maxLeftMonth = addMonths(maxRightMonth, -1);
1301
+ onRightMonthChange(maxRightMonth);
1302
+ onLeftMonthChange(maxLeftMonth);
1303
+ }
1304
+ }
1305
+ setStartDate(startDate, minDate, maxDate, timePicker, timePickerIncrement) {
1306
+ let newStartDate = new Date(startDate);
1307
+ if (!timePicker) {
1308
+ newStartDate = startOfDay(newStartDate);
1309
+ }
1310
+ if (timePicker && timePickerIncrement) {
1311
+ const minutes = newStartDate.getMinutes();
1312
+ const roundedMinutes = Math.round(minutes / timePickerIncrement) * timePickerIncrement;
1313
+ newStartDate.setMinutes(roundedMinutes);
1314
+ }
1315
+ if (minDate && isBeforeDate(newStartDate, minDate)) {
1316
+ newStartDate = new Date(minDate);
1317
+ if (timePicker && timePickerIncrement) {
1318
+ const minutes = newStartDate.getMinutes();
1319
+ const roundedMinutes = Math.round(minutes / timePickerIncrement) * timePickerIncrement;
1320
+ newStartDate.setMinutes(roundedMinutes);
1321
+ }
1322
+ }
1323
+ if (maxDate && newStartDate > maxDate) {
1324
+ newStartDate = new Date(maxDate);
1325
+ if (timePicker && timePickerIncrement) {
1326
+ const minutes = newStartDate.getMinutes();
1327
+ const flooredMinutes = Math.floor(minutes / timePickerIncrement) * timePickerIncrement;
1328
+ newStartDate.setMinutes(flooredMinutes);
1329
+ }
1330
+ }
1331
+ return newStartDate;
1332
+ }
1333
+ setEndDate(endDate, startDate, maxDate, maxSpan, timePicker, timePickerIncrement) {
1334
+ let newEndDate = new Date(endDate);
1335
+ if (!timePicker) {
1336
+ newEndDate = endOfDay(newEndDate);
1337
+ }
1338
+ if (timePicker && timePickerIncrement) {
1339
+ const minutes = newEndDate.getMinutes();
1340
+ const roundedMinutes = Math.round(minutes / timePickerIncrement) * timePickerIncrement;
1341
+ newEndDate.setMinutes(roundedMinutes);
1342
+ }
1343
+ if (startDate && isBeforeDate(newEndDate, startDate)) {
1344
+ newEndDate = new Date(startDate);
1345
+ }
1346
+ if (maxDate && newEndDate > maxDate) {
1347
+ newEndDate = new Date(maxDate);
1348
+ }
1349
+ // Validate maxSpan
1350
+ if (maxSpan && startDate) {
1351
+ const maxLimit = addDays(startDate, this.getMaxSpanDays(maxSpan));
1352
+ if (newEndDate > maxLimit) {
1353
+ newEndDate = new Date(maxLimit);
1354
+ }
1355
+ }
1356
+ return newEndDate;
1357
+ }
1358
+ getMaxSpanDays(maxSpan) {
1359
+ let days = 0;
1360
+ if (maxSpan['days'])
1361
+ days += maxSpan['days'];
1362
+ if (maxSpan['weeks'])
1363
+ days += maxSpan['weeks'] * 7;
1364
+ if (maxSpan['months'])
1365
+ days += maxSpan['months'] * 30;
1366
+ if (maxSpan['years'])
1367
+ days += maxSpan['years'] * 365;
1368
+ return days;
1369
+ }
1370
+ isDateDisabled(date, minDate, maxDate, maxSpan, currentStartDate, currentEndDate, singleDatePicker, isInvalidDateFn) {
1371
+ if (minDate && date < minDate)
1372
+ return true;
1373
+ if (maxDate && date > maxDate)
1374
+ return true;
1375
+ if (maxSpan && !singleDatePicker && currentStartDate && !currentEndDate) {
1376
+ const maxLimit = addDays(currentStartDate, this.getMaxSpanDays(maxSpan));
1377
+ if (date > maxLimit)
1378
+ return true;
1379
+ }
1380
+ if (isInvalidDateFn && isInvalidDateFn(date))
1381
+ return true;
1382
+ return false;
1383
+ }
1384
+ isDateSelected(date, currentStartDate, currentEndDate, singleDatePicker) {
1385
+ if (!currentStartDate)
1386
+ return false;
1387
+ if (singleDatePicker) {
1388
+ return isSameDate(date, currentStartDate, 'day');
1389
+ }
1390
+ return (isSameDate(date, currentStartDate, 'day') ||
1391
+ (currentEndDate !== null && isSameDate(date, currentEndDate, 'day')));
1392
+ }
1393
+ isDateInRange(date, currentStartDate, currentEndDate, singleDatePicker) {
1394
+ if (!currentStartDate || !currentEndDate || singleDatePicker)
1395
+ return false;
1396
+ return date > currentStartDate && date < currentEndDate;
1397
+ }
1398
+ isDateRangeStart(date, currentStartDate) {
1399
+ if (!currentStartDate)
1400
+ return false;
1401
+ return isSameDate(date, currentStartDate, 'day');
1402
+ }
1403
+ isDateRangeEnd(date, currentEndDate) {
1404
+ if (!currentEndDate)
1405
+ return false;
1406
+ return isSameDate(date, currentEndDate, 'day');
1407
+ }
1408
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexCalendarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1409
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexCalendarService });
1410
+ }
1411
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatexCalendarService, decorators: [{
1412
+ type: Injectable
1413
+ }] });
1414
+
1415
+ /**
1416
+ * @fileoverview NgxDatex - Advanced Angular Date Range Picker Component
1417
+ *
1418
+ * A comprehensive date picker component for Angular applications that supports:
1419
+ * - Single date and date range selection
1420
+ * - Time picker integration
1421
+ * - Predefined date ranges
1422
+ * - Multiple themes and localization
1423
+ * - Mobile-responsive design
1424
+ * - Accessibility features
1425
+ * - Form integration with ControlValueAccessor
1426
+ *
1427
+ * Based on the popular vanilla daterangepicker library but built specifically for Angular
1428
+ * with modern Angular patterns including signals, standalone components, and CDK overlays.
1429
+ *
1430
+ * @example
1431
+ * Basic usage:
1432
+ * ```html
1433
+ * <ngx-datex
1434
+ * [(ngModel)]="selectedRange"
1435
+ * [ranges]="predefinedRanges"
1436
+ * [locale]="spanishLocale">
1437
+ * </ngx-datex>
1438
+ * ```
1439
+ *
1440
+ * @example
1441
+ * Advanced configuration:
1442
+ * ```html
1443
+ * <ngx-datex
1444
+ * [singleDatePicker]="false"
1445
+ * [timePicker]="true"
1446
+ * [autoApply]="true"
1447
+ * [showDropdowns]="true"
1448
+ * [linkedCalendars]="true"
1449
+ * [minDate]="minDate"
1450
+ * [maxDate]="maxDate"
1451
+ * (dateChange)="onDateChange($event)"
1452
+ * (rangeChange)="onRangeChange($event)">
1453
+ * </ngx-datex>
1454
+ * ```
1455
+ */
1456
+ /**
1457
+ * NgxDatex - Advanced Angular Date Range Picker Component
1458
+ *
1459
+ * A feature-rich, accessible date picker component that provides both single date
1460
+ * and date range selection capabilities. Built with Angular signals for optimal
1461
+ * performance and modern Angular patterns.
1462
+ *
1463
+ * ## Key Features
1464
+ * - 📅 Single date and date range selection
1465
+ * - ⏰ Optional time picker with 12/24 hour formats
1466
+ * - 🎨 Customizable themes and styling
1467
+ * - 🌍 Full internationalization support
1468
+ * - 📱 Mobile-responsive design
1469
+ * - ♿ WCAG accessibility compliant
1470
+ * - 🔧 Extensive configuration options
1471
+ * - 📋 Angular Forms integration (ControlValueAccessor)
1472
+ *
1473
+ * ## Basic Usage
1474
+ * ```html
1475
+ * <ngx-datex
1476
+ * [(ngModel)]="selectedRange"
1477
+ * [locale]="locale"
1478
+ * [ranges]="predefinedRanges">
1479
+ * </ngx-datex>
1480
+ * ```
1481
+ *
1482
+ * ## Advanced Configuration
1483
+ * ```typescript
1484
+ * // Component
1485
+ * export class MyComponent {
1486
+ * selectedRange: NgxDatexValue | null = null;
1487
+ *
1488
+ * customRanges = {
1489
+ * 'Today': [startOfDay(new Date()), endOfDay(new Date())],
1490
+ * 'Last 7 Days': [startOfDay(subDays(new Date(), 6)), endOfDay(new Date())]
1491
+ * };
1492
+ *
1493
+ * onDateChange(value: NgxDatexValue | null) {
1494
+ * console.log('Selected range:', value);
1495
+ * }
1496
+ * }
1497
+ * ```
1498
+ *
1499
+ * ```html
1500
+ * <!-- Template -->
1501
+ * <ngx-datex
1502
+ * [singleDatePicker]="false"
1503
+ * [timePicker]="true"
1504
+ * [autoApply]="false"
1505
+ * [showDropdowns]="true"
1506
+ * [ranges]="customRanges"
1507
+ * [minDate]="minDate"
1508
+ * [maxDate]="maxDate"
1509
+ * (dateChange)="onDateChange($event)"
1510
+ * (rangeChange)="onRangeChange($event)">
1511
+ * </ngx-datex>
1512
+ * ```
1513
+ *
1514
+ * @see {@link NgxDatexValue} for the value interface
1515
+ * @see {@link NgxDatexConfig} for configuration options
1516
+ * @see {@link NgxDatexLocale} for localization settings
1517
+ * @see {@link NgxDatexTheme} for theming options
1518
+ */
1519
+ class NgxDatex {
1520
+ inputElement;
1521
+ calendarTemplate;
1522
+ // Services
1523
+ datexService = inject(NgxDatexService);
1524
+ overlayService = inject(NgxDatexOverlayService);
1525
+ timePickerService = inject(NgxDatexTimePickerService);
1526
+ calendarService = inject(NgxDatexCalendarService);
1527
+ platformId = inject(PLATFORM_ID);
1528
+ viewContainerRef = inject(ViewContainerRef);
1529
+ // Configuration inputs
1530
+ /**
1531
+ * General configuration options for the date picker.
1532
+ *
1533
+ * @example
1534
+ * ```typescript
1535
+ * const config: NgxDatexConfig = {
1536
+ * dateFormat: 'DD/MM/YYYY',
1537
+ * firstDayOfWeek: 1,
1538
+ * businessDaysOnly: true
1539
+ * };
1540
+ * ```
1541
+ */
1542
+ config = input({}, ...(ngDevMode ? [{ debugName: "config" }] : []));
1543
+ /**
1544
+ * Localization settings for internationalization.
1545
+ * Controls language, date formats, and UI text.
1546
+ *
1547
+ * @default SPANISH_LOCALE
1548
+ *
1549
+ * @example
1550
+ * ```typescript
1551
+ * const frenchLocale: NgxDatexLocale = {
1552
+ * format: 'DD/MM/YYYY',
1553
+ * daysOfWeek: ['Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa', 'Di'],
1554
+ * applyLabel: 'Appliquer',
1555
+ * cancelLabel: 'Annuler'
1556
+ * };
1557
+ * ```
1558
+ */
1559
+ locale = input(SPANISH_LOCALE, ...(ngDevMode ? [{ debugName: "locale" }] : []));
1560
+ /**
1561
+ * Theme configuration for styling the date picker.
1562
+ * Defines colors, typography, spacing, and visual appearance.
1563
+ *
1564
+ * @default MATERIAL_LIGHT_THEME
1565
+ *
1566
+ * @example
1567
+ * ```typescript
1568
+ * const darkTheme: NgxDatexTheme = {
1569
+ * name: 'dark',
1570
+ * colors: { primary: '#bb86fc', background: '#121212', ... }
1571
+ * };
1572
+ * ```
1573
+ */
1574
+ theme = input(MATERIAL_LIGHT_THEME, ...(ngDevMode ? [{ debugName: "theme" }] : []));
1575
+ // Appearance inputs
1576
+ /**
1577
+ * Material form field appearance style.
1578
+ *
1579
+ * @default 'outline'
1580
+ *
1581
+ * @example
1582
+ * ```html
1583
+ * <ngx-datex appearance="fill"></ngx-datex>
1584
+ * <ngx-datex appearance="outline"></ngx-datex>
1585
+ * ```
1586
+ */
1587
+ appearance = input('outline', ...(ngDevMode ? [{ debugName: "appearance" }] : []));
1588
+ /**
1589
+ * Controls when the form field label should float.
1590
+ *
1591
+ * @default 'auto'
1592
+ *
1593
+ * @example
1594
+ * ```html
1595
+ * <ngx-datex floatLabel="always"></ngx-datex>
1596
+ * <ngx-datex floatLabel="auto"></ngx-datex>
1597
+ * ```
1598
+ */
1599
+ floatLabel = input('auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : []));
1600
+ /**
1601
+ * Label text displayed above the input field.
1602
+ *
1603
+ * @default ''
1604
+ *
1605
+ * @example
1606
+ * ```html
1607
+ * <ngx-datex label="Select Date Range"></ngx-datex>
1608
+ * ```
1609
+ */
1610
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
1611
+ /**
1612
+ * Placeholder text shown when no date is selected.
1613
+ *
1614
+ * @default 'Seleccionar fecha'
1615
+ *
1616
+ * @example
1617
+ * ```html
1618
+ * <ngx-datex placeholder="Choose your dates"></ngx-datex>
1619
+ * ```
1620
+ */
1621
+ placeholder = input('Seleccionar fecha', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
1622
+ /**
1623
+ * Material icon name for the calendar icon.
1624
+ *
1625
+ * @default 'calendar_today'
1626
+ *
1627
+ * @example
1628
+ * ```html
1629
+ * <ngx-datex calendarIcon="event" [showCalendarIcon]="true"></ngx-datex>
1630
+ * ```
1631
+ */
1632
+ calendarIcon = input('calendar_today', ...(ngDevMode ? [{ debugName: "calendarIcon" }] : []));
1633
+ /**
1634
+ * Whether to display the calendar icon.
1635
+ *
1636
+ * @default false
1637
+ *
1638
+ * @example
1639
+ * ```html
1640
+ * <ngx-datex [showCalendarIcon]="true"></ngx-datex>
1641
+ * ```
1642
+ */
1643
+ showCalendarIcon = input(false, ...(ngDevMode ? [{ debugName: "showCalendarIcon" }] : []));
1644
+ /**
1645
+ * Position of the calendar icon relative to the input.
1646
+ *
1647
+ * @default 'suffix'
1648
+ *
1649
+ * @example
1650
+ * ```html
1651
+ * <ngx-datex [showCalendarIcon]="true" calendarIconPosition="prefix"></ngx-datex>
1652
+ * ```
1653
+ */
1654
+ calendarIconPosition = input('suffix', ...(ngDevMode ? [{ debugName: "calendarIconPosition" }] : []));
1655
+ /**
1656
+ * Whether to display a checkbox alongside the input.
1657
+ *
1658
+ * @default false
1659
+ *
1660
+ * @example
1661
+ * ```html
1662
+ * <ngx-datex [showCheckbox]="true" (checkboxChange)="onCheckboxChange($event)"></ngx-datex>
1663
+ * ```
1664
+ */
1665
+ showCheckbox = input(false, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
1666
+ /**
1667
+ * Position of the checkbox relative to the input.
1668
+ *
1669
+ * @default 'prefix'
1670
+ *
1671
+ * @example
1672
+ * ```html
1673
+ * <ngx-datex [showCheckbox]="true" checkboxPosition="suffix"></ngx-datex>
1674
+ * ```
1675
+ */
1676
+ checkboxPosition = input('prefix', ...(ngDevMode ? [{ debugName: "checkboxPosition" }] : []));
1677
+ /**
1678
+ * Whether the input field is readonly.
1679
+ * When true, users can only interact with the calendar popup.
1680
+ *
1681
+ * @default false
1682
+ *
1683
+ * @example
1684
+ * ```html
1685
+ * <ngx-datex [readonly]="true"></ngx-datex>
1686
+ * ```
1687
+ */
1688
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
1689
+ /**
1690
+ * Whether the entire component is disabled.
1691
+ *
1692
+ * @default false
1693
+ *
1694
+ * @example
1695
+ * ```html
1696
+ * <ngx-datex [disabled]="isFormDisabled"></ngx-datex>
1697
+ * ```
1698
+ */
1699
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1700
+ /**
1701
+ * Whether to show the calendar header with navigation controls.
1702
+ *
1703
+ * @default true
1704
+ *
1705
+ * @example
1706
+ * ```html
1707
+ * <ngx-datex [showHeader]="false"></ngx-datex>
1708
+ * ```
1709
+ */
1710
+ showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : []));
1711
+ /**
1712
+ * Whether to show the calendar footer with apply/cancel buttons.
1713
+ *
1714
+ * @default true
1715
+ *
1716
+ * @example
1717
+ * ```html
1718
+ * <ngx-datex [showFooter]="false" [autoApply]="true"></ngx-datex>
1719
+ * ```
1720
+ */
1721
+ showFooter = input(true, ...(ngDevMode ? [{ debugName: "showFooter" }] : []));
1722
+ // Behavior inputs
1723
+ /**
1724
+ * Whether to use single date selection mode instead of date range selection.
1725
+ * When true, only one date can be selected at a time.
1726
+ *
1727
+ * @default false
1728
+ *
1729
+ * @example
1730
+ * ```html
1731
+ * <!-- Single date picker -->
1732
+ * <ngx-datex [singleDatePicker]="true"></ngx-datex>
1733
+ *
1734
+ * <!-- Date range picker (default) -->
1735
+ * <ngx-datex [singleDatePicker]="false"></ngx-datex>
1736
+ * ```
1737
+ */
1738
+ singleDatePicker = input(false, ...(ngDevMode ? [{ debugName: "singleDatePicker" }] : []));
1739
+ /**
1740
+ * Whether to automatically apply the selection without requiring the Apply button.
1741
+ * When true, selections are applied immediately. Automatically disabled when timePicker is enabled.
1742
+ *
1743
+ * @default false
1744
+ *
1745
+ * @example
1746
+ * ```html
1747
+ * <!-- Auto-apply selections -->
1748
+ * <ngx-datex [autoApply]="true"></ngx-datex>
1749
+ *
1750
+ * <!-- Require Apply button -->
1751
+ * <ngx-datex [autoApply]="false" [showFooter]="true"></ngx-datex>
1752
+ * ```
1753
+ */
1754
+ autoApply = input(false, ...(ngDevMode ? [{ debugName: "autoApply" }] : []));
1755
+ /**
1756
+ * Whether to show month and year dropdown selectors in the calendar header.
1757
+ * Allows quick navigation to distant dates.
1758
+ *
1759
+ * @default false
1760
+ *
1761
+ * @example
1762
+ * ```html
1763
+ * <ngx-datex [showDropdowns]="true"></ngx-datex>
1764
+ * ```
1765
+ */
1766
+ showDropdowns = input(false, ...(ngDevMode ? [{ debugName: "showDropdowns" }] : []));
1767
+ /**
1768
+ * Whether to enable time selection alongside date selection.
1769
+ * Adds time picker controls to the calendar.
1770
+ *
1771
+ * @default false
1772
+ *
1773
+ * @example
1774
+ * ```html
1775
+ * <!-- Date and time picker -->
1776
+ * <ngx-datex [timePicker]="true" [timePicker24Hour]="true"></ngx-datex>
1777
+ *
1778
+ * <!-- Date only picker -->
1779
+ * <ngx-datex [timePicker]="false"></ngx-datex>
1780
+ * ```
1781
+ */
1782
+ timePicker = input(false, ...(ngDevMode ? [{ debugName: "timePicker" }] : []));
1783
+ /**
1784
+ * Whether to use 24-hour time format instead of 12-hour AM/PM format.
1785
+ * Only applies when timePicker is enabled.
1786
+ *
1787
+ * @default true
1788
+ *
1789
+ * @example
1790
+ * ```html
1791
+ * <!-- 24-hour format: 14:30 -->
1792
+ * <ngx-datex [timePicker]="true" [timePicker24Hour]="true"></ngx-datex>
1793
+ *
1794
+ * <!-- 12-hour format: 2:30 PM -->
1795
+ * <ngx-datex [timePicker]="true" [timePicker24Hour]="false"></ngx-datex>
1796
+ * ```
1797
+ */
1798
+ timePicker24Hour = input(true, ...(ngDevMode ? [{ debugName: "timePicker24Hour" }] : []));
1799
+ /**
1800
+ * The increment step for minute selection in the time picker.
1801
+ * Minutes will be rounded to the nearest increment.
1802
+ *
1803
+ * @default 1
1804
+ *
1805
+ * @example
1806
+ * ```html
1807
+ * <!-- 15-minute increments: 00, 15, 30, 45 -->
1808
+ * <ngx-datex [timePicker]="true" [timePickerIncrement]="15"></ngx-datex>
1809
+ *
1810
+ * <!-- 5-minute increments: 00, 05, 10, 15, 20... -->
1811
+ * <ngx-datex [timePicker]="true" [timePickerIncrement]="5"></ngx-datex>
1812
+ * ```
1813
+ */
1814
+ timePickerIncrement = input(1, ...(ngDevMode ? [{ debugName: "timePickerIncrement" }] : []));
1815
+ /**
1816
+ * Whether to show seconds in the time picker.
1817
+ * Only applies when timePicker is enabled.
1818
+ *
1819
+ * @default false
1820
+ *
1821
+ * @example
1822
+ * ```html
1823
+ * <ngx-datex [timePicker]="true" [timePickerSeconds]="true"></ngx-datex>
1824
+ * ```
1825
+ */
1826
+ timePickerSeconds = input(false, ...(ngDevMode ? [{ debugName: "timePickerSeconds" }] : []));
1827
+ /**
1828
+ * Whether the left and right calendars should be linked for navigation.
1829
+ * When true, navigating one calendar automatically updates the other.
1830
+ *
1831
+ * @default true
1832
+ *
1833
+ * @example
1834
+ * ```html
1835
+ * <!-- Linked navigation -->
1836
+ * <ngx-datex [linkedCalendars]="true"></ngx-datex>
1837
+ *
1838
+ * <!-- Independent navigation -->
1839
+ * <ngx-datex [linkedCalendars]="false"></ngx-datex>
1840
+ * ```
1841
+ */
1842
+ linkedCalendars = input(true, ...(ngDevMode ? [{ debugName: "linkedCalendars" }] : []));
1843
+ /**
1844
+ * Whether to automatically update the input field with the selected value.
1845
+ * When false, the input remains unchanged until manually updated.
1846
+ *
1847
+ * @default true
1848
+ *
1849
+ * @example
1850
+ * ```html
1851
+ * <!-- Auto-update input -->
1852
+ * <ngx-datex [autoUpdateInput]="true"></ngx-datex>
1853
+ *
1854
+ * <!-- Manual input updates -->
1855
+ * <ngx-datex [autoUpdateInput]="false"></ngx-datex>
1856
+ * ```
1857
+ */
1858
+ autoUpdateInput = input(true, ...(ngDevMode ? [{ debugName: "autoUpdateInput" }] : []));
1859
+ /**
1860
+ * Whether to always show both calendars even in single date picker mode.
1861
+ * Useful for consistent layout regardless of picker mode.
1862
+ *
1863
+ * @default false
1864
+ *
1865
+ * @example
1866
+ * ```html
1867
+ * <ngx-datex [singleDatePicker]="true" [alwaysShowCalendars]="true"></ngx-datex>
1868
+ * ```
1869
+ */
1870
+ alwaysShowCalendars = input(false, ...(ngDevMode ? [{ debugName: "alwaysShowCalendars" }] : []));
1871
+ /**
1872
+ * Whether to show the "Custom Range" option in the predefined ranges list.
1873
+ * Only applies when ranges are configured.
1874
+ *
1875
+ * @default true
1876
+ *
1877
+ * @example
1878
+ * ```html
1879
+ * <!-- Show custom range option -->
1880
+ * <ngx-datex [ranges]="predefinedRanges" [showCustomRangeLabel]="true"></ngx-datex>
1881
+ *
1882
+ * <!-- Hide custom range option -->
1883
+ * <ngx-datex [ranges]="predefinedRanges" [showCustomRangeLabel]="false"></ngx-datex>
1884
+ * ```
1885
+ */
1886
+ showCustomRangeLabel = input(true, ...(ngDevMode ? [{ debugName: "showCustomRangeLabel" }] : []));
1887
+ // Computed autoApply following vanilla daterangepicker logic
1888
+ effectiveAutoApply = computed(() => {
1889
+ const autoApplyInput = this.autoApply();
1890
+ const timePickerEnabled = this.timePicker();
1891
+ return timePickerEnabled ? false : autoApplyInput;
1892
+ }, ...(ngDevMode ? [{ debugName: "effectiveAutoApply" }] : []));
1893
+ // Date range inputs
1894
+ /**
1895
+ * The initial start date for the date picker.
1896
+ * If not provided, defaults to today.
1897
+ *
1898
+ * @default null
1899
+ *
1900
+ * @example
1901
+ * ```typescript
1902
+ * const startDate = new Date('2024-01-01');
1903
+ * ```
1904
+ *
1905
+ * ```html
1906
+ * <ngx-datex [startDate]="startDate" [endDate]="endDate"></ngx-datex>
1907
+ * ```
1908
+ */
1909
+ startDate = input(null, ...(ngDevMode ? [{ debugName: "startDate" }] : []));
1910
+ /**
1911
+ * The initial end date for the date picker.
1912
+ * If not provided, defaults to the start date (single date) or today (range).
1913
+ *
1914
+ * @default null
1915
+ *
1916
+ * @example
1917
+ * ```typescript
1918
+ * const endDate = new Date('2024-01-31');
1919
+ * ```
1920
+ *
1921
+ * ```html
1922
+ * <ngx-datex [startDate]="startDate" [endDate]="endDate"></ngx-datex>
1923
+ * ```
1924
+ */
1925
+ endDate = input(null, ...(ngDevMode ? [{ debugName: "endDate" }] : []));
1926
+ /**
1927
+ * The minimum selectable date.
1928
+ * Users cannot select dates before this date.
1929
+ *
1930
+ * @default null
1931
+ *
1932
+ * @example
1933
+ * ```typescript
1934
+ * // Restrict to dates from today onwards
1935
+ * const minDate = new Date();
1936
+ * ```
1937
+ *
1938
+ * ```html
1939
+ * <ngx-datex [minDate]="minDate"></ngx-datex>
1940
+ * ```
1941
+ */
1942
+ minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : []));
1943
+ /**
1944
+ * The maximum selectable date.
1945
+ * Users cannot select dates after this date.
1946
+ *
1947
+ * @default null
1948
+ *
1949
+ * @example
1950
+ * ```typescript
1951
+ * // Restrict to dates within the next year
1952
+ * const maxDate = new Date();
1953
+ * maxDate.setFullYear(maxDate.getFullYear() + 1);
1954
+ * ```
1955
+ *
1956
+ * ```html
1957
+ * <ngx-datex [maxDate]="maxDate"></ngx-datex>
1958
+ * ```
1959
+ */
1960
+ maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
1961
+ /**
1962
+ * The maximum allowed span between start and end dates.
1963
+ * Prevents users from selecting ranges that exceed the specified duration.
1964
+ *
1965
+ * @default null
1966
+ *
1967
+ * @example
1968
+ * ```typescript
1969
+ * // Limit to 30 days maximum
1970
+ * const maxSpan = { days: 30 };
1971
+ *
1972
+ * // Limit to 3 months maximum
1973
+ * const maxSpan = { months: 3 };
1974
+ *
1975
+ * // Limit to 1 year maximum
1976
+ * const maxSpan = { years: 1 };
1977
+ * ```
1978
+ *
1979
+ * ```html
1980
+ * <ngx-datex [maxSpan]="maxSpan"></ngx-datex>
1981
+ * ```
1982
+ */
1983
+ maxSpan = input(null, ...(ngDevMode ? [{ debugName: "maxSpan" }] : []));
1984
+ /**
1985
+ * Whether to show week numbers in the calendar.
1986
+ * Displays ISO week numbers on the left side of each week row.
1987
+ *
1988
+ * @default false
1989
+ *
1990
+ * @example
1991
+ * ```html
1992
+ * <ngx-datex [showWeekNumbers]="true"></ngx-datex>
1993
+ * ```
1994
+ */
1995
+ showWeekNumbers = input(false, ...(ngDevMode ? [{ debugName: "showWeekNumbers" }] : []));
1996
+ /**
1997
+ * Whether to show ISO week numbers instead of standard week numbers.
1998
+ * Only applies when showWeekNumbers is true.
1999
+ *
2000
+ * @default false
2001
+ *
2002
+ * @example
2003
+ * ```html
2004
+ * <ngx-datex [showWeekNumbers]="true" [showISOWeekNumbers]="true"></ngx-datex>
2005
+ * ```
2006
+ */
2007
+ showISOWeekNumbers = input(false, ...(ngDevMode ? [{ debugName: "showISOWeekNumbers" }] : []));
2008
+ /**
2009
+ * CSS classes to apply to calendar buttons.
2010
+ * Used for styling navigation and action buttons.
2011
+ *
2012
+ * @default 'btn btn-sm'
2013
+ *
2014
+ * @example
2015
+ * ```html
2016
+ * <ngx-datex buttonClasses="btn btn-outline-primary btn-sm"></ngx-datex>
2017
+ * ```
2018
+ */
2019
+ buttonClasses = input('btn btn-sm', ...(ngDevMode ? [{ debugName: "buttonClasses" }] : []));
2020
+ /**
2021
+ * CSS classes to apply to the Apply button.
2022
+ * Combined with buttonClasses for the final styling.
2023
+ *
2024
+ * @default 'btn-success'
2025
+ *
2026
+ * @example
2027
+ * ```html
2028
+ * <ngx-datex applyButtonClasses="btn-primary"></ngx-datex>
2029
+ * ```
2030
+ */
2031
+ applyButtonClasses = input('btn-success', ...(ngDevMode ? [{ debugName: "applyButtonClasses" }] : []));
2032
+ /**
2033
+ * CSS classes to apply to the Cancel button.
2034
+ * Combined with buttonClasses for the final styling.
2035
+ *
2036
+ * @default 'btn-danger'
2037
+ *
2038
+ * @example
2039
+ * ```html
2040
+ * <ngx-datex cancelButtonClasses="btn-secondary"></ngx-datex>
2041
+ * ```
2042
+ */
2043
+ cancelButtonClasses = input('btn-danger', ...(ngDevMode ? [{ debugName: "cancelButtonClasses" }] : []));
2044
+ /**
2045
+ * Function to determine if a specific date should be disabled.
2046
+ * Return true to disable the date, false to enable it.
2047
+ *
2048
+ * @default null
2049
+ *
2050
+ * @example
2051
+ * ```typescript
2052
+ * // Disable weekends
2053
+ * const isInvalidDate = (date: Date): boolean => {
2054
+ * const day = date.getDay();
2055
+ * return day === 0 || day === 6; // Sunday or Saturday
2056
+ * };
2057
+ *
2058
+ * // Disable specific dates
2059
+ * const blackoutDates = [new Date('2024-12-25'), new Date('2024-01-01')];
2060
+ * const isInvalidDate = (date: Date): boolean => {
2061
+ * return blackoutDates.some(blackout =>
2062
+ * date.toDateString() === blackout.toDateString()
2063
+ * );
2064
+ * };
2065
+ * ```
2066
+ *
2067
+ * ```html
2068
+ * <ngx-datex [isInvalidDate]="isInvalidDate"></ngx-datex>
2069
+ * ```
2070
+ */
2071
+ isInvalidDate = input(null, ...(ngDevMode ? [{ debugName: "isInvalidDate" }] : []));
2072
+ /**
2073
+ * Function to apply custom CSS classes to specific dates.
2074
+ * Return a string or array of strings for CSS classes, or false for no custom styling.
2075
+ *
2076
+ * @default null
2077
+ *
2078
+ * @example
2079
+ * ```typescript
2080
+ * // Highlight holidays
2081
+ * const isCustomDate = (date: Date): string | string[] | false => {
2082
+ * const holidays = [new Date('2024-12-25'), new Date('2024-01-01')];
2083
+ * if (holidays.some(holiday => date.toDateString() === holiday.toDateString())) {
2084
+ * return 'holiday-date';
2085
+ * }
2086
+ * return false;
2087
+ * };
2088
+ *
2089
+ * // Multiple classes for different date types
2090
+ * const isCustomDate = (date: Date): string | string[] | false => {
2091
+ * if (date.getDay() === 0) return ['weekend', 'sunday'];
2092
+ * if (date.getDay() === 6) return ['weekend', 'saturday'];
2093
+ * return false;
2094
+ * };
2095
+ * ```
2096
+ *
2097
+ * ```html
2098
+ * <ngx-datex [isCustomDate]="isCustomDate"></ngx-datex>
2099
+ * ```
2100
+ */
2101
+ isCustomDate = input(null, ...(ngDevMode ? [{ debugName: "isCustomDate" }] : []));
2102
+ /**
2103
+ * The minimum year to show in the year dropdown.
2104
+ * Only applies when showDropdowns is true.
2105
+ *
2106
+ * @default Current year - 100
2107
+ *
2108
+ * @example
2109
+ * ```html
2110
+ * <ngx-datex [showDropdowns]="true" [minYear]="2020" [maxYear]="2030"></ngx-datex>
2111
+ * ```
2112
+ */
2113
+ minYear = input(new Date().getFullYear() - 100, ...(ngDevMode ? [{ debugName: "minYear" }] : []));
2114
+ /**
2115
+ * The maximum year to show in the year dropdown.
2116
+ * Only applies when showDropdowns is true.
2117
+ *
2118
+ * @default Current year + 100
2119
+ *
2120
+ * @example
2121
+ * ```html
2122
+ * <ngx-datex [showDropdowns]="true" [minYear]="2020" [maxYear]="2030"></ngx-datex>
2123
+ * ```
2124
+ */
2125
+ maxYear = input(new Date().getFullYear() + 100, ...(ngDevMode ? [{ debugName: "maxYear" }] : []));
2126
+ /**
2127
+ * Predefined date ranges that users can quickly select.
2128
+ * Displayed as a sidebar list for easy access to common date ranges.
2129
+ *
2130
+ * @default DEFAULT_RANGES
2131
+ *
2132
+ * @example
2133
+ * ```typescript
2134
+ * const customRanges = {
2135
+ * 'Today': [startOfDay(new Date()), endOfDay(new Date())],
2136
+ * 'Yesterday': [
2137
+ * startOfDay(subDays(new Date(), 1)),
2138
+ * endOfDay(subDays(new Date(), 1))
2139
+ * ],
2140
+ * 'Last 7 Days': [
2141
+ * startOfDay(subDays(new Date(), 6)),
2142
+ * endOfDay(new Date())
2143
+ * ],
2144
+ * 'Last 30 Days': [
2145
+ * startOfDay(subDays(new Date(), 29)),
2146
+ * endOfDay(new Date())
2147
+ * ],
2148
+ * 'This Month': [
2149
+ * startOfMonth(new Date()),
2150
+ * endOfMonth(new Date())
2151
+ * ]
2152
+ * };
2153
+ * ```
2154
+ *
2155
+ * ```html
2156
+ * <ngx-datex [ranges]="customRanges"></ngx-datex>
2157
+ * ```
2158
+ */
2159
+ ranges = input(DEFAULT_RANGES, ...(ngDevMode ? [{ debugName: "ranges" }] : []));
2160
+ // Positioning inputs
2161
+ /**
2162
+ * The horizontal alignment of the calendar overlay relative to the input.
2163
+ * Controls where the calendar appears horizontally.
2164
+ *
2165
+ * @default 'center'
2166
+ *
2167
+ * @example
2168
+ * ```html
2169
+ * <!-- Calendar opens to the left of the input -->
2170
+ * <ngx-datex opens="left"></ngx-datex>
2171
+ *
2172
+ * <!-- Calendar opens to the right of the input -->
2173
+ * <ngx-datex opens="right"></ngx-datex>
2174
+ *
2175
+ * <!-- Calendar opens centered on the input -->
2176
+ * <ngx-datex opens="center"></ngx-datex>
2177
+ * ```
2178
+ */
2179
+ opens = input('center', ...(ngDevMode ? [{ debugName: "opens" }] : []));
2180
+ /**
2181
+ * The vertical alignment of the calendar overlay relative to the input.
2182
+ * Controls where the calendar appears vertically.
2183
+ *
2184
+ * @default 'auto'
2185
+ *
2186
+ * @example
2187
+ * ```html
2188
+ * <!-- Calendar always opens below the input -->
2189
+ * <ngx-datex drops="down"></ngx-datex>
2190
+ *
2191
+ * <!-- Calendar always opens above the input -->
2192
+ * <ngx-datex drops="up"></ngx-datex>
2193
+ *
2194
+ * <!-- Calendar opens in the best available space -->
2195
+ * <ngx-datex drops="auto"></ngx-datex>
2196
+ * ```
2197
+ */
2198
+ drops = input('auto', ...(ngDevMode ? [{ debugName: "drops" }] : []));
2199
+ // Template inputs
2200
+ /**
2201
+ * Custom template for the calendar header.
2202
+ * Allows complete customization of the header area including navigation controls.
2203
+ *
2204
+ * @default null
2205
+ *
2206
+ * @example
2207
+ * ```html
2208
+ * <ngx-datex [headerTemplate]="customHeader">
2209
+ * </ngx-datex>
2210
+ *
2211
+ * <ng-template #customHeader>
2212
+ * <div class="custom-header">
2213
+ * <button (click)="previousMonth()">Previous</button>
2214
+ * <span>{{ currentMonth }}</span>
2215
+ * <button (click)="nextMonth()">Next</button>
2216
+ * </div>
2217
+ * </ng-template>
2218
+ * ```
2219
+ */
2220
+ headerTemplate = input(null, ...(ngDevMode ? [{ debugName: "headerTemplate" }] : []));
2221
+ /**
2222
+ * Custom template for the calendar footer.
2223
+ * Allows complete customization of the footer area including action buttons.
2224
+ *
2225
+ * @default null
2226
+ *
2227
+ * @example
2228
+ * ```html
2229
+ * <ngx-datex [footerTemplate]="customFooter">
2230
+ * </ngx-datex>
2231
+ *
2232
+ * <ng-template #customFooter>
2233
+ * <div class="custom-footer">
2234
+ * <button (click)="clearSelection()">Clear</button>
2235
+ * <button (click)="applySelection()">Apply</button>
2236
+ * </div>
2237
+ * </ng-template>
2238
+ * ```
2239
+ */
2240
+ footerTemplate = input(null, ...(ngDevMode ? [{ debugName: "footerTemplate" }] : []));
2241
+ /**
2242
+ * Custom template for individual calendar day cells.
2243
+ * Allows complete customization of how each date is displayed.
2244
+ *
2245
+ * @default null
2246
+ *
2247
+ * @example
2248
+ * ```html
2249
+ * <ngx-datex [dayTemplate]="customDay">
2250
+ * </ngx-datex>
2251
+ *
2252
+ * <ng-template #customDay let-date="date" let-isSelected="isSelected">
2253
+ * <div class="custom-day" [class.selected]="isSelected">
2254
+ * <span>{{ date.getDate() }}</span>
2255
+ * <small *ngIf="hasEvent(date)">•</small>
2256
+ * </div>
2257
+ * </ng-template>
2258
+ * ```
2259
+ */
2260
+ dayTemplate = input(null, ...(ngDevMode ? [{ debugName: "dayTemplate" }] : []));
2261
+ // Accessibility inputs
2262
+ /**
2263
+ * ARIA label for the date picker input.
2264
+ * Provides accessible description for screen readers.
2265
+ *
2266
+ * @default 'Date picker'
2267
+ *
2268
+ * @example
2269
+ * ```html
2270
+ * <ngx-datex ariaLabel="Select booking dates"></ngx-datex>
2271
+ * <ngx-datex ariaLabel="Choose event date range"></ngx-datex>
2272
+ * ```
2273
+ */
2274
+ ariaLabel = input('Date picker', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
2275
+ /**
2276
+ * ARIA described-by attribute for the date picker input.
2277
+ * References additional descriptive text for screen readers.
2278
+ *
2279
+ * @default ''
2280
+ *
2281
+ * @example
2282
+ * ```html
2283
+ * <ngx-datex ariaDescribedBy="date-help-text"></ngx-datex>
2284
+ * <div id="date-help-text">Select your preferred date range</div>
2285
+ * ```
2286
+ */
2287
+ ariaDescribedBy = input('', ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
2288
+ // Outputs
2289
+ /**
2290
+ * Emitted when the selected date or date range changes.
2291
+ * Provides the complete NgxDatexValue with start and end dates.
2292
+ *
2293
+ * @example
2294
+ * ```html
2295
+ * <ngx-datex (dateChange)="onDateChange($event)"></ngx-datex>
2296
+ * ```
2297
+ *
2298
+ * ```typescript
2299
+ * onDateChange(value: NgxDatexValue | null) {
2300
+ * if (value) {
2301
+ * console.log('Start:', value.startDate);
2302
+ * console.log('End:', value.endDate);
2303
+ * }
2304
+ * }
2305
+ * ```
2306
+ */
2307
+ dateChange = output();
2308
+ /**
2309
+ * Emitted when a date range is selected (not for single date picker).
2310
+ * Provides start and end dates separately for convenience.
2311
+ *
2312
+ * @example
2313
+ * ```html
2314
+ * <ngx-datex (rangeChange)="onRangeChange($event)"></ngx-datex>
2315
+ * ```
2316
+ *
2317
+ * ```typescript
2318
+ * onRangeChange(range: { startDate: Date; endDate: Date | null }) {
2319
+ * console.log('Range selected:', range.startDate, 'to', range.endDate);
2320
+ * }
2321
+ * ```
2322
+ */
2323
+ rangeChange = output();
2324
+ /**
2325
+ * Emitted when the calendar overlay is opened.
2326
+ *
2327
+ * @example
2328
+ * ```html
2329
+ * <ngx-datex (openEvent)="onCalendarOpen()"></ngx-datex>
2330
+ * ```
2331
+ *
2332
+ * ```typescript
2333
+ * onCalendarOpen() {
2334
+ * console.log('Calendar opened');
2335
+ * // Perform actions when calendar opens
2336
+ * }
2337
+ * ```
2338
+ */
2339
+ openEvent = output();
2340
+ /**
2341
+ * Emitted when the calendar overlay is closed.
2342
+ *
2343
+ * @example
2344
+ * ```html
2345
+ * <ngx-datex (closeEvent)="onCalendarClose()"></ngx-datex>
2346
+ * ```
2347
+ *
2348
+ * ```typescript
2349
+ * onCalendarClose() {
2350
+ * console.log('Calendar closed');
2351
+ * // Perform cleanup or validation
2352
+ * }
2353
+ * ```
2354
+ */
2355
+ closeEvent = output();
2356
+ /**
2357
+ * Emitted when the displayed month changes in the calendar.
2358
+ *
2359
+ * @example
2360
+ * ```html
2361
+ * <ngx-datex (monthChange)="onMonthChange($event)"></ngx-datex>
2362
+ * ```
2363
+ *
2364
+ * ```typescript
2365
+ * onMonthChange(date: Date) {
2366
+ * console.log('Month changed to:', date.getMonth() + 1, date.getFullYear());
2367
+ * }
2368
+ * ```
2369
+ */
2370
+ monthChange = output();
2371
+ /**
2372
+ * Emitted when the displayed year changes in the calendar.
2373
+ *
2374
+ * @example
2375
+ * ```html
2376
+ * <ngx-datex (yearChange)="onYearChange($event)"></ngx-datex>
2377
+ * ```
2378
+ *
2379
+ * ```typescript
2380
+ * onYearChange(year: number) {
2381
+ * console.log('Year changed to:', year);
2382
+ * }
2383
+ * ```
2384
+ */
2385
+ yearChange = output();
2386
+ /**
2387
+ * Emitted when a date is hovered in the calendar.
2388
+ * Useful for showing preview information or highlighting date ranges.
2389
+ *
2390
+ * @example
2391
+ * ```html
2392
+ * <ngx-datex (dateHover)="onDateHover($event)"></ngx-datex>
2393
+ * ```
2394
+ *
2395
+ * ```typescript
2396
+ * onDateHover(date: Date) {
2397
+ * console.log('Hovering over:', date);
2398
+ * // Show preview or update UI
2399
+ * }
2400
+ * ```
2401
+ */
2402
+ dateHover = output();
2403
+ /**
2404
+ * Emitted when a validation error occurs.
2405
+ * Provides error message and optional error code for handling.
2406
+ *
2407
+ * @example
2408
+ * ```html
2409
+ * <ngx-datex (validationError)="onValidationError($event)"></ngx-datex>
2410
+ * ```
2411
+ *
2412
+ * ```typescript
2413
+ * onValidationError(error: { error: string; errorCode?: string }) {
2414
+ * console.error('Validation error:', error.error);
2415
+ * if (error.errorCode) {
2416
+ * // Handle specific error types
2417
+ * switch (error.errorCode) {
2418
+ * case 'MIN_DATE':
2419
+ * // Handle minimum date error
2420
+ * break;
2421
+ * case 'MAX_DATE':
2422
+ * // Handle maximum date error
2423
+ * break;
2424
+ * }
2425
+ * }
2426
+ * }
2427
+ * ```
2428
+ */
2429
+ validationError = output();
2430
+ /**
2431
+ * Emitted when the checkbox state changes (if showCheckbox is enabled).
2432
+ *
2433
+ * @example
2434
+ * ```html
2435
+ * <ngx-datex [showCheckbox]="true" (checkboxChange)="onCheckboxChange($event)"></ngx-datex>
2436
+ * ```
2437
+ *
2438
+ * ```typescript
2439
+ * onCheckboxChange(checked: boolean) {
2440
+ * console.log('Checkbox is now:', checked ? 'checked' : 'unchecked');
2441
+ * }
2442
+ * ```
2443
+ */
2444
+ checkboxChange = output();
2445
+ // Internal state signals
2446
+ _isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : []));
2447
+ _currentValue = signal(null, ...(ngDevMode ? [{ debugName: "_currentValue" }] : []));
2448
+ _leftCalendarMonth = signal(new Date(), ...(ngDevMode ? [{ debugName: "_leftCalendarMonth" }] : []));
2449
+ _rightCalendarMonth = signal(new Date(), ...(ngDevMode ? [{ debugName: "_rightCalendarMonth" }] : []));
2450
+ _hoverDate = signal(null, ...(ngDevMode ? [{ debugName: "_hoverDate" }] : []));
2451
+ _errorMessage = signal('', ...(ngDevMode ? [{ debugName: "_errorMessage" }] : []));
2452
+ _startTime = signal({ hour: 0, minute: 0, ampm: 'AM' }, ...(ngDevMode ? [{ debugName: "_startTime" }] : []));
2453
+ _endTime = signal({ hour: 0, minute: 0, ampm: 'AM' }, ...(ngDevMode ? [{ debugName: "_endTime" }] : []));
2454
+ _overlayPosition = signal(null, ...(ngDevMode ? [{ debugName: "_overlayPosition" }] : []));
2455
+ _inputFocused = signal(false, ...(ngDevMode ? [{ debugName: "_inputFocused" }] : []));
2456
+ _displayValue = signal('', ...(ngDevMode ? [{ debugName: "_displayValue" }] : []));
2457
+ _checkboxValue = signal(false, ...(ngDevMode ? [{ debugName: "_checkboxValue" }] : []));
2458
+ _leftCalendarMatrix = signal([], ...(ngDevMode ? [{ debugName: "_leftCalendarMatrix" }] : []));
2459
+ _rightCalendarMatrix = signal([], ...(ngDevMode ? [{ debugName: "_rightCalendarMatrix" }] : []));
2460
+ // Internal date properties following vanilla daterangepicker pattern
2461
+ _internalStartDate = new Date();
2462
+ _internalEndDate = null;
2463
+ oldStartDate = null;
2464
+ oldEndDate = null;
2465
+ previousRightTime = null;
2466
+ // Public getters for current dates
2467
+ get currentStartDate() {
2468
+ return this._internalStartDate;
2469
+ }
2470
+ set currentStartDate(value) {
2471
+ this._internalStartDate = value;
2472
+ }
2473
+ get currentEndDate() {
2474
+ return this._internalEndDate;
2475
+ }
2476
+ set currentEndDate(value) {
2477
+ this._internalEndDate = value;
2478
+ }
2479
+ // Computed signals for template
2480
+ isOpen = this._isOpen.asReadonly();
2481
+ currentValue = this._currentValue.asReadonly();
2482
+ displayValue = this._displayValue.asReadonly();
2483
+ hasStartDate = computed(() => !!this.currentStartDate, ...(ngDevMode ? [{ debugName: "hasStartDate" }] : []));
2484
+ hasEndDate = computed(() => !!this.currentEndDate, ...(ngDevMode ? [{ debugName: "hasEndDate" }] : []));
2485
+ leftCalendarMonth = this._leftCalendarMonth.asReadonly();
2486
+ rightCalendarMonth = this._rightCalendarMonth.asReadonly();
2487
+ leftCalendarMatrix = this._leftCalendarMatrix.asReadonly();
2488
+ rightCalendarMatrix = this._rightCalendarMatrix.asReadonly();
2489
+ errorMessage = this._errorMessage.asReadonly();
2490
+ hasError = computed(() => this._errorMessage().length > 0, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
2491
+ checkboxValue = this._checkboxValue.asReadonly();
2492
+ // Calendar computed values
2493
+ leftCalendarMonthValue = computed(() => this._leftCalendarMonth().getMonth(), ...(ngDevMode ? [{ debugName: "leftCalendarMonthValue" }] : []));
2494
+ leftCalendarYearValue = computed(() => this._leftCalendarMonth().getFullYear(), ...(ngDevMode ? [{ debugName: "leftCalendarYearValue" }] : []));
2495
+ rightCalendarMonthValue = computed(() => this._rightCalendarMonth().getMonth(), ...(ngDevMode ? [{ debugName: "rightCalendarMonthValue" }] : []));
2496
+ rightCalendarYearValue = computed(() => this._rightCalendarMonth().getFullYear(), ...(ngDevMode ? [{ debugName: "rightCalendarYearValue" }] : []));
2497
+ // Locale and theme computed values
2498
+ monthNames = computed(() => this.locale().monthNames || SPANISH_LOCALE.monthNames || [], ...(ngDevMode ? [{ debugName: "monthNames" }] : []));
2499
+ daysOfWeek = computed(() => {
2500
+ const days = this.locale().daysOfWeek || SPANISH_LOCALE.daysOfWeek || [];
2501
+ return days.slice(0, 7);
2502
+ }, ...(ngDevMode ? [{ debugName: "daysOfWeek" }] : []));
2503
+ availableYears = computed(() => {
2504
+ const years = [];
2505
+ const minYear = this.minYear();
2506
+ const maxYear = this.maxYear();
2507
+ for (let year = minYear; year <= maxYear; year++) {
2508
+ years.push(year);
2509
+ }
2510
+ return years;
2511
+ }, ...(ngDevMode ? [{ debugName: "availableYears" }] : []));
2512
+ // Range computed values
2513
+ rangeEntries = computed(() => {
2514
+ const ranges = this.ranges();
2515
+ return Object.entries(ranges).map(([label, range]) => ({ label, range }));
2516
+ }, ...(ngDevMode ? [{ debugName: "rangeEntries" }] : []));
2517
+ showRanges = computed(() => {
2518
+ const ranges = this.ranges();
2519
+ return ranges && Object.keys(ranges).length > 0;
2520
+ }, ...(ngDevMode ? [{ debugName: "showRanges" }] : []));
2521
+ // Device and UI computed values
2522
+ isMobileDevice = computed(() => isPlatformBrowser(this.platformId) && isMobileDevice(), ...(ngDevMode ? [{ debugName: "isMobileDevice" }] : []));
2523
+ headerTitle = computed(() => this.singleDatePicker() ? 'Seleccionar fecha' : 'Seleccionar rango de fechas', ...(ngDevMode ? [{ debugName: "headerTitle" }] : []));
2524
+ canApplyValue = computed(() => {
2525
+ const startDate = this.currentStartDate;
2526
+ const endDate = this.currentEndDate;
2527
+ if (!startDate)
2528
+ return false;
2529
+ if (this.singleDatePicker())
2530
+ return !!startDate;
2531
+ return !!(startDate && endDate);
2532
+ }, ...(ngDevMode ? [{ debugName: "canApplyValue" }] : []));
2533
+ // Arrow positioning computed values
2534
+ arrowDirection = computed(() => {
2535
+ const position = this._overlayPosition();
2536
+ if (!position)
2537
+ return 'down';
2538
+ if (position.originY === 'top' && position.overlayY === 'bottom') {
2539
+ return 'up';
2540
+ }
2541
+ else if (position.originY === 'bottom' && position.overlayY === 'top') {
2542
+ return 'down';
2543
+ }
2544
+ else if (position.originX === 'end' && position.overlayX === 'start') {
2545
+ return 'left';
2546
+ }
2547
+ else if (position.originX === 'start' && position.overlayX === 'end') {
2548
+ return 'right';
2549
+ }
2550
+ return 'down';
2551
+ }, ...(ngDevMode ? [{ debugName: "arrowDirection" }] : []));
2552
+ arrowAlignment = computed(() => {
2553
+ const position = this._overlayPosition();
2554
+ if (!position)
2555
+ return 'center';
2556
+ if (this.arrowDirection() === 'up' || this.arrowDirection() === 'down') {
2557
+ if (position.overlayX === 'start')
2558
+ return 'start';
2559
+ if (position.overlayX === 'end')
2560
+ return 'end';
2561
+ return 'center';
2562
+ }
2563
+ else {
2564
+ if (position.overlayY === 'top')
2565
+ return 'start';
2566
+ if (position.overlayY === 'bottom')
2567
+ return 'end';
2568
+ return 'center';
2569
+ }
2570
+ }, ...(ngDevMode ? [{ debugName: "arrowAlignment" }] : []));
2571
+ // Formatted range display
2572
+ formattedSelectedRange = computed(() => {
2573
+ const startDate = this.currentStartDate;
2574
+ const endDate = this.currentEndDate;
2575
+ if (!startDate)
2576
+ return '';
2577
+ const format = this.locale().format || 'DD/MM/YYYY';
2578
+ const timeFormat = this.timePicker() ? (this.timePicker24Hour() ? ' HH:mm' : ' hh:mm A') : '';
2579
+ const fullFormat = format + timeFormat;
2580
+ const start = formatDateValue(startDate, fullFormat);
2581
+ if (this.singleDatePicker()) {
2582
+ return start;
2583
+ }
2584
+ if (!endDate) {
2585
+ return start;
2586
+ }
2587
+ const end = formatDateValue(endDate, fullFormat);
2588
+ return `${start} - ${end}`;
2589
+ }, ...(ngDevMode ? [{ debugName: "formattedSelectedRange" }] : []));
2590
+ // Current label for selected range
2591
+ currentLabel = computed(() => {
2592
+ const startDate = this.currentStartDate;
2593
+ const endDate = this.currentEndDate;
2594
+ if (!startDate)
2595
+ return '';
2596
+ if (!endDate)
2597
+ return '';
2598
+ let customRange = true;
2599
+ const ranges = this.ranges();
2600
+ for (const [label, [rangeStart, rangeEnd]] of Object.entries(ranges)) {
2601
+ let startMatches;
2602
+ let endMatches;
2603
+ if (this.timePicker()) {
2604
+ const format = this.timePickerSeconds() ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm';
2605
+ const startFormatted = formatDateValue(startDate, format);
2606
+ const endFormatted = formatDateValue(endDate, format);
2607
+ const rangeStartFormatted = formatDateValue(rangeStart, format);
2608
+ const rangeEndFormatted = formatDateValue(rangeEnd, format);
2609
+ startMatches = startFormatted === rangeStartFormatted;
2610
+ endMatches = endFormatted === rangeEndFormatted;
2611
+ }
2612
+ else {
2613
+ startMatches = isSameDate(startDate, rangeStart, 'day');
2614
+ endMatches = isSameDate(endDate, rangeEnd, 'day');
2615
+ }
2616
+ if (startMatches && endMatches) {
2617
+ customRange = false;
2618
+ return label;
2619
+ }
2620
+ }
2621
+ if (customRange) {
2622
+ return this.locale().customRangeLabel || 'Rango Personalizado';
2623
+ }
2624
+ return '';
2625
+ }, ...(ngDevMode ? [{ debugName: "currentLabel" }] : []));
2626
+ isCustomRange = computed(() => {
2627
+ const startDate = this.currentStartDate;
2628
+ const endDate = this.currentEndDate;
2629
+ if (!startDate || !endDate)
2630
+ return false;
2631
+ const ranges = this.ranges();
2632
+ for (const [rangeStart, rangeEnd] of Object.values(ranges)) {
2633
+ let startMatches;
2634
+ let endMatches;
2635
+ if (this.timePicker()) {
2636
+ const format = this.timePickerSeconds() ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm';
2637
+ const startFormatted = formatDateValue(startDate, format);
2638
+ const endFormatted = formatDateValue(endDate, format);
2639
+ const rangeStartFormatted = formatDateValue(rangeStart, format);
2640
+ const rangeEndFormatted = formatDateValue(rangeEnd, format);
2641
+ startMatches = startFormatted === rangeStartFormatted;
2642
+ endMatches = endFormatted === rangeEndFormatted;
2643
+ }
2644
+ else {
2645
+ startMatches = isSameDate(startDate, rangeStart, 'day');
2646
+ endMatches = isSameDate(endDate, rangeEnd, 'day');
2647
+ }
2648
+ if (startMatches && endMatches) {
2649
+ return false;
2650
+ }
2651
+ }
2652
+ return true;
2653
+ }, ...(ngDevMode ? [{ debugName: "isCustomRange" }] : []));
2654
+ // Time picker computed values
2655
+ selectedStartHour = computed(() => this._startTime().hour, ...(ngDevMode ? [{ debugName: "selectedStartHour" }] : []));
2656
+ selectedStartMinute = computed(() => this._startTime().minute, ...(ngDevMode ? [{ debugName: "selectedStartMinute" }] : []));
2657
+ selectedStartAmPm = computed(() => this._startTime().ampm, ...(ngDevMode ? [{ debugName: "selectedStartAmPm" }] : []));
2658
+ selectedEndHour = computed(() => this._endTime().hour, ...(ngDevMode ? [{ debugName: "selectedEndHour" }] : []));
2659
+ selectedEndMinute = computed(() => this._endTime().minute, ...(ngDevMode ? [{ debugName: "selectedEndMinute" }] : []));
2660
+ selectedEndAmPm = computed(() => this._endTime().ampm, ...(ngDevMode ? [{ debugName: "selectedEndAmPm" }] : []));
2661
+ hours24 = computed(() => Array.from({ length: 24 }, (_, i) => i), ...(ngDevMode ? [{ debugName: "hours24" }] : []));
2662
+ hours12 = computed(() => Array.from({ length: 12 }, (_, i) => i + 1), ...(ngDevMode ? [{ debugName: "hours12" }] : []));
2663
+ minutes = computed(() => {
2664
+ const increment = this.timePickerIncrement();
2665
+ const minutes = [];
2666
+ for (let i = 0; i < 60; i += increment) {
2667
+ minutes.push(i);
2668
+ }
2669
+ return minutes;
2670
+ }, ...(ngDevMode ? [{ debugName: "minutes" }] : []));
2671
+ // Available time options
2672
+ availableStartHours = computed(() => {
2673
+ return this.timePickerService.getAvailableHours(this.currentStartDate, this.minDate(), this.maxDate(), this.timePicker24Hour(), this._startTime(), false);
2674
+ }, ...(ngDevMode ? [{ debugName: "availableStartHours" }] : []));
2675
+ availableEndHours = computed(() => {
2676
+ return this.timePickerService.getAvailableHours(this.currentEndDate, this.minDate(), this.maxDate(), this.timePicker24Hour(), this._endTime(), true, this.currentStartDate);
2677
+ }, ...(ngDevMode ? [{ debugName: "availableEndHours" }] : []));
2678
+ availableStartMinutes = computed(() => {
2679
+ return this.timePickerService.getAvailableMinutes(this.currentStartDate, this.minDate(), this.maxDate(), this._startTime(), this.timePickerIncrement(), this.timePicker24Hour(), false);
2680
+ }, ...(ngDevMode ? [{ debugName: "availableStartMinutes" }] : []));
2681
+ availableEndMinutes = computed(() => {
2682
+ return this.timePickerService.getAvailableMinutes(this.currentEndDate, this.minDate(), this.maxDate(), this._endTime(), this.timePickerIncrement(), this.timePicker24Hour(), true, this.currentStartDate);
2683
+ }, ...(ngDevMode ? [{ debugName: "availableEndMinutes" }] : []));
2684
+ // ControlValueAccessor implementation
2685
+ onChange = () => {
2686
+ // Implementation provided by registerOnChange
2687
+ };
2688
+ onTouched = () => {
2689
+ // Implementation provided by registerOnTouched
2690
+ };
2691
+ constructor() {
2692
+ afterNextRender(() => {
2693
+ this.initializeComponent();
2694
+ });
2695
+ }
2696
+ ngOnInit() {
2697
+ this.datexService.updateConfig(this.config());
2698
+ this.datexService.setLocale(this.locale());
2699
+ }
2700
+ ngOnDestroy() {
2701
+ this._isOpen.set(false);
2702
+ this.overlayService.disposeOverlay();
2703
+ }
2704
+ // ControlValueAccessor methods
2705
+ writeValue(value) {
2706
+ if (value) {
2707
+ this.currentStartDate = new Date(value.startDate);
2708
+ this.currentEndDate = value.endDate ? new Date(value.endDate) : null;
2709
+ this._currentValue.set(value);
2710
+ if (value.startDate) {
2711
+ this.updateMonthsInView();
2712
+ }
2713
+ if (this.timePicker()) {
2714
+ this.updateTimeSignalsFromDate(value.startDate, 'start');
2715
+ if (value.endDate) {
2716
+ this.updateTimeSignalsFromDate(value.endDate, 'end');
2717
+ }
2718
+ }
2719
+ this.updateElement();
2720
+ }
2721
+ else {
2722
+ this.initializeDefaultDates();
2723
+ }
2724
+ }
2725
+ registerOnChange(fn) {
2726
+ this.onChange = fn;
2727
+ }
2728
+ registerOnTouched(fn) {
2729
+ this.onTouched = fn;
2730
+ }
2731
+ setDisabledState(isDisabled) {
2732
+ // Implementation for disabled state
2733
+ void isDisabled;
2734
+ }
2735
+ // Public methods
2736
+ /**
2737
+ * Toggles the calendar visibility.
2738
+ * Opens the calendar if closed, closes it if open.
2739
+ * Does nothing if the component is disabled or readonly.
2740
+ *
2741
+ * @example
2742
+ * ```typescript
2743
+ * // In component
2744
+ * @ViewChild(NgxDatex) datePicker!: NgxDatex;
2745
+ *
2746
+ * togglePicker() {
2747
+ * this.datePicker.toggle();
2748
+ * }
2749
+ * ```
2750
+ */
2751
+ toggle() {
2752
+ if (this.disabled() || this.readonly())
2753
+ return;
2754
+ if (this._isOpen()) {
2755
+ this.closeCalendar();
2756
+ }
2757
+ else {
2758
+ this.openCalendar();
2759
+ }
2760
+ }
2761
+ /**
2762
+ * Opens the calendar overlay.
2763
+ * Saves current state for potential cancellation and initializes the calendar view.
2764
+ *
2765
+ * @example
2766
+ * ```typescript
2767
+ * // Programmatically open the calendar
2768
+ * this.datePicker.openCalendar();
2769
+ * ```
2770
+ */
2771
+ openCalendar() {
2772
+ if (this.disabled() || this.readonly() || this._isOpen())
2773
+ return;
2774
+ // Save old values for potential cancellation
2775
+ this.oldStartDate = new Date(this.currentStartDate);
2776
+ this.oldEndDate = this.currentEndDate ? new Date(this.currentEndDate) : null;
2777
+ this.previousRightTime = this.currentEndDate ? new Date(this.currentEndDate) : null;
2778
+ this.createOverlay();
2779
+ this._isOpen.set(true);
2780
+ if (this.timePicker()) {
2781
+ this.updateTimeSignalsFromDate(this.currentStartDate, 'start');
2782
+ if (this.currentEndDate) {
2783
+ this.updateTimeSignalsFromDate(this.currentEndDate, 'end');
2784
+ }
2785
+ }
2786
+ this.updateView();
2787
+ this.openEvent.emit();
2788
+ }
2789
+ /**
2790
+ * Closes the calendar overlay.
2791
+ * Handles incomplete selections and emits change events if values have changed.
2792
+ *
2793
+ * @example
2794
+ * ```typescript
2795
+ * // Programmatically close the calendar
2796
+ * this.datePicker.closeCalendar();
2797
+ * ```
2798
+ */
2799
+ closeCalendar() {
2800
+ this._isOpen.set(false);
2801
+ this._hoverDate.set(null);
2802
+ // Handle incomplete selection
2803
+ if (!this.currentEndDate && this.oldStartDate && this.oldEndDate) {
2804
+ this.currentStartDate = this.oldStartDate;
2805
+ this.currentEndDate = this.oldEndDate;
2806
+ this.updateCurrentValue();
2807
+ }
2808
+ if (this.oldStartDate && this.oldEndDate) {
2809
+ const hasChanges = !isSameDate(this.currentStartDate, this.oldStartDate, 'day') ||
2810
+ (this.currentEndDate && !isSameDate(this.currentEndDate, this.oldEndDate, 'day'));
2811
+ if (hasChanges && this.currentEndDate) {
2812
+ this.emitValueChange();
2813
+ }
2814
+ }
2815
+ this.updateElement();
2816
+ this.overlayService.closeOverlay();
2817
+ this.closeEvent.emit();
2818
+ }
2819
+ /**
2820
+ * Applies the current selection and closes the calendar.
2821
+ * Only works if there's a valid selection to apply.
2822
+ *
2823
+ * @example
2824
+ * ```typescript
2825
+ * // Apply current selection programmatically
2826
+ * if (this.datePicker.canApplyValue()) {
2827
+ * this.datePicker.apply();
2828
+ * }
2829
+ * ```
2830
+ */
2831
+ apply() {
2832
+ if (!this.canApplyValue())
2833
+ return;
2834
+ this.updateElement();
2835
+ this.closeCalendar();
2836
+ }
2837
+ /**
2838
+ * Cancels the current selection and reverts to the previous values.
2839
+ * Closes the calendar without applying changes.
2840
+ *
2841
+ * @example
2842
+ * ```typescript
2843
+ * // Cancel current selection
2844
+ * this.datePicker.cancel();
2845
+ * ```
2846
+ */
2847
+ cancel() {
2848
+ if (this.oldStartDate && this.oldEndDate) {
2849
+ this.currentStartDate = this.oldStartDate;
2850
+ this.currentEndDate = this.oldEndDate;
2851
+ this.updateCurrentValue();
2852
+ }
2853
+ this.closeCalendar();
2854
+ }
2855
+ /**
2856
+ * Selects a specific date in the calendar.
2857
+ * Handles both single date and range selection logic.
2858
+ *
2859
+ * @param date - The date to select
2860
+ *
2861
+ * @example
2862
+ * ```typescript
2863
+ * // Programmatically select a date
2864
+ * const today = new Date();
2865
+ * this.datePicker.selectDate(today);
2866
+ * ```
2867
+ */
2868
+ selectDate(date) {
2869
+ if (this.isDisabled(date))
2870
+ return;
2871
+ const validation = this.datexService.validateDate(date);
2872
+ if (!validation.isValid) {
2873
+ this._errorMessage.set(validation.error || '');
2874
+ this.validationError.emit({
2875
+ error: validation.error || '',
2876
+ errorCode: validation.errorCode,
2877
+ });
2878
+ return;
2879
+ }
2880
+ this._errorMessage.set('');
2881
+ this._hoverDate.set(null);
2882
+ if (this.singleDatePicker()) {
2883
+ this.setStartDate(date);
2884
+ this.setEndDate(date);
2885
+ if (this.effectiveAutoApply()) {
2886
+ this.emitValueChange();
2887
+ }
2888
+ if (!this.timePicker() && this.effectiveAutoApply()) {
2889
+ this.closeCalendar();
2890
+ }
2891
+ }
2892
+ else {
2893
+ // Range selection logic following vanilla daterangepicker
2894
+ if (this.currentEndDate ||
2895
+ (this.currentStartDate && isBeforeDate(date, this.currentStartDate))) {
2896
+ // Picking start
2897
+ const startDate = new Date(date);
2898
+ if (this.timePicker()) {
2899
+ const startTime = this._startTime();
2900
+ let hour = startTime.hour;
2901
+ if (!this.timePicker24Hour()) {
2902
+ if (startTime.ampm === 'PM' && hour < 12)
2903
+ hour += 12;
2904
+ if (startTime.ampm === 'AM' && hour === 12)
2905
+ hour = 0;
2906
+ }
2907
+ startDate.setHours(hour, startTime.minute, 0, 0);
2908
+ }
2909
+ this.currentEndDate = null;
2910
+ this.setStartDate(startDate);
2911
+ }
2912
+ else {
2913
+ // Picking end
2914
+ const endDate = new Date(date);
2915
+ if (this.timePicker()) {
2916
+ const endTime = this._endTime();
2917
+ let hour = endTime.hour;
2918
+ if (!this.timePicker24Hour()) {
2919
+ if (endTime.ampm === 'PM' && hour < 12)
2920
+ hour += 12;
2921
+ if (endTime.ampm === 'AM' && hour === 12)
2922
+ hour = 0;
2923
+ }
2924
+ endDate.setHours(hour, endTime.minute, 0, 0);
2925
+ }
2926
+ this.setEndDate(endDate);
2927
+ if (this.effectiveAutoApply()) {
2928
+ this.emitValueChange();
2929
+ this.closeCalendar();
2930
+ }
2931
+ }
2932
+ }
2933
+ this.updateView();
2934
+ }
2935
+ /**
2936
+ * Selects a predefined date range by label.
2937
+ *
2938
+ * @param label - The label of the range to select (must exist in ranges configuration)
2939
+ *
2940
+ * @example
2941
+ * ```typescript
2942
+ * // Select "Last 7 days" range
2943
+ * this.datePicker.selectRange('Last 7 days');
2944
+ * ```
2945
+ */
2946
+ selectRange(label) {
2947
+ const ranges = this.ranges();
2948
+ const range = ranges[label];
2949
+ if (!range)
2950
+ return;
2951
+ const [rangeStart, rangeEnd] = range;
2952
+ this.currentStartDate = startOfDay(new Date(rangeStart));
2953
+ this.currentEndDate = endOfDay(new Date(rangeEnd));
2954
+ this.updateCurrentValue();
2955
+ this.updateMonthsInView();
2956
+ this.apply();
2957
+ }
2958
+ selectCustomRange() {
2959
+ // Custom range selection logic - mainly visual
2960
+ }
2961
+ // Event handlers
2962
+ onInputClick() {
2963
+ if (this.readonly())
2964
+ return;
2965
+ if (this._inputFocused() && this._isOpen())
2966
+ return;
2967
+ if (!this._isOpen()) {
2968
+ this.openCalendar();
2969
+ }
2970
+ }
2971
+ onInputFocus() {
2972
+ this._inputFocused.set(true);
2973
+ this.onTouched();
2974
+ }
2975
+ onInputBlur() {
2976
+ this._inputFocused.set(false);
2977
+ this.elementChanged();
2978
+ }
2979
+ onInputKeydown(event) {
2980
+ if (event.key === 'Enter' || event.key === ' ') {
2981
+ event.preventDefault();
2982
+ this.toggle();
2983
+ }
2984
+ else if (event.key === 'Escape') {
2985
+ if (!this._inputFocused() || (this._inputFocused() && this._isOpen())) {
2986
+ this.closeCalendar();
2987
+ }
2988
+ }
2989
+ else if (event.key === 'Tab' || event.key === 'Enter') {
2990
+ if (this._isOpen()) {
2991
+ this.closeCalendar();
2992
+ }
2993
+ }
2994
+ }
2995
+ onInputKeyup() {
2996
+ this.elementChanged();
2997
+ }
2998
+ onDateHover(date) {
2999
+ if (!this.currentEndDate && !this.singleDatePicker() && this.currentStartDate) {
3000
+ if (!isBeforeDate(date, this.currentStartDate)) {
3001
+ this._hoverDate.set(date);
3002
+ this.dateHover.emit(date);
3003
+ }
3004
+ }
3005
+ }
3006
+ onCalendarMouseLeave() {
3007
+ this._hoverDate.set(null);
3008
+ }
3009
+ onCalendarKeydown(event) {
3010
+ if (event.key === 'Escape' && !this._inputFocused()) {
3011
+ this.closeCalendar();
3012
+ }
3013
+ }
3014
+ onCheckboxChange(checked) {
3015
+ this._checkboxValue.set(checked);
3016
+ this.checkboxChange.emit(checked);
3017
+ }
3018
+ // Calendar navigation
3019
+ previousMonth(calendar) {
3020
+ if (calendar === 'left') {
3021
+ const current = this._leftCalendarMonth();
3022
+ const newMonth = addMonths(current, -1);
3023
+ this._leftCalendarMonth.set(newMonth);
3024
+ this.updateLeftCalendarMatrix();
3025
+ if (this.linkedCalendars() && !this.singleDatePicker()) {
3026
+ const rightMonth = addMonths(newMonth, 1);
3027
+ this._rightCalendarMonth.set(rightMonth);
3028
+ this.updateRightCalendarMatrix();
3029
+ }
3030
+ }
3031
+ else {
3032
+ const current = this._rightCalendarMonth();
3033
+ const newMonth = addMonths(current, -1);
3034
+ this._rightCalendarMonth.set(newMonth);
3035
+ this.updateRightCalendarMatrix();
3036
+ if (this.linkedCalendars()) {
3037
+ const leftMonth = addMonths(newMonth, -1);
3038
+ this._leftCalendarMonth.set(leftMonth);
3039
+ this.updateLeftCalendarMatrix();
3040
+ }
3041
+ }
3042
+ }
3043
+ nextMonth(calendar) {
3044
+ if (calendar === 'left') {
3045
+ const current = this._leftCalendarMonth();
3046
+ const newMonth = addMonths(current, 1);
3047
+ this._leftCalendarMonth.set(newMonth);
3048
+ this.updateLeftCalendarMatrix();
3049
+ if (this.linkedCalendars() && !this.singleDatePicker()) {
3050
+ const rightMonth = addMonths(newMonth, 1);
3051
+ this._rightCalendarMonth.set(rightMonth);
3052
+ this.updateRightCalendarMatrix();
3053
+ }
3054
+ }
3055
+ else {
3056
+ const current = this._rightCalendarMonth();
3057
+ const newMonth = addMonths(current, 1);
3058
+ this._rightCalendarMonth.set(newMonth);
3059
+ this.updateRightCalendarMatrix();
3060
+ if (this.linkedCalendars()) {
3061
+ const leftMonth = addMonths(newMonth, -1);
3062
+ this._leftCalendarMonth.set(leftMonth);
3063
+ this.updateLeftCalendarMatrix();
3064
+ }
3065
+ }
3066
+ }
3067
+ // Navigation methods
3068
+ canNavigatePrevious(calendar) {
3069
+ const month = calendar === 'left' ? this._leftCalendarMonth() : this._rightCalendarMonth();
3070
+ const minDate = this.minDate();
3071
+ if (minDate) {
3072
+ return month > minDate;
3073
+ }
3074
+ return true;
3075
+ }
3076
+ canNavigateNext(calendar) {
3077
+ const month = calendar === 'left' ? this._leftCalendarMonth() : this._rightCalendarMonth();
3078
+ const maxDate = this.maxDate();
3079
+ if (maxDate) {
3080
+ return month < maxDate;
3081
+ }
3082
+ return true;
3083
+ }
3084
+ onMonthChange(calendar, event) {
3085
+ const target = event.target;
3086
+ const monthIndex = parseInt(target.value, 10);
3087
+ if (calendar === 'left') {
3088
+ const current = this._leftCalendarMonth();
3089
+ let newMonth = new Date(current.getFullYear(), monthIndex, 1);
3090
+ const minDate = this.minDate();
3091
+ const maxDate = this.maxDate();
3092
+ if (minDate &&
3093
+ (newMonth.getFullYear() < minDate.getFullYear() ||
3094
+ (newMonth.getFullYear() === minDate.getFullYear() && monthIndex < minDate.getMonth()))) {
3095
+ newMonth = new Date(minDate.getFullYear(), minDate.getMonth(), 1);
3096
+ }
3097
+ if (maxDate &&
3098
+ (newMonth.getFullYear() > maxDate.getFullYear() ||
3099
+ (newMonth.getFullYear() === maxDate.getFullYear() && monthIndex > maxDate.getMonth()))) {
3100
+ newMonth = new Date(maxDate.getFullYear(), maxDate.getMonth(), 1);
3101
+ }
3102
+ this._leftCalendarMonth.set(newMonth);
3103
+ this.updateLeftCalendarMatrix();
3104
+ if (this.linkedCalendars() && !this.singleDatePicker()) {
3105
+ const rightMonth = addMonths(newMonth, 1);
3106
+ this._rightCalendarMonth.set(rightMonth);
3107
+ this.updateRightCalendarMatrix();
3108
+ }
3109
+ }
3110
+ else {
3111
+ const current = this._rightCalendarMonth();
3112
+ let newMonth = new Date(current.getFullYear(), monthIndex, 1);
3113
+ const startDate = this.currentStartDate;
3114
+ if (startDate &&
3115
+ (newMonth.getFullYear() < startDate.getFullYear() ||
3116
+ (newMonth.getFullYear() === startDate.getFullYear() && monthIndex < startDate.getMonth()))) {
3117
+ newMonth = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
3118
+ }
3119
+ this._rightCalendarMonth.set(newMonth);
3120
+ this.updateRightCalendarMatrix();
3121
+ if (this.linkedCalendars()) {
3122
+ const leftMonth = addMonths(newMonth, -1);
3123
+ this._leftCalendarMonth.set(leftMonth);
3124
+ this.updateLeftCalendarMatrix();
3125
+ }
3126
+ }
3127
+ }
3128
+ onYearChange(calendar, event) {
3129
+ const target = event.target;
3130
+ const year = parseInt(target.value, 10);
3131
+ if (calendar === 'left') {
3132
+ const current = this._leftCalendarMonth();
3133
+ let newMonth = new Date(year, current.getMonth(), 1);
3134
+ const minDate = this.minDate();
3135
+ const maxDate = this.maxDate();
3136
+ if (minDate &&
3137
+ (year < minDate.getFullYear() ||
3138
+ (year === minDate.getFullYear() && current.getMonth() < minDate.getMonth()))) {
3139
+ newMonth = new Date(minDate.getFullYear(), minDate.getMonth(), 1);
3140
+ }
3141
+ if (maxDate &&
3142
+ (year > maxDate.getFullYear() ||
3143
+ (year === maxDate.getFullYear() && current.getMonth() > maxDate.getMonth()))) {
3144
+ newMonth = new Date(maxDate.getFullYear(), maxDate.getMonth(), 1);
3145
+ }
3146
+ this._leftCalendarMonth.set(newMonth);
3147
+ this.updateLeftCalendarMatrix();
3148
+ if (this.linkedCalendars() && !this.singleDatePicker()) {
3149
+ const rightMonth = addMonths(newMonth, 1);
3150
+ this._rightCalendarMonth.set(rightMonth);
3151
+ this.updateRightCalendarMatrix();
3152
+ }
3153
+ }
3154
+ else {
3155
+ const current = this._rightCalendarMonth();
3156
+ let newMonth = new Date(year, current.getMonth(), 1);
3157
+ const startDate = this.currentStartDate;
3158
+ if (startDate &&
3159
+ (year < startDate.getFullYear() ||
3160
+ (year === startDate.getFullYear() && current.getMonth() < startDate.getMonth()))) {
3161
+ newMonth = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
3162
+ }
3163
+ this._rightCalendarMonth.set(newMonth);
3164
+ this.updateRightCalendarMatrix();
3165
+ if (this.linkedCalendars()) {
3166
+ const leftMonth = addMonths(newMonth, -1);
3167
+ this._leftCalendarMonth.set(leftMonth);
3168
+ this.updateLeftCalendarMatrix();
3169
+ }
3170
+ }
3171
+ }
3172
+ // Helper methods for template
3173
+ isRangeActive(label) {
3174
+ if (!this.currentStartDate)
3175
+ return false;
3176
+ const ranges = this.ranges();
3177
+ const range = ranges[label];
3178
+ if (!range)
3179
+ return false;
3180
+ const [rangeStart, rangeEnd] = range;
3181
+ const startMatches = isSameDate(this.currentStartDate, rangeStart, 'day');
3182
+ const endMatches = this.currentEndDate
3183
+ ? isSameDate(this.currentEndDate, rangeEnd, 'day')
3184
+ : isSameDate(this.currentStartDate, rangeEnd, 'day');
3185
+ return startMatches && endMatches;
3186
+ }
3187
+ isDisabled(date) {
3188
+ return this.calendarService.isDateDisabled(date, this.minDate(), this.maxDate(), this.maxSpan(), this.currentStartDate, this.currentEndDate, this.singleDatePicker(), this.isInvalidDate());
3189
+ }
3190
+ isSelected(date) {
3191
+ return this.calendarService.isDateSelected(date, this.currentStartDate, this.currentEndDate, this.singleDatePicker());
3192
+ }
3193
+ isInRange(date) {
3194
+ return this.calendarService.isDateInRange(date, this.currentStartDate, this.currentEndDate, this.singleDatePicker());
3195
+ }
3196
+ isRangeStart(date) {
3197
+ return this.calendarService.isDateRangeStart(date, this.currentStartDate);
3198
+ }
3199
+ isRangeEnd(date) {
3200
+ return this.calendarService.isDateRangeEnd(date, this.currentEndDate);
3201
+ }
3202
+ isToday(date) {
3203
+ const today = new Date();
3204
+ return isSameDate(date, today, 'day');
3205
+ }
3206
+ isOtherMonth(date, calendarMonth) {
3207
+ return (date.getMonth() !== calendarMonth.getMonth() ||
3208
+ date.getFullYear() !== calendarMonth.getFullYear());
3209
+ }
3210
+ // Hover methods
3211
+ isHovered(date) {
3212
+ const hoverDate = this._hoverDate();
3213
+ if (!hoverDate)
3214
+ return false;
3215
+ if (!this.currentStartDate || this.currentEndDate || this.singleDatePicker())
3216
+ return false;
3217
+ const startDate = this.currentStartDate;
3218
+ const rangeStart = hoverDate < startDate ? hoverDate : startDate;
3219
+ const rangeEnd = hoverDate < startDate ? startDate : hoverDate;
3220
+ const dateTime = startOfDay(date).getTime();
3221
+ const rangeStartTime = startOfDay(rangeStart).getTime();
3222
+ const rangeEndTime = startOfDay(rangeEnd).getTime();
3223
+ return dateTime > rangeStartTime && dateTime < rangeEndTime;
3224
+ }
3225
+ isHoverStart(date) {
3226
+ const hoverDate = this._hoverDate();
3227
+ if (!hoverDate)
3228
+ return false;
3229
+ if (!this.currentStartDate || this.currentEndDate || this.singleDatePicker())
3230
+ return false;
3231
+ const startDate = this.currentStartDate;
3232
+ const rangeStart = hoverDate < startDate ? hoverDate : startDate;
3233
+ return isSameDate(date, rangeStart, 'day');
3234
+ }
3235
+ isHoverEnd(date) {
3236
+ const hoverDate = this._hoverDate();
3237
+ if (!hoverDate)
3238
+ return false;
3239
+ if (!this.currentStartDate || this.currentEndDate || this.singleDatePicker())
3240
+ return false;
3241
+ const startDate = this.currentStartDate;
3242
+ const rangeEnd = hoverDate < startDate ? startDate : hoverDate;
3243
+ return isSameDate(date, rangeEnd, 'day');
3244
+ }
3245
+ formatDateForAria(date) {
3246
+ const dayNames = ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'];
3247
+ const monthNames = this.monthNames();
3248
+ return `${dayNames[date.getDay()]}, ${date.getDate()} de ${monthNames[date.getMonth()]} de ${date.getFullYear()}`;
3249
+ }
3250
+ // Time picker event handlers
3251
+ onStartHourChange(event) {
3252
+ const target = event.target;
3253
+ const hour = parseInt(target.value, 10);
3254
+ this.updateTimeFromPicker('start', 'hour', hour);
3255
+ }
3256
+ onStartMinuteChange(event) {
3257
+ const target = event.target;
3258
+ const minute = parseInt(target.value, 10);
3259
+ this.updateTimeFromPicker('start', 'minute', minute);
3260
+ }
3261
+ onStartAmPmChange(event) {
3262
+ const target = event.target;
3263
+ const ampm = target.value;
3264
+ this.updateTimeFromPicker('start', 'ampm', ampm);
3265
+ }
3266
+ onEndHourChange(event) {
3267
+ const target = event.target;
3268
+ const hour = parseInt(target.value, 10);
3269
+ this.updateTimeFromPicker('end', 'hour', hour);
3270
+ }
3271
+ onEndMinuteChange(event) {
3272
+ const target = event.target;
3273
+ const minute = parseInt(target.value, 10);
3274
+ this.updateTimeFromPicker('end', 'minute', minute);
3275
+ }
3276
+ onEndAmPmChange(event) {
3277
+ const target = event.target;
3278
+ const ampm = target.value;
3279
+ this.updateTimeFromPicker('end', 'ampm', ampm);
3280
+ }
3281
+ // Private methods
3282
+ initializeComponent() {
3283
+ const inputStartDate = this.startDate();
3284
+ const inputEndDate = this.endDate();
3285
+ if (!inputStartDate && !inputEndDate) {
3286
+ this.initializeDefaultDates();
3287
+ }
3288
+ else {
3289
+ this.initializeWithInputDates(inputStartDate, inputEndDate);
3290
+ }
3291
+ this.datexService.updateConfig(this.config());
3292
+ this.datexService.setLocale(this.locale());
3293
+ this.updateElement();
3294
+ }
3295
+ initializeDefaultDates() {
3296
+ const today = new Date();
3297
+ const startDate = startOfDay(today);
3298
+ const endDate = this.singleDatePicker() ? startOfDay(today) : endOfDay(today);
3299
+ this.currentStartDate = startDate;
3300
+ this.currentEndDate = endDate;
3301
+ this.initializeCalendars(startDate, endDate);
3302
+ this.updateCurrentValue();
3303
+ if (this.timePicker()) {
3304
+ this.updateTimeSignalsFromDate(today, 'start');
3305
+ this.updateTimeSignalsFromDate(today, 'end');
3306
+ }
3307
+ }
3308
+ initializeWithInputDates(inputStartDate, inputEndDate) {
3309
+ const startDate = inputStartDate
3310
+ ? startOfDay(new Date(inputStartDate))
3311
+ : startOfDay(new Date());
3312
+ const endDate = this.singleDatePicker()
3313
+ ? startDate
3314
+ : inputEndDate
3315
+ ? endOfDay(new Date(inputEndDate))
3316
+ : endOfDay(startDate);
3317
+ this.currentStartDate = startDate;
3318
+ this.currentEndDate = endDate;
3319
+ this.initializeCalendars(startDate, endDate);
3320
+ this.updateCurrentValue();
3321
+ if (this.timePicker()) {
3322
+ this.updateTimeSignalsFromDate(startDate, 'start');
3323
+ if (endDate) {
3324
+ this.updateTimeSignalsFromDate(endDate, 'end');
3325
+ }
3326
+ }
3327
+ }
3328
+ initializeCalendars(startDate, endDate) {
3329
+ this._leftCalendarMonth.set(new Date(startDate.getFullYear(), startDate.getMonth(), 1));
3330
+ this.updateLeftCalendarMatrix();
3331
+ if (!this.singleDatePicker()) {
3332
+ if (this.linkedCalendars()) {
3333
+ const nextMonth = addMonths(new Date(startDate.getFullYear(), startDate.getMonth(), 1), 1);
3334
+ this._rightCalendarMonth.set(nextMonth);
3335
+ }
3336
+ else if (endDate &&
3337
+ (endDate.getMonth() !== startDate.getMonth() ||
3338
+ endDate.getFullYear() !== startDate.getFullYear())) {
3339
+ this._rightCalendarMonth.set(new Date(endDate.getFullYear(), endDate.getMonth(), 1));
3340
+ }
3341
+ else {
3342
+ const nextMonth = addMonths(new Date(startDate.getFullYear(), startDate.getMonth(), 1), 1);
3343
+ this._rightCalendarMonth.set(nextMonth);
3344
+ }
3345
+ this.updateRightCalendarMatrix();
3346
+ }
3347
+ }
3348
+ updateMonthsInView() {
3349
+ this.calendarService.updateMonthsInView(this.currentStartDate, this.currentEndDate, this._leftCalendarMonth(), this._rightCalendarMonth(), this.singleDatePicker(), this.linkedCalendars(), this.maxDate(), (month) => {
3350
+ this._leftCalendarMonth.set(month);
3351
+ this.updateLeftCalendarMatrix();
3352
+ }, (month) => {
3353
+ this._rightCalendarMonth.set(month);
3354
+ this.updateRightCalendarMatrix();
3355
+ });
3356
+ }
3357
+ updateLeftCalendarMatrix() {
3358
+ const matrix = this.datexService.buildCalendarMatrix(this._leftCalendarMonth());
3359
+ this._leftCalendarMatrix.set(matrix);
3360
+ }
3361
+ updateRightCalendarMatrix() {
3362
+ const matrix = this.datexService.buildCalendarMatrix(this._rightCalendarMonth());
3363
+ this._rightCalendarMatrix.set(matrix);
3364
+ }
3365
+ updateTimeSignalsFromDate(date, type) {
3366
+ const timeValue = this.timePickerService.updateTimeSignalsFromDate(date, this.timePicker24Hour());
3367
+ if (type === 'start') {
3368
+ this._startTime.set(timeValue);
3369
+ }
3370
+ else {
3371
+ this._endTime.set(timeValue);
3372
+ }
3373
+ }
3374
+ setStartDate(startDate) {
3375
+ const newStartDate = this.calendarService.setStartDate(startDate, this.minDate(), this.maxDate(), this.timePicker(), this.timePickerIncrement());
3376
+ this.currentStartDate = newStartDate;
3377
+ this.updateCurrentValue();
3378
+ this.updateMonthsInView();
3379
+ if (!this._isOpen()) {
3380
+ this.updateElement();
3381
+ }
3382
+ }
3383
+ setEndDate(endDate) {
3384
+ const newEndDate = this.calendarService.setEndDate(endDate, this.currentStartDate, this.maxDate(), this.maxSpan(), this.timePicker(), this.timePickerIncrement());
3385
+ this.previousRightTime = new Date(newEndDate);
3386
+ this.currentEndDate = newEndDate;
3387
+ this.updateCurrentValue();
3388
+ this.updateMonthsInView();
3389
+ if (!this._isOpen()) {
3390
+ this.updateElement();
3391
+ }
3392
+ }
3393
+ updateElement() {
3394
+ if (!this.autoUpdateInput())
3395
+ return;
3396
+ const startDate = this._internalStartDate;
3397
+ const endDate = this._internalEndDate;
3398
+ if (!startDate)
3399
+ return;
3400
+ const format = this.locale().format || 'DD/MM/YYYY';
3401
+ const timeFormat = this.timePicker() ? (this.timePicker24Hour() ? ' HH:mm' : ' hh:mm A') : '';
3402
+ const fullFormat = format + timeFormat;
3403
+ const separator = this.locale().separator || ' - ';
3404
+ let newValue = formatDateValue(startDate, fullFormat);
3405
+ if (!this.singleDatePicker() && endDate) {
3406
+ newValue += separator + formatDateValue(endDate, fullFormat);
3407
+ }
3408
+ this._displayValue.set(newValue);
3409
+ }
3410
+ updateCurrentValue() {
3411
+ const value = {
3412
+ startDate: this.currentStartDate,
3413
+ endDate: this.currentEndDate,
3414
+ };
3415
+ this._currentValue.set(value);
3416
+ }
3417
+ emitValueChange() {
3418
+ const value = {
3419
+ startDate: this.currentStartDate,
3420
+ endDate: this.currentEndDate,
3421
+ };
3422
+ this._currentValue.set(value);
3423
+ this.onChange(value);
3424
+ this.dateChange.emit(value);
3425
+ if (this.currentEndDate) {
3426
+ this.rangeChange.emit({
3427
+ startDate: this.currentStartDate,
3428
+ endDate: this.currentEndDate,
3429
+ });
3430
+ }
3431
+ }
3432
+ updateView() {
3433
+ if (this.timePicker()) {
3434
+ this.updateTimePickers();
3435
+ }
3436
+ this.updateLeftCalendarMatrix();
3437
+ this.updateRightCalendarMatrix();
3438
+ }
3439
+ updateTimePickers() {
3440
+ // Time picker update logic handled by service
3441
+ }
3442
+ updateTimeFromPicker(side, component, value) {
3443
+ this.timePickerService.updateTimeFromPicker(this.currentStartDate, this.currentEndDate, side, component, value, this._startTime(), this._endTime(), this.timePicker24Hour(), this.singleDatePicker(), (date) => this.setStartDate(date), (date) => this.setEndDate(date), (side, time) => {
3444
+ if (side === 'start') {
3445
+ this._startTime.set(time);
3446
+ }
3447
+ else {
3448
+ this._endTime.set(time);
3449
+ }
3450
+ });
3451
+ this.updateView();
3452
+ }
3453
+ elementChanged() {
3454
+ const input = this.inputElement?.nativeElement;
3455
+ if (!input || !input.value || !input.value.length)
3456
+ return;
3457
+ const inputValue = input.value;
3458
+ const separator = this.locale().separator || ' - ';
3459
+ const dateString = inputValue.split(separator);
3460
+ let start = null;
3461
+ let end = null;
3462
+ if (dateString.length === 2) {
3463
+ start = this.parseInputDate(dateString[0].trim());
3464
+ end = this.parseInputDate(dateString[1].trim());
3465
+ }
3466
+ if (this.singleDatePicker() || start === null || end === null) {
3467
+ start = this.parseInputDate(inputValue);
3468
+ end = start;
3469
+ }
3470
+ if (!start || !isValidDate(start) || !end || !isValidDate(end)) {
3471
+ this._errorMessage.set('Formato de fecha inválido');
3472
+ return;
3473
+ }
3474
+ this._errorMessage.set('');
3475
+ try {
3476
+ this.setStartDate(start);
3477
+ this.setEndDate(end);
3478
+ this.updateView();
3479
+ if (!this._isOpen()) {
3480
+ this.emitValueChange();
3481
+ }
3482
+ }
3483
+ catch {
3484
+ this._errorMessage.set('Fecha fuera del rango permitido');
3485
+ }
3486
+ }
3487
+ parseInputDate(dateStr) {
3488
+ if (!dateStr)
3489
+ return null;
3490
+ const format = this.locale().format || 'DD/MM/YYYY';
3491
+ try {
3492
+ const date = parseDateValue(dateStr, format);
3493
+ if (isValidDate(date)) {
3494
+ return date;
3495
+ }
3496
+ const nativeDate = new Date(dateStr);
3497
+ if (isValidDate(nativeDate)) {
3498
+ return nativeDate;
3499
+ }
3500
+ return null;
3501
+ }
3502
+ catch {
3503
+ return null;
3504
+ }
3505
+ }
3506
+ createOverlay() {
3507
+ this.overlayService.createOverlay(this.inputElement, this.calendarTemplate, this.viewContainerRef, this.locale(), this.opens(), this.drops(), () => this.closeCalendar(), (event) => {
3508
+ if (event.key === 'Escape' && !this._inputFocused()) {
3509
+ this.closeCalendar();
3510
+ }
3511
+ }, (position) => this._overlayPosition.set(position));
3512
+ }
3513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatex, deps: [], target: i0.ɵɵFactoryTarget.Component });
3514
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: NgxDatex, isStandalone: true, selector: "ngx-datex", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, floatLabel: { classPropertyName: "floatLabel", publicName: "floatLabel", 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 }, calendarIcon: { classPropertyName: "calendarIcon", publicName: "calendarIcon", isSignal: true, isRequired: false, transformFunction: null }, showCalendarIcon: { classPropertyName: "showCalendarIcon", publicName: "showCalendarIcon", isSignal: true, isRequired: false, transformFunction: null }, calendarIconPosition: { classPropertyName: "calendarIconPosition", publicName: "calendarIconPosition", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, checkboxPosition: { classPropertyName: "checkboxPosition", publicName: "checkboxPosition", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, showFooter: { classPropertyName: "showFooter", publicName: "showFooter", isSignal: true, isRequired: false, transformFunction: null }, singleDatePicker: { classPropertyName: "singleDatePicker", publicName: "singleDatePicker", isSignal: true, isRequired: false, transformFunction: null }, autoApply: { classPropertyName: "autoApply", publicName: "autoApply", isSignal: true, isRequired: false, transformFunction: null }, showDropdowns: { classPropertyName: "showDropdowns", publicName: "showDropdowns", isSignal: true, isRequired: false, transformFunction: null }, timePicker: { classPropertyName: "timePicker", publicName: "timePicker", isSignal: true, isRequired: false, transformFunction: null }, timePicker24Hour: { classPropertyName: "timePicker24Hour", publicName: "timePicker24Hour", isSignal: true, isRequired: false, transformFunction: null }, timePickerIncrement: { classPropertyName: "timePickerIncrement", publicName: "timePickerIncrement", isSignal: true, isRequired: false, transformFunction: null }, timePickerSeconds: { classPropertyName: "timePickerSeconds", publicName: "timePickerSeconds", isSignal: true, isRequired: false, transformFunction: null }, linkedCalendars: { classPropertyName: "linkedCalendars", publicName: "linkedCalendars", isSignal: true, isRequired: false, transformFunction: null }, autoUpdateInput: { classPropertyName: "autoUpdateInput", publicName: "autoUpdateInput", isSignal: true, isRequired: false, transformFunction: null }, alwaysShowCalendars: { classPropertyName: "alwaysShowCalendars", publicName: "alwaysShowCalendars", isSignal: true, isRequired: false, transformFunction: null }, showCustomRangeLabel: { classPropertyName: "showCustomRangeLabel", publicName: "showCustomRangeLabel", isSignal: true, isRequired: false, transformFunction: null }, startDate: { classPropertyName: "startDate", publicName: "startDate", isSignal: true, isRequired: false, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, maxSpan: { classPropertyName: "maxSpan", publicName: "maxSpan", isSignal: true, isRequired: false, transformFunction: null }, showWeekNumbers: { classPropertyName: "showWeekNumbers", publicName: "showWeekNumbers", isSignal: true, isRequired: false, transformFunction: null }, showISOWeekNumbers: { classPropertyName: "showISOWeekNumbers", publicName: "showISOWeekNumbers", isSignal: true, isRequired: false, transformFunction: null }, buttonClasses: { classPropertyName: "buttonClasses", publicName: "buttonClasses", isSignal: true, isRequired: false, transformFunction: null }, applyButtonClasses: { classPropertyName: "applyButtonClasses", publicName: "applyButtonClasses", isSignal: true, isRequired: false, transformFunction: null }, cancelButtonClasses: { classPropertyName: "cancelButtonClasses", publicName: "cancelButtonClasses", isSignal: true, isRequired: false, transformFunction: null }, isInvalidDate: { classPropertyName: "isInvalidDate", publicName: "isInvalidDate", isSignal: true, isRequired: false, transformFunction: null }, isCustomDate: { classPropertyName: "isCustomDate", publicName: "isCustomDate", isSignal: true, isRequired: false, transformFunction: null }, minYear: { classPropertyName: "minYear", publicName: "minYear", isSignal: true, isRequired: false, transformFunction: null }, maxYear: { classPropertyName: "maxYear", publicName: "maxYear", isSignal: true, isRequired: false, transformFunction: null }, ranges: { classPropertyName: "ranges", publicName: "ranges", isSignal: true, isRequired: false, transformFunction: null }, opens: { classPropertyName: "opens", publicName: "opens", isSignal: true, isRequired: false, transformFunction: null }, drops: { classPropertyName: "drops", publicName: "drops", isSignal: true, isRequired: false, transformFunction: null }, headerTemplate: { classPropertyName: "headerTemplate", publicName: "headerTemplate", isSignal: true, isRequired: false, transformFunction: null }, footerTemplate: { classPropertyName: "footerTemplate", publicName: "footerTemplate", isSignal: true, isRequired: false, transformFunction: null }, dayTemplate: { classPropertyName: "dayTemplate", publicName: "dayTemplate", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dateChange: "dateChange", rangeChange: "rangeChange", openEvent: "openEvent", closeEvent: "closeEvent", monthChange: "monthChange", yearChange: "yearChange", dateHover: "dateHover", validationError: "validationError", checkboxChange: "checkboxChange" }, providers: [
3515
+ {
3516
+ provide: NG_VALUE_ACCESSOR,
3517
+ useExisting: forwardRef(() => NgxDatex),
3518
+ multi: true,
3519
+ },
3520
+ NgxDatexOverlayService,
3521
+ NgxDatexTimePickerService,
3522
+ NgxDatexCalendarService,
3523
+ ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }, { propertyName: "calendarTemplate", first: true, predicate: ["calendarTemplate"], descendants: true }], ngImport: i0, template: "<div class=\"ngx-datex-container\" [class.ngx-datex-mobile]=\"isMobileDevice()\">\r\n <!-- Input Field -->\r\n <mat-form-field\r\n [appearance]=\"appearance()\"\r\n [floatLabel]=\"floatLabel()\"\r\n class=\"ngx-datex-input-field\"\r\n >\r\n @if (label()) {\r\n <mat-label>{{ label() }}</mat-label>\r\n }\r\n\r\n <!-- Checkbox como prefix -->\r\n @if (showCheckbox() && checkboxPosition() === 'prefix') {\r\n <mat-checkbox\r\n matPrefix\r\n [checked]=\"checkboxValue()\"\r\n (change)=\"onCheckboxChange($event.checked)\"\r\n class=\"ngx-datex-checkbox ngx-datex-checkbox-prefix\"\r\n >\r\n </mat-checkbox>\r\n }\r\n\r\n <!-- \u00CDcono como prefix -->\r\n @if (showCalendarIcon() && calendarIconPosition() === 'prefix') {\r\n <mat-icon\r\n matPrefix\r\n class=\"ngx-datex-calendar-icon\"\r\n [class.ngx-datex-icon-active]=\"isOpen()\"\r\n (click)=\"toggle()\"\r\n (keydown.enter)=\"toggle()\"\r\n (keydown.space)=\"toggle()\"\r\n tabindex=\"0\"\r\n role=\"button\"\r\n [attr.aria-label]=\"'Open calendar'\"\r\n >\r\n {{ calendarIcon() }}\r\n </mat-icon>\r\n }\r\n\r\n <input\r\n matInput\r\n #inputElement\r\n [value]=\"displayValue()\"\r\n [placeholder]=\"placeholder()\"\r\n [readonly]=\"readonly()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"ariaLabel()\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (click)=\"onInputClick()\"\r\n (focus)=\"onInputFocus()\"\r\n (blur)=\"onInputBlur()\"\r\n (keydown)=\"onInputKeydown($event)\"\r\n (keyup)=\"onInputKeyup()\"\r\n autocomplete=\"off\"\r\n />\r\n\r\n <!-- \u00CDcono como suffix -->\r\n @if (showCalendarIcon() && calendarIconPosition() === 'suffix') {\r\n <mat-icon\r\n matSuffix\r\n class=\"ngx-datex-calendar-icon\"\r\n [class.ngx-datex-icon-active]=\"isOpen()\"\r\n (click)=\"toggle()\"\r\n (keydown.enter)=\"toggle()\"\r\n (keydown.space)=\"toggle()\"\r\n tabindex=\"0\"\r\n role=\"button\"\r\n [attr.aria-label]=\"'Open calendar'\"\r\n >\r\n {{ calendarIcon() }}\r\n </mat-icon>\r\n }\r\n\r\n <!-- Checkbox como suffix -->\r\n @if (showCheckbox() && checkboxPosition() === 'suffix') {\r\n <mat-checkbox\r\n matSuffix\r\n [checked]=\"checkboxValue()\"\r\n (change)=\"onCheckboxChange($event.checked)\"\r\n class=\"ngx-datex-checkbox ngx-datex-checkbox-suffix\"\r\n >\r\n </mat-checkbox>\r\n }\r\n\r\n @if (hasError()) {\r\n <mat-error>{{ errorMessage() }}</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- Calendar Template for CDK Overlay -->\r\n <ng-template #calendarTemplate>\r\n <div\r\n class=\"ngx-datex-overlay\"\r\n [class.ngx-datex-overlay-mobile]=\"isMobileDevice()\"\r\n [class.ngx-datex-single]=\"singleDatePicker()\"\r\n [class.ngx-datex-arrow-up]=\"arrowDirection() === 'up'\"\r\n [class.ngx-datex-arrow-down]=\"arrowDirection() === 'down'\"\r\n [class.ngx-datex-arrow-left]=\"arrowDirection() === 'left'\"\r\n [class.ngx-datex-arrow-right]=\"arrowDirection() === 'right'\"\r\n [class.ngx-datex-arrow-align-start]=\"arrowAlignment() === 'start'\"\r\n [class.ngx-datex-arrow-align-center]=\"arrowAlignment() === 'center'\"\r\n [class.ngx-datex-arrow-align-end]=\"arrowAlignment() === 'end'\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"headerTitle()\"\r\n tabindex=\"-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n (keydown)=\"onCalendarKeydown($event)\"\r\n >\r\n <!-- Mobile Header (only shown on mobile) -->\r\n @if (isMobileDevice()) {\r\n <div class=\"ngx-datex-mobile-header\">\r\n <div class=\"ngx-datex-mobile-header-content\">\r\n <div class=\"ngx-datex-mobile-selected-range\">\r\n @if (hasStartDate()) {\r\n {{ formattedSelectedRange() }}\r\n } @else {\r\n Selecciona un rango de fechas\r\n }\r\n </div>\r\n <div class=\"ngx-datex-mobile-range-label\">\r\n @if (currentLabel()) {\r\n {{ currentLabel() }}\r\n }\r\n </div>\r\n </div>\r\n <div class=\"ngx-datex-mobile-header-buttons\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-mobile-cancel-button\"\r\n [ngClass]=\"[buttonClasses(), cancelButtonClasses()]\"\r\n (click)=\"cancel()\"\r\n >\r\n {{ locale().cancelLabel || 'Cancelar' }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-mobile-apply-button\"\r\n [ngClass]=\"[buttonClasses(), applyButtonClasses()]\"\r\n [disabled]=\"!canApplyValue()\"\r\n (click)=\"apply()\"\r\n >\r\n {{ locale().applyLabel || 'Aplicar' }}\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"ngx-datex-content\">\r\n <!-- Predefined Ranges - Desktop: Sidebar, Mobile: Horizontal Chips -->\r\n @if (showRanges()) {\r\n <!-- Desktop Ranges Sidebar -->\r\n @if (!isMobileDevice()) {\r\n <div class=\"ngx-datex-ranges-sidebar\">\r\n @for (rangeEntry of rangeEntries(); track rangeEntry.label) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-item\"\r\n [class.ngx-datex-range-active]=\"isRangeActive(rangeEntry.label)\"\r\n (click)=\"selectRange(rangeEntry.label)\"\r\n >\r\n {{ rangeEntry.label }}\r\n </button>\r\n }\r\n <!-- Rango Personalizado -->\r\n @if (showCustomRangeLabel()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-item\"\r\n [class.ngx-datex-range-active]=\"isCustomRange()\"\r\n (click)=\"selectCustomRange()\"\r\n >\r\n {{ locale().customRangeLabel || 'Rango Personalizado' }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Mobile Ranges Chips -->\r\n @if (isMobileDevice()) {\r\n <div class=\"ngx-datex-ranges-chips\">\r\n <div class=\"ngx-datex-ranges-chips-container\">\r\n @for (rangeEntry of rangeEntries(); track rangeEntry.label) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-chip\"\r\n [class.ngx-datex-range-chip-active]=\"isRangeActive(rangeEntry.label)\"\r\n (click)=\"selectRange(rangeEntry.label)\"\r\n >\r\n {{ rangeEntry.label }}\r\n </button>\r\n }\r\n <!-- Rango Personalizado -->\r\n @if (showCustomRangeLabel()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-chip\"\r\n [class.ngx-datex-range-chip-active]=\"isCustomRange()\"\r\n (click)=\"selectCustomRange()\"\r\n >\r\n {{ locale().customRangeLabel || 'Personalizado' }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Calendar Section -->\r\n <div class=\"ngx-datex-calendar-section\">\r\n <!-- Calendar Container -->\r\n <div class=\"ngx-datex-calendars\" (mouseleave)=\"onCalendarMouseLeave()\">\r\n <!-- Left Calendar -->\r\n <div\r\n class=\"ngx-datex-calendar ngx-datex-calendar-left\"\r\n [class.ngx-datex-calendar-single]=\"singleDatePicker()\"\r\n >\r\n <div class=\"ngx-datex-calendar-header\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"previousMonth('left')\"\r\n [disabled]=\"!canNavigatePrevious('left')\"\r\n aria-label=\"Mes anterior\"\r\n >\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"ngx-datex-month-year\">\r\n @if (showDropdowns()) {\r\n <select\r\n class=\"ngx-datex-month-select\"\r\n [value]=\"leftCalendarMonthValue()\"\r\n (change)=\"onMonthChange('left', $event)\"\r\n aria-label=\"Seleccionar mes\"\r\n >\r\n @for (month of monthNames(); track $index) {\r\n <option [value]=\"$index\">{{ month }}</option>\r\n }\r\n </select>\r\n\r\n <select\r\n class=\"ngx-datex-year-select\"\r\n [value]=\"leftCalendarYearValue()\"\r\n (change)=\"onYearChange('left', $event)\"\r\n aria-label=\"Seleccionar a\u00F1o\"\r\n >\r\n @for (year of availableYears(); track year) {\r\n <option [value]=\"year\">{{ year }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <span class=\"ngx-datex-month-year-display\">\r\n {{ monthNames()[leftCalendarMonthValue()] }} {{ leftCalendarYearValue() }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"nextMonth('left')\"\r\n [disabled]=\"!canNavigateNext('left')\"\r\n aria-label=\"Mes siguiente\"\r\n >\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Days of Week Header -->\r\n <div class=\"ngx-datex-days-header\">\r\n <div class=\"ngx-datex-days-header-row\">\r\n @for (day of daysOfWeek(); track day) {\r\n <div class=\"ngx-datex-day-header\">{{ day }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Calendar Grid -->\r\n <div class=\"ngx-datex-calendar-grid\">\r\n @for (week of leftCalendarMatrix(); track $index) {\r\n <div class=\"ngx-datex-week\">\r\n @for (date of week; track date.getTime()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-day\"\r\n [class.ngx-datex-day-other-month]=\"isOtherMonth(date, leftCalendarMonth())\"\r\n [class.ngx-datex-day-today]=\"isToday(date)\"\r\n [class.ngx-datex-day-selected]=\"isSelected(date)\"\r\n [class.ngx-datex-day-active]=\"isSelected(date)\"\r\n [class.ngx-datex-day-in-range]=\"isInRange(date)\"\r\n [class.ngx-datex-day-range-start]=\"isRangeStart(date)\"\r\n [class.ngx-datex-day-range-end]=\"isRangeEnd(date)\"\r\n [class.ngx-datex-day-hover-range]=\"\r\n isHovered(date) && !isHoverStart(date) && !isHoverEnd(date)\r\n \"\r\n [class.ngx-datex-day-hover-start]=\"isHoverStart(date)\"\r\n [class.ngx-datex-day-hover-end]=\"isHoverEnd(date)\"\r\n [class.ngx-datex-day-disabled]=\"isDisabled(date)\"\r\n [disabled]=\"isDisabled(date)\"\r\n (click)=\"selectDate(date)\"\r\n (mouseenter)=\"onDateHover(date)\"\r\n [attr.aria-label]=\"formatDateForAria(date)\"\r\n >\r\n {{ date.getDate() }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Time Picker for Left Calendar -->\r\n @if (timePicker()) {\r\n <div class=\"ngx-datex-time-picker\">\r\n <div class=\"ngx-datex-time-controls\">\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-hour-select\"\r\n [value]=\"selectedStartHour()\"\r\n (change)=\"onStartHourChange($event)\"\r\n aria-label=\"Seleccionar hora\"\r\n >\r\n @if (timePicker24Hour()) {\r\n @for (hourOption of availableStartHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedStartHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n } @else {\r\n @for (hourOption of availableStartHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedStartHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value }}\r\n </option>\r\n }\r\n }\r\n </select>\r\n\r\n <span class=\"ngx-datex-time-separator\">:</span>\r\n\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-minute-select\"\r\n [value]=\"selectedStartMinute()\"\r\n (change)=\"onStartMinuteChange($event)\"\r\n aria-label=\"Seleccionar minuto\"\r\n >\r\n @for (minuteOption of availableStartMinutes(); track minuteOption.value) {\r\n <option\r\n [value]=\"minuteOption.value\"\r\n [selected]=\"selectedStartMinute() === minuteOption.value\"\r\n [disabled]=\"minuteOption.disabled\"\r\n >\r\n {{ minuteOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n </select>\r\n\r\n @if (!timePicker24Hour()) {\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-ampm-select\"\r\n [value]=\"selectedStartAmPm()\"\r\n (change)=\"onStartAmPmChange($event)\"\r\n aria-label=\"Seleccionar AM/PM\"\r\n >\r\n <option value=\"AM\">AM</option>\r\n <option value=\"PM\">PM</option>\r\n </select>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Right Calendar (for range picker) -->\r\n @if (!singleDatePicker()) {\r\n <div class=\"ngx-datex-calendar ngx-datex-calendar-right\">\r\n <div class=\"ngx-datex-calendar-header\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"previousMonth('right')\"\r\n [disabled]=\"!canNavigatePrevious('right')\"\r\n aria-label=\"Mes anterior\"\r\n >\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"ngx-datex-month-year\">\r\n @if (showDropdowns()) {\r\n <select\r\n class=\"ngx-datex-month-select\"\r\n [value]=\"rightCalendarMonthValue()\"\r\n (change)=\"onMonthChange('right', $event)\"\r\n aria-label=\"Seleccionar mes\"\r\n >\r\n @for (month of monthNames(); track $index) {\r\n <option [value]=\"$index\">{{ month }}</option>\r\n }\r\n </select>\r\n\r\n <select\r\n class=\"ngx-datex-year-select\"\r\n [value]=\"rightCalendarYearValue()\"\r\n (change)=\"onYearChange('right', $event)\"\r\n aria-label=\"Seleccionar a\u00F1o\"\r\n >\r\n @for (year of availableYears(); track year) {\r\n <option [value]=\"year\">{{ year }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <span class=\"ngx-datex-month-year-display\">\r\n {{ monthNames()[rightCalendarMonthValue()] }} {{ rightCalendarYearValue() }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"nextMonth('right')\"\r\n [disabled]=\"!canNavigateNext('right')\"\r\n aria-label=\"Mes siguiente\"\r\n >\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Days of Week Header -->\r\n <div class=\"ngx-datex-days-header\">\r\n <div class=\"ngx-datex-days-header-row\">\r\n @for (day of daysOfWeek(); track day) {\r\n <div class=\"ngx-datex-day-header\">{{ day }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Calendar Grid -->\r\n <div class=\"ngx-datex-calendar-grid\">\r\n @for (week of rightCalendarMatrix(); track $index) {\r\n <div class=\"ngx-datex-week\">\r\n @for (date of week; track date.getTime()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-day\"\r\n [class.ngx-datex-day-other-month]=\"\r\n isOtherMonth(date, rightCalendarMonth())\r\n \"\r\n [class.ngx-datex-day-today]=\"isToday(date)\"\r\n [class.ngx-datex-day-selected]=\"isSelected(date)\"\r\n [class.ngx-datex-day-active]=\"isSelected(date)\"\r\n [class.ngx-datex-day-in-range]=\"isInRange(date)\"\r\n [class.ngx-datex-day-range-start]=\"isRangeStart(date)\"\r\n [class.ngx-datex-day-range-end]=\"isRangeEnd(date)\"\r\n [class.ngx-datex-day-hover-range]=\"\r\n isHovered(date) && !isHoverStart(date) && !isHoverEnd(date)\r\n \"\r\n [class.ngx-datex-day-hover-start]=\"isHoverStart(date)\"\r\n [class.ngx-datex-day-hover-end]=\"isHoverEnd(date)\"\r\n [class.ngx-datex-day-disabled]=\"isDisabled(date)\"\r\n [disabled]=\"isDisabled(date)\"\r\n (click)=\"selectDate(date)\"\r\n (mouseenter)=\"onDateHover(date)\"\r\n [attr.aria-label]=\"formatDateForAria(date)\"\r\n >\r\n {{ date.getDate() }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Time Picker for Right Calendar -->\r\n @if (timePicker() && !singleDatePicker()) {\r\n <div class=\"ngx-datex-time-picker\">\r\n <div class=\"ngx-datex-time-controls\">\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-hour-select\"\r\n [value]=\"selectedEndHour()\"\r\n [disabled]=\"!hasEndDate()\"\r\n (change)=\"onEndHourChange($event)\"\r\n aria-label=\"Seleccionar hora\"\r\n >\r\n @if (timePicker24Hour()) {\r\n @for (hourOption of availableEndHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedEndHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n } @else {\r\n @for (hourOption of availableEndHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedEndHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value }}\r\n </option>\r\n }\r\n }\r\n </select>\r\n\r\n <span class=\"ngx-datex-time-separator\">:</span>\r\n\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-minute-select\"\r\n [value]=\"selectedEndMinute()\"\r\n [disabled]=\"!hasEndDate()\"\r\n (change)=\"onEndMinuteChange($event)\"\r\n aria-label=\"Seleccionar minuto\"\r\n >\r\n @for (minuteOption of availableEndMinutes(); track minuteOption.value) {\r\n <option\r\n [value]=\"minuteOption.value\"\r\n [selected]=\"selectedEndMinute() === minuteOption.value\"\r\n [disabled]=\"minuteOption.disabled\"\r\n >\r\n {{ minuteOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n </select>\r\n\r\n @if (!timePicker24Hour()) {\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-ampm-select\"\r\n [value]=\"selectedEndAmPm()\"\r\n [disabled]=\"!hasEndDate()\"\r\n (change)=\"onEndAmPmChange($event)\"\r\n aria-label=\"Seleccionar AM/PM\"\r\n >\r\n <option value=\"AM\">AM</option>\r\n <option value=\"PM\">PM</option>\r\n </select>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Footer with selected range and buttons (desktop only) -->\r\n @if (!isMobileDevice()) {\r\n <div class=\"ngx-datex-footer\">\r\n <div class=\"ngx-datex-selected-range\">\r\n @if (hasStartDate()) {\r\n {{ formattedSelectedRange() }}\r\n } @else {\r\n Selecciona un rango de fechas\r\n }\r\n </div>\r\n <div class=\"ngx-datex-footer-buttons\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-cancel-button\"\r\n [ngClass]=\"[buttonClasses(), cancelButtonClasses()]\"\r\n (click)=\"cancel()\"\r\n >\r\n {{ locale().cancelLabel || 'Cancelar' }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-apply-button\"\r\n [ngClass]=\"[buttonClasses(), applyButtonClasses()]\"\r\n [disabled]=\"!canApplyValue()\"\r\n (click)=\"apply()\"\r\n >\r\n {{ locale().applyLabel || 'Aplicar' }}\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</div>\r\n", styles: [".ngx-datex-container{position:relative;display:inline-block;width:100%}.ngx-datex-input-field{width:100%}.ngx-datex-calendar-icon{cursor:pointer;transition:color .2s ease;font-size:20px}.ngx-datex-calendar-icon:hover,.ngx-datex-calendar-icon.ngx-datex-icon-active{color:var(--ngx-datex-primary-color, #1976d2)}.ngx-datex-overlay-panel{z-index:1000}.ngx-datex-mobile-overlay{z-index:1001}.ngx-datex-overlay{background:#fff;border-radius:8px;box-shadow:0 5px 15px #00000026,0 2px 4px #0000001f;border:1px solid #e0e0e0;width:650px;font-family:Roboto,sans-serif;font-size:12px;line-height:1em;position:relative;color:#212121}.ngx-datex-overlay:before,.ngx-datex-overlay:after{position:absolute;content:\"\";display:none;z-index:10}.ngx-datex-overlay.ngx-datex-arrow-down:before{display:block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #e0e0e0;top:-7px}.ngx-datex-overlay.ngx-datex-arrow-down:after{display:block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;top:-6px}.ngx-datex-overlay.ngx-datex-arrow-up:before{display:block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #e0e0e0;bottom:-7px}.ngx-datex-overlay.ngx-datex-arrow-up:after{display:block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #ffffff;bottom:-6px}.ngx-datex-overlay.ngx-datex-arrow-right:before{display:block;border-top:7px solid transparent;border-bottom:7px solid transparent;border-right:7px solid #e0e0e0;left:-7px}.ngx-datex-overlay.ngx-datex-arrow-right:after{display:block;border-top:6px solid transparent;border-bottom:6px solid transparent;border-right:6px solid #ffffff;left:-6px}.ngx-datex-overlay.ngx-datex-arrow-left:before{display:block;border-top:7px solid transparent;border-bottom:7px solid transparent;border-left:7px solid #e0e0e0;right:-7px}.ngx-datex-overlay.ngx-datex-arrow-left:after{display:block;border-top:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid #ffffff;right:-6px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-start:before,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-start:before{left:20px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-start:after,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-start:after{left:21px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-center:before,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-center:before{left:50%;transform:translate(-50%)}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-center:after,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-center:after{left:50%;transform:translate(-50%)}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-end:before,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-end:before{right:20px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-end:after,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-end:after{right:21px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-start:before,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-start:before{top:20px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-start:after,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-start:after{top:21px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-center:before,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-center:before{top:50%;transform:translateY(-50%)}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-center:after,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-center:after{top:50%;transform:translateY(-50%)}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-end:before,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-end:before{bottom:20px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-end:after,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-end:after{bottom:21px}.ngx-datex-overlay.ngx-datex-single{width:300px}.ngx-datex-overlay.ngx-datex-single .ngx-datex-ranges-sidebar{display:none}.ngx-datex-overlay.ngx-datex-overlay-mobile{width:100vw;max-width:100vw;min-width:100vw;border-radius:16px 16px 0 0;max-height:90vh;overflow-y:auto;box-shadow:0 -8px 32px #0003;border:none;margin:0;padding:0}.ngx-datex-overlay.ngx-datex-overlay-mobile:before,.ngx-datex-overlay.ngx-datex-overlay-mobile:after{display:none}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-content{min-height:auto;padding:0;margin:0;width:100%;display:flex;flex-direction:column}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-calendar-section{padding:0;margin:0;width:100%;flex:1}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-calendars{padding:16px;gap:20px;margin:0;width:100%;box-sizing:border-box}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-calendar{padding:0;margin:0;border-radius:0;background:transparent;border:none;box-shadow:none;width:100%}.ngx-datex-content{display:flex;min-height:350px}.ngx-datex-ranges-sidebar{width:140px;background:#fff;border-right:1px solid #e0e0e0;padding:0;border-radius:8px 0 0 8px;display:flex;flex-direction:column}.ngx-datex-range-item{background:none;border:none;padding:8px 12px;text-align:left;cursor:pointer;font-size:12px;color:#212121;transition:all .2s ease;border-radius:0;margin:2px 0}.ngx-datex-range-item:hover{background:#f5f5f5;color:#1976d2}.ngx-datex-range-item.ngx-datex-range-active{background:#1976d2;color:#fff;font-weight:500}.ngx-datex-ranges-chips{width:100%;background:#fff;border-bottom:1px solid #e0e0e0;padding:16px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none}.ngx-datex-ranges-chips::-webkit-scrollbar{display:none}.ngx-datex-ranges-chips-container{display:flex;gap:12px;padding:0 16px;min-width:max-content}.ngx-datex-range-chip{background:#f8f9fa;border:1px solid #dee2e6;border-radius:20px;padding:10px 16px;font-size:14px;font-weight:500;color:#495057;cursor:pointer;transition:all .3s ease;white-space:nowrap;flex-shrink:0;min-height:40px;display:flex;align-items:center;justify-content:center;min-width:80px;text-align:center}.ngx-datex-range-chip:hover{background:#e3f2fd;border-color:#2196f3;color:#1976d2;transform:translateY(-2px);box-shadow:0 4px 12px #2196f340}.ngx-datex-range-chip.ngx-datex-range-chip-active{background:linear-gradient(135deg,#2196f3,#1976d2);border-color:#1976d2;color:#fff;font-weight:600;box-shadow:0 4px 16px #1976d266;transform:translateY(-1px)}.ngx-datex-range-chip:active{transform:translateY(0)}.ngx-datex-calendar-section{flex:1;display:flex;flex-direction:column}@media(max-width:768px){.ngx-datex-ranges-chips+.ngx-datex-calendar-section{padding-top:0}.ngx-datex-calendar-section{padding:0;background:#fff;width:100%;margin:0}}.ngx-datex-calendars{display:flex;padding:0;gap:0;flex:1}@media(max-width:768px){.ngx-datex-calendars{flex-direction:column;gap:16px;padding:12px;width:100%;margin:0;box-sizing:border-box}}.ngx-datex-calendar{flex:1;padding:8px;min-width:0}.ngx-datex-calendar.ngx-datex-calendar-right{padding:8px}@media(max-width:768px){.ngx-datex-calendar{padding:0;background:transparent;border:none;box-shadow:none;width:100%;flex:none;min-width:unset;box-sizing:border-box;margin:0}.ngx-datex-calendar .ngx-datex-calendar-grid{width:100%;margin:0}.ngx-datex-calendar .ngx-datex-week{width:100%;display:table-row;margin:0}.ngx-datex-calendar .ngx-datex-day{display:table-cell;width:14.28%;margin:0;padding:0}.ngx-datex-calendar .ngx-datex-days-header{width:100%;margin:0 0 8px}.ngx-datex-calendar .ngx-datex-calendar-header{margin:0 0 16px;padding:0}}.ngx-datex-calendar.ngx-datex-calendar-single{padding:8px}.ngx-datex-calendar-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;padding:0;height:28px}@media(max-width:768px){.ngx-datex-calendar-header{height:40px;margin-bottom:12px;padding:0 4px}}.ngx-datex-nav-button{background:none;border:none;cursor:pointer;padding:4px;border-radius:3px;display:flex;align-items:center;justify-content:center;transition:background-color .2s ease;color:#757575;width:32px;height:28px}@media(max-width:768px){.ngx-datex-nav-button{width:40px;height:40px;border-radius:6px;background:#f5f5f5}.ngx-datex-nav-button mat-icon{font-size:18px}}.ngx-datex-nav-button:hover:not(:disabled){background:#f5f5f5;color:#1976d2}.ngx-datex-nav-button:disabled{opacity:.5;cursor:not-allowed}.ngx-datex-nav-button mat-icon{font-size:18px;width:18px;height:18px;color:inherit}.ngx-datex-month-year{display:flex;align-items:center;gap:4px;font-weight:500;font-size:12px}@media(max-width:768px){.ngx-datex-month-year{font-size:16px;font-weight:600;gap:8px}}.ngx-datex-month-select,.ngx-datex-year-select{border:1px solid #e0e0e0;border-radius:3px;padding:1px 2px;font-size:12px;background:#fff;cursor:pointer;color:#212121;height:auto;margin:0;outline:none;appearance:auto;-webkit-appearance:menulist;-moz-appearance:menulist;min-height:20px}@media(max-width:768px){.ngx-datex-month-select,.ngx-datex-year-select{font-size:14px;padding:4px 8px;border-radius:6px;min-height:32px;border:2px solid #e0e0e0}}.ngx-datex-month-select:focus,.ngx-datex-year-select:focus{outline:none;border-color:#1976d2;box-shadow:0 0 0 2px #1976d233}.ngx-datex-month-select option,.ngx-datex-year-select option{background:#fff;color:#212121}.ngx-datex-month-select{margin-right:2%;width:56%;min-width:50px}.ngx-datex-year-select{width:40%;min-width:50px}.ngx-datex-days-header{display:table;width:100%;margin-bottom:0;border-collapse:collapse;border-spacing:0}@media(max-width:768px){.ngx-datex-days-header{width:100%;margin:0 0 8px;padding:0}}.ngx-datex-days-header-row{display:table-row}.ngx-datex-day-header{display:table-cell;text-align:center;font-size:12px;font-weight:500;width:14.28%;height:28px;line-height:28px;background:#fff;color:#212121;box-sizing:border-box;vertical-align:middle;padding:0}@media(max-width:768px){.ngx-datex-day-header{height:36px;line-height:36px;font-size:13px;font-weight:600;background:#f8f9fa;color:#495057;border-bottom:1px solid #e0e0e0}}.ngx-datex-calendar-grid{display:table;width:100%;margin:0;padding:0;border-collapse:collapse;border-spacing:0;table-layout:fixed}@media(max-width:768px){.ngx-datex-calendar-grid{border-radius:0;overflow:visible;width:100%;margin:0;padding:0}}.ngx-datex-week{display:table-row}.ngx-datex-day{display:table-cell;width:14.28%;height:28px;text-align:center;font-size:12px;padding:0;margin:0;box-sizing:border-box;border:1px solid transparent;background:#fff;cursor:pointer}@media(max-width:768px){.ngx-datex-day{height:40px;font-size:15px;font-weight:500;line-height:40px;vertical-align:middle;border:1px solid #f0f0f0}}.ngx-datex-day{transition:all .15s ease;position:relative;border-radius:4px;line-height:28px;white-space:nowrap;color:#212121;vertical-align:middle}.ngx-datex-day:hover:not(:disabled){background:#f5f5f5;color:#212121}.ngx-datex-day.ngx-datex-day-other-month{background-color:#fff!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important}.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-in-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-end,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-selected,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-active,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-end{background-color:#fff!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;border-radius:4px!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-other-month:hover{background-color:#eee!important;border:1px solid transparent!important;border-color:transparent!important;color:inherit!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-today{font-weight:700;background:#1976d21a;color:#1976d2}.ngx-datex-day.ngx-datex-day-selected,.ngx-datex-day.ngx-datex-day-active{background-color:#1976d2;border-color:transparent;color:#fff}.ngx-datex-day.ngx-datex-day-selected:hover,.ngx-datex-day.ngx-datex-day-active:hover{background-color:#1976d2;opacity:.9}.ngx-datex-day.ngx-datex-day-in-range{background-color:#ebf4f8;border-color:transparent;color:#212121;border-radius:0}.ngx-datex-day.ngx-datex-day-range-start{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:4px 0 0 4px}.ngx-datex-day.ngx-datex-day-range-end{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:0 4px 4px 0}.ngx-datex-day.ngx-datex-day-range-start.ngx-datex-day-range-end{border-radius:4px}.ngx-datex-day.ngx-datex-day-hover-range{background-color:#ebf4f8;border-color:transparent;color:#212121;border-radius:0}.ngx-datex-day.ngx-datex-day-hover-start{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:4px 0 0 4px}.ngx-datex-day.ngx-datex-day-hover-end{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:0 4px 4px 0}.ngx-datex-day.ngx-datex-day-hover-start.ngx-datex-day-hover-end{border-radius:4px}.ngx-datex-day.ngx-datex-day-disabled{color:#bdbdbd;cursor:not-allowed;text-decoration:line-through}.ngx-datex-day.ngx-datex-day-disabled:hover{background:#fff;color:#bdbdbd}.ngx-datex-footer{padding:8px;border-top:1px solid #e0e0e0;background:#fff;border-radius:0 0 8px 8px;display:flex;justify-content:space-between;align-items:center;line-height:10px;vertical-align:middle;margin:0}.ngx-datex-selected-range{font-size:12px;color:#212121;font-weight:400;display:inline-block;padding-right:8px}.ngx-datex-footer-buttons{display:flex;gap:4px}.ngx-datex-cancel-button,.ngx-datex-apply-button{min-width:70px;font-weight:700;font-size:12px;padding:7px 8px;border-radius:3px;border:1px solid transparent;cursor:pointer;transition:all .15s ease-in-out;margin-left:4px}.ngx-datex-cancel-button{background-color:#dc3545;border-color:#dc3545;color:#fff}.ngx-datex-cancel-button:hover:not(:disabled){background-color:#dc3545;border-color:#dc3545;opacity:.8}.ngx-datex-apply-button{background-color:#22c55e;border-color:#22c55e;color:#fff}.ngx-datex-apply-button:disabled{background:#9ca3af;border-color:#9ca3af;opacity:.6;cursor:not-allowed}.ngx-datex-apply-button:hover:not(:disabled){background-color:#22c55e;border-color:#22c55e;opacity:.8}.ngx-datex-mobile .ngx-datex-overlay{position:fixed;inset:auto 0 0;width:100%;max-width:100vw;min-width:100vw;border-radius:12px 12px 0 0;margin:0;max-height:85vh;overflow-y:auto;z-index:999999;box-shadow:0 -4px 20px #00000026}.ngx-datex-mobile .ngx-datex-overlay:before,.ngx-datex-mobile .ngx-datex-overlay:after{display:none}.ngx-datex-mobile .ngx-datex-content{flex-direction:column}.ngx-datex-mobile .ngx-datex-ranges-sidebar{width:100%;border-right:none;border-bottom:1px solid #e0e0e0;border-radius:12px 12px 0 0;flex-direction:row;overflow-x:auto;padding:8px 0;-webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none}.ngx-datex-mobile .ngx-datex-ranges-sidebar::-webkit-scrollbar{display:none}.ngx-datex-mobile .ngx-datex-range-item{white-space:nowrap;padding:8px 12px;margin:0 4px;border-radius:20px;background-color:#f5f5f5;border:1px solid #e0e0e0;flex:0 0 auto;min-width:60px;text-align:center;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}.ngx-datex-mobile .ngx-datex-range-item:hover{background-color:#1976d21a;transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.ngx-datex-mobile .ngx-datex-range-item.ngx-datex-range-active{background-color:#1976d2;color:#fff;border-color:#1976d2;font-weight:600}.ngx-datex-mobile .ngx-datex-calendars{flex-direction:column;padding:6px}.ngx-datex-mobile .ngx-datex-calendar{width:100%;padding:6px}.ngx-datex-mobile .ngx-datex-days-header{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-days-header-row{display:table-row}.ngx-datex-mobile .ngx-datex-calendar-grid{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-week{display:table-row}.ngx-datex-mobile .ngx-datex-week-number-header,.ngx-datex-mobile .ngx-datex-week-number{display:none}.ngx-datex-mobile .ngx-datex-day-header{display:table-cell;height:28px;line-height:28px;font-size:11px;font-weight:600;text-align:center;padding:0;margin:0;box-sizing:border-box;vertical-align:middle}.ngx-datex-mobile .ngx-datex-day{display:table-cell;height:40px;line-height:40px;font-size:15px;font-weight:500;border-radius:6px;-webkit-tap-highlight-color:transparent;touch-action:manipulation;text-align:center;vertical-align:middle}.ngx-datex-mobile .ngx-datex-footer{display:none}.ngx-datex-mobile-header{width:100%;background-color:#fff;color:#333;border-radius:16px 16px 0 0;box-sizing:border-box;position:relative;z-index:3;border-bottom:1px solid #e0e0e0;display:flex;flex-direction:column;gap:0;padding:12px 0 8px}.ngx-datex-mobile-header-content{padding:0 16px 8px;text-align:center;flex:1}.ngx-datex-mobile-header-buttons{display:flex;justify-content:space-between;align-items:center;padding:8px 16px 0;gap:12px;border-top:1px solid #f0f0f0;background-color:#fff}.ngx-datex-mobile-selected-range{display:block;font-size:16px;font-weight:600;line-height:1.3;color:#1a1a1a;margin-bottom:2px}.ngx-datex-mobile-range-label{display:block;font-size:13px;font-weight:500;color:#2196f3;margin-top:2px}.ngx-datex-mobile-cancel-button,.ngx-datex-mobile-apply-button{flex:1;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;transition:all .3s ease;border:2px solid transparent;text-transform:none;letter-spacing:0;min-height:40px;display:flex;align-items:center;justify-content:center}.ngx-datex-mobile-cancel-button{background:#dc3545;border:2px solid #dc3545;color:#fff}.ngx-datex-mobile-cancel-button:hover{background:#c82333;border-color:#c82333;transform:translateY(-1px);box-shadow:0 4px 12px #dc35454d}.ngx-datex-mobile-cancel-button:active{transform:translateY(0);box-shadow:0 2px 4px #dc354533}.ngx-datex-mobile-apply-button{background:#28a745;border:2px solid #28a745;color:#fff}.ngx-datex-mobile-apply-button:hover{background:#218838;border-color:#218838;transform:translateY(-1px);box-shadow:0 4px 16px #28a7454d}.ngx-datex-mobile-apply-button:active{transform:translateY(0);box-shadow:0 2px 8px #28a74533}.ngx-datex-mobile-apply-button:disabled{background:#e0e0e0;border-color:#e0e0e0;color:#9e9e9e;cursor:not-allowed;transform:none;box-shadow:none}.ngx-datex-day:focus{outline:2px solid #1976d2;outline-offset:-2px;z-index:1}.ngx-datex-nav-button:focus{outline:2px solid #1976d2;outline-offset:2px}.ngx-datex-range-item:focus{outline:2px solid #1976d2;outline-offset:-2px}.ngx-datex-overlay{animation:fadeInScale .2s ease-out}.ngx-datex-overlay:before,.ngx-datex-overlay:after{animation:fadeInArrow .2s ease-out .1s both}.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-up:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-up:after,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-down:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-down:after{animation:fadeInArrowCenterHorizontal .2s ease-out .1s both}.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-left:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-left:after,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-right:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-right:after{animation:fadeInArrowCenterVertical .2s ease-out .1s both}@keyframes fadeInScale{0%{opacity:0;transform:scale(.95) translateY(-10px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes fadeInArrow{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes fadeInArrowCenterHorizontal{0%{opacity:0;transform:translate(-50%) scale(.8)}to{opacity:1;transform:translate(-50%) scale(1)}}@keyframes fadeInArrowCenterVertical{0%{opacity:0;transform:translateY(-50%) scale(.8)}to{opacity:1;transform:translateY(-50%) scale(1)}}.ngx-datex-overlay.ngx-datex-single .ngx-datex-calendar.ngx-datex-calendar-right{display:none}.ngx-datex-overlay.ngx-datex-single .ngx-datex-calendar.ngx-datex-calendar-left{padding:8px}.ngx-datex-time-picker{border-top:1px solid #e0e0e0;padding:8px;background:#fff;margin-top:8px}.ngx-datex-time-controls{display:flex;justify-content:center;align-items:center;gap:4px;flex-wrap:nowrap}.ngx-datex-time-separator{font-size:14px;font-weight:700;color:#333;margin:0 2px;line-height:1}.ngx-datex-time-select{border:1px solid #ccc;border-radius:3px;padding:2px 4px;font-size:11px;background:#fff;color:#333;outline:none;cursor:pointer;appearance:auto;-webkit-appearance:menulist;-moz-appearance:menulist;min-height:22px;line-height:1}.ngx-datex-time-select:focus{border-color:#1976d2;box-shadow:0 0 0 1px #1976d233}.ngx-datex-time-select:disabled{background:#f5f5f5;color:#999;cursor:not-allowed;opacity:.6}.ngx-datex-time-select option{padding:2px 4px;font-size:11px}.ngx-datex-hour-select,.ngx-datex-minute-select{width:50px;text-align:center}.ngx-datex-ampm-select{width:45px;text-align:center}.ngx-datex-mobile .ngx-datex-time-picker{margin-top:12px;padding:12px}.ngx-datex-mobile .ngx-datex-time-controls{gap:6px}.ngx-datex-mobile .ngx-datex-time-separator{font-size:16px;margin:0 4px}.ngx-datex-mobile .ngx-datex-time-select{font-size:14px;padding:4px 6px;min-height:32px;border-radius:4px}.ngx-datex-mobile .ngx-datex-hour-select,.ngx-datex-mobile .ngx-datex-minute-select{width:60px}.ngx-datex-mobile .ngx-datex-ampm-select{width:55px}@media(min-width:564px)and (max-width:768px){.ngx-datex-overlay.ngx-datex-overlay-mobile{position:fixed;width:90%;max-width:500px;inset:auto auto 20px 50%;transform:translate(-50%);border-radius:8px}}@media(min-width:564px){.ngx-datex-mobile .ngx-datex-overlay{position:absolute;inset:100% auto auto 0;width:650px;max-width:90vw;border-radius:8px;margin-top:7px;max-height:none}.ngx-datex-mobile .ngx-datex-overlay:before,.ngx-datex-mobile .ngx-datex-overlay:after{display:inline-block}.ngx-datex-mobile .ngx-datex-footer{display:flex}.ngx-datex-mobile .ngx-datex-mobile-header{display:none}.ngx-datex-mobile .ngx-datex-ranges-sidebar{width:140px;flex-direction:column;border-right:1px solid #e0e0e0;border-bottom:none;border-radius:8px 0 0 8px;overflow-x:visible;padding:0}.ngx-datex-mobile .ngx-datex-range-item{white-space:normal;padding:8px 12px;margin:2px 0;border-radius:0;background:none;border:none;text-align:left;min-width:auto}.ngx-datex-mobile .ngx-datex-calendars{flex-direction:row;padding:0}.ngx-datex-mobile .ngx-datex-calendar{padding:8px 0 8px 8px}.ngx-datex-mobile .ngx-datex-calendar.ngx-datex-calendar-right{padding:8px 8px 8px 0}.ngx-datex-mobile .ngx-datex-days-header{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-days-header-row{display:table-row}.ngx-datex-mobile .ngx-datex-calendar-grid{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-week{display:table-row}.ngx-datex-mobile .ngx-datex-week-number-header,.ngx-datex-mobile .ngx-datex-week-number{display:none}.ngx-datex-mobile .ngx-datex-day-header{display:table-cell;width:14.28%;height:28px;line-height:28px;font-size:12px;box-sizing:border-box;text-align:center;vertical-align:middle}.ngx-datex-mobile .ngx-datex-day{display:table-cell;width:14.28%;height:28px;line-height:28px;font-size:12px;border-radius:4px;box-sizing:border-box;text-align:center;vertical-align:middle}}.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-selected,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-active,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-in-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-end,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-end{background-color:#fff!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;border-radius:4px!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-other-month:hover,.ngx-datex-day.ngx-datex-day-other-month:focus{background-color:#eee!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-other-month:focus{background-color:#eee!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;box-shadow:none!important;outline:none!important}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3524
+ }
3525
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxDatex, decorators: [{
3526
+ type: Component,
3527
+ args: [{ selector: 'ngx-datex', imports: [
3528
+ MatButtonModule,
3529
+ MatIconModule,
3530
+ MatInputModule,
3531
+ MatFormFieldModule,
3532
+ MatCheckboxModule,
3533
+ NgClass,
3534
+ ], providers: [
3535
+ {
3536
+ provide: NG_VALUE_ACCESSOR,
3537
+ useExisting: forwardRef(() => NgxDatex),
3538
+ multi: true,
3539
+ },
3540
+ NgxDatexOverlayService,
3541
+ NgxDatexTimePickerService,
3542
+ NgxDatexCalendarService,
3543
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ngx-datex-container\" [class.ngx-datex-mobile]=\"isMobileDevice()\">\r\n <!-- Input Field -->\r\n <mat-form-field\r\n [appearance]=\"appearance()\"\r\n [floatLabel]=\"floatLabel()\"\r\n class=\"ngx-datex-input-field\"\r\n >\r\n @if (label()) {\r\n <mat-label>{{ label() }}</mat-label>\r\n }\r\n\r\n <!-- Checkbox como prefix -->\r\n @if (showCheckbox() && checkboxPosition() === 'prefix') {\r\n <mat-checkbox\r\n matPrefix\r\n [checked]=\"checkboxValue()\"\r\n (change)=\"onCheckboxChange($event.checked)\"\r\n class=\"ngx-datex-checkbox ngx-datex-checkbox-prefix\"\r\n >\r\n </mat-checkbox>\r\n }\r\n\r\n <!-- \u00CDcono como prefix -->\r\n @if (showCalendarIcon() && calendarIconPosition() === 'prefix') {\r\n <mat-icon\r\n matPrefix\r\n class=\"ngx-datex-calendar-icon\"\r\n [class.ngx-datex-icon-active]=\"isOpen()\"\r\n (click)=\"toggle()\"\r\n (keydown.enter)=\"toggle()\"\r\n (keydown.space)=\"toggle()\"\r\n tabindex=\"0\"\r\n role=\"button\"\r\n [attr.aria-label]=\"'Open calendar'\"\r\n >\r\n {{ calendarIcon() }}\r\n </mat-icon>\r\n }\r\n\r\n <input\r\n matInput\r\n #inputElement\r\n [value]=\"displayValue()\"\r\n [placeholder]=\"placeholder()\"\r\n [readonly]=\"readonly()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"ariaLabel()\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (click)=\"onInputClick()\"\r\n (focus)=\"onInputFocus()\"\r\n (blur)=\"onInputBlur()\"\r\n (keydown)=\"onInputKeydown($event)\"\r\n (keyup)=\"onInputKeyup()\"\r\n autocomplete=\"off\"\r\n />\r\n\r\n <!-- \u00CDcono como suffix -->\r\n @if (showCalendarIcon() && calendarIconPosition() === 'suffix') {\r\n <mat-icon\r\n matSuffix\r\n class=\"ngx-datex-calendar-icon\"\r\n [class.ngx-datex-icon-active]=\"isOpen()\"\r\n (click)=\"toggle()\"\r\n (keydown.enter)=\"toggle()\"\r\n (keydown.space)=\"toggle()\"\r\n tabindex=\"0\"\r\n role=\"button\"\r\n [attr.aria-label]=\"'Open calendar'\"\r\n >\r\n {{ calendarIcon() }}\r\n </mat-icon>\r\n }\r\n\r\n <!-- Checkbox como suffix -->\r\n @if (showCheckbox() && checkboxPosition() === 'suffix') {\r\n <mat-checkbox\r\n matSuffix\r\n [checked]=\"checkboxValue()\"\r\n (change)=\"onCheckboxChange($event.checked)\"\r\n class=\"ngx-datex-checkbox ngx-datex-checkbox-suffix\"\r\n >\r\n </mat-checkbox>\r\n }\r\n\r\n @if (hasError()) {\r\n <mat-error>{{ errorMessage() }}</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- Calendar Template for CDK Overlay -->\r\n <ng-template #calendarTemplate>\r\n <div\r\n class=\"ngx-datex-overlay\"\r\n [class.ngx-datex-overlay-mobile]=\"isMobileDevice()\"\r\n [class.ngx-datex-single]=\"singleDatePicker()\"\r\n [class.ngx-datex-arrow-up]=\"arrowDirection() === 'up'\"\r\n [class.ngx-datex-arrow-down]=\"arrowDirection() === 'down'\"\r\n [class.ngx-datex-arrow-left]=\"arrowDirection() === 'left'\"\r\n [class.ngx-datex-arrow-right]=\"arrowDirection() === 'right'\"\r\n [class.ngx-datex-arrow-align-start]=\"arrowAlignment() === 'start'\"\r\n [class.ngx-datex-arrow-align-center]=\"arrowAlignment() === 'center'\"\r\n [class.ngx-datex-arrow-align-end]=\"arrowAlignment() === 'end'\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"headerTitle()\"\r\n tabindex=\"-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n (keydown)=\"onCalendarKeydown($event)\"\r\n >\r\n <!-- Mobile Header (only shown on mobile) -->\r\n @if (isMobileDevice()) {\r\n <div class=\"ngx-datex-mobile-header\">\r\n <div class=\"ngx-datex-mobile-header-content\">\r\n <div class=\"ngx-datex-mobile-selected-range\">\r\n @if (hasStartDate()) {\r\n {{ formattedSelectedRange() }}\r\n } @else {\r\n Selecciona un rango de fechas\r\n }\r\n </div>\r\n <div class=\"ngx-datex-mobile-range-label\">\r\n @if (currentLabel()) {\r\n {{ currentLabel() }}\r\n }\r\n </div>\r\n </div>\r\n <div class=\"ngx-datex-mobile-header-buttons\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-mobile-cancel-button\"\r\n [ngClass]=\"[buttonClasses(), cancelButtonClasses()]\"\r\n (click)=\"cancel()\"\r\n >\r\n {{ locale().cancelLabel || 'Cancelar' }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-mobile-apply-button\"\r\n [ngClass]=\"[buttonClasses(), applyButtonClasses()]\"\r\n [disabled]=\"!canApplyValue()\"\r\n (click)=\"apply()\"\r\n >\r\n {{ locale().applyLabel || 'Aplicar' }}\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"ngx-datex-content\">\r\n <!-- Predefined Ranges - Desktop: Sidebar, Mobile: Horizontal Chips -->\r\n @if (showRanges()) {\r\n <!-- Desktop Ranges Sidebar -->\r\n @if (!isMobileDevice()) {\r\n <div class=\"ngx-datex-ranges-sidebar\">\r\n @for (rangeEntry of rangeEntries(); track rangeEntry.label) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-item\"\r\n [class.ngx-datex-range-active]=\"isRangeActive(rangeEntry.label)\"\r\n (click)=\"selectRange(rangeEntry.label)\"\r\n >\r\n {{ rangeEntry.label }}\r\n </button>\r\n }\r\n <!-- Rango Personalizado -->\r\n @if (showCustomRangeLabel()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-item\"\r\n [class.ngx-datex-range-active]=\"isCustomRange()\"\r\n (click)=\"selectCustomRange()\"\r\n >\r\n {{ locale().customRangeLabel || 'Rango Personalizado' }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Mobile Ranges Chips -->\r\n @if (isMobileDevice()) {\r\n <div class=\"ngx-datex-ranges-chips\">\r\n <div class=\"ngx-datex-ranges-chips-container\">\r\n @for (rangeEntry of rangeEntries(); track rangeEntry.label) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-chip\"\r\n [class.ngx-datex-range-chip-active]=\"isRangeActive(rangeEntry.label)\"\r\n (click)=\"selectRange(rangeEntry.label)\"\r\n >\r\n {{ rangeEntry.label }}\r\n </button>\r\n }\r\n <!-- Rango Personalizado -->\r\n @if (showCustomRangeLabel()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-range-chip\"\r\n [class.ngx-datex-range-chip-active]=\"isCustomRange()\"\r\n (click)=\"selectCustomRange()\"\r\n >\r\n {{ locale().customRangeLabel || 'Personalizado' }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Calendar Section -->\r\n <div class=\"ngx-datex-calendar-section\">\r\n <!-- Calendar Container -->\r\n <div class=\"ngx-datex-calendars\" (mouseleave)=\"onCalendarMouseLeave()\">\r\n <!-- Left Calendar -->\r\n <div\r\n class=\"ngx-datex-calendar ngx-datex-calendar-left\"\r\n [class.ngx-datex-calendar-single]=\"singleDatePicker()\"\r\n >\r\n <div class=\"ngx-datex-calendar-header\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"previousMonth('left')\"\r\n [disabled]=\"!canNavigatePrevious('left')\"\r\n aria-label=\"Mes anterior\"\r\n >\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"ngx-datex-month-year\">\r\n @if (showDropdowns()) {\r\n <select\r\n class=\"ngx-datex-month-select\"\r\n [value]=\"leftCalendarMonthValue()\"\r\n (change)=\"onMonthChange('left', $event)\"\r\n aria-label=\"Seleccionar mes\"\r\n >\r\n @for (month of monthNames(); track $index) {\r\n <option [value]=\"$index\">{{ month }}</option>\r\n }\r\n </select>\r\n\r\n <select\r\n class=\"ngx-datex-year-select\"\r\n [value]=\"leftCalendarYearValue()\"\r\n (change)=\"onYearChange('left', $event)\"\r\n aria-label=\"Seleccionar a\u00F1o\"\r\n >\r\n @for (year of availableYears(); track year) {\r\n <option [value]=\"year\">{{ year }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <span class=\"ngx-datex-month-year-display\">\r\n {{ monthNames()[leftCalendarMonthValue()] }} {{ leftCalendarYearValue() }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"nextMonth('left')\"\r\n [disabled]=\"!canNavigateNext('left')\"\r\n aria-label=\"Mes siguiente\"\r\n >\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Days of Week Header -->\r\n <div class=\"ngx-datex-days-header\">\r\n <div class=\"ngx-datex-days-header-row\">\r\n @for (day of daysOfWeek(); track day) {\r\n <div class=\"ngx-datex-day-header\">{{ day }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Calendar Grid -->\r\n <div class=\"ngx-datex-calendar-grid\">\r\n @for (week of leftCalendarMatrix(); track $index) {\r\n <div class=\"ngx-datex-week\">\r\n @for (date of week; track date.getTime()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-day\"\r\n [class.ngx-datex-day-other-month]=\"isOtherMonth(date, leftCalendarMonth())\"\r\n [class.ngx-datex-day-today]=\"isToday(date)\"\r\n [class.ngx-datex-day-selected]=\"isSelected(date)\"\r\n [class.ngx-datex-day-active]=\"isSelected(date)\"\r\n [class.ngx-datex-day-in-range]=\"isInRange(date)\"\r\n [class.ngx-datex-day-range-start]=\"isRangeStart(date)\"\r\n [class.ngx-datex-day-range-end]=\"isRangeEnd(date)\"\r\n [class.ngx-datex-day-hover-range]=\"\r\n isHovered(date) && !isHoverStart(date) && !isHoverEnd(date)\r\n \"\r\n [class.ngx-datex-day-hover-start]=\"isHoverStart(date)\"\r\n [class.ngx-datex-day-hover-end]=\"isHoverEnd(date)\"\r\n [class.ngx-datex-day-disabled]=\"isDisabled(date)\"\r\n [disabled]=\"isDisabled(date)\"\r\n (click)=\"selectDate(date)\"\r\n (mouseenter)=\"onDateHover(date)\"\r\n [attr.aria-label]=\"formatDateForAria(date)\"\r\n >\r\n {{ date.getDate() }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Time Picker for Left Calendar -->\r\n @if (timePicker()) {\r\n <div class=\"ngx-datex-time-picker\">\r\n <div class=\"ngx-datex-time-controls\">\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-hour-select\"\r\n [value]=\"selectedStartHour()\"\r\n (change)=\"onStartHourChange($event)\"\r\n aria-label=\"Seleccionar hora\"\r\n >\r\n @if (timePicker24Hour()) {\r\n @for (hourOption of availableStartHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedStartHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n } @else {\r\n @for (hourOption of availableStartHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedStartHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value }}\r\n </option>\r\n }\r\n }\r\n </select>\r\n\r\n <span class=\"ngx-datex-time-separator\">:</span>\r\n\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-minute-select\"\r\n [value]=\"selectedStartMinute()\"\r\n (change)=\"onStartMinuteChange($event)\"\r\n aria-label=\"Seleccionar minuto\"\r\n >\r\n @for (minuteOption of availableStartMinutes(); track minuteOption.value) {\r\n <option\r\n [value]=\"minuteOption.value\"\r\n [selected]=\"selectedStartMinute() === minuteOption.value\"\r\n [disabled]=\"minuteOption.disabled\"\r\n >\r\n {{ minuteOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n </select>\r\n\r\n @if (!timePicker24Hour()) {\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-ampm-select\"\r\n [value]=\"selectedStartAmPm()\"\r\n (change)=\"onStartAmPmChange($event)\"\r\n aria-label=\"Seleccionar AM/PM\"\r\n >\r\n <option value=\"AM\">AM</option>\r\n <option value=\"PM\">PM</option>\r\n </select>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Right Calendar (for range picker) -->\r\n @if (!singleDatePicker()) {\r\n <div class=\"ngx-datex-calendar ngx-datex-calendar-right\">\r\n <div class=\"ngx-datex-calendar-header\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"previousMonth('right')\"\r\n [disabled]=\"!canNavigatePrevious('right')\"\r\n aria-label=\"Mes anterior\"\r\n >\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"ngx-datex-month-year\">\r\n @if (showDropdowns()) {\r\n <select\r\n class=\"ngx-datex-month-select\"\r\n [value]=\"rightCalendarMonthValue()\"\r\n (change)=\"onMonthChange('right', $event)\"\r\n aria-label=\"Seleccionar mes\"\r\n >\r\n @for (month of monthNames(); track $index) {\r\n <option [value]=\"$index\">{{ month }}</option>\r\n }\r\n </select>\r\n\r\n <select\r\n class=\"ngx-datex-year-select\"\r\n [value]=\"rightCalendarYearValue()\"\r\n (change)=\"onYearChange('right', $event)\"\r\n aria-label=\"Seleccionar a\u00F1o\"\r\n >\r\n @for (year of availableYears(); track year) {\r\n <option [value]=\"year\">{{ year }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <span class=\"ngx-datex-month-year-display\">\r\n {{ monthNames()[rightCalendarMonthValue()] }} {{ rightCalendarYearValue() }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-nav-button\"\r\n (click)=\"nextMonth('right')\"\r\n [disabled]=\"!canNavigateNext('right')\"\r\n aria-label=\"Mes siguiente\"\r\n >\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Days of Week Header -->\r\n <div class=\"ngx-datex-days-header\">\r\n <div class=\"ngx-datex-days-header-row\">\r\n @for (day of daysOfWeek(); track day) {\r\n <div class=\"ngx-datex-day-header\">{{ day }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Calendar Grid -->\r\n <div class=\"ngx-datex-calendar-grid\">\r\n @for (week of rightCalendarMatrix(); track $index) {\r\n <div class=\"ngx-datex-week\">\r\n @for (date of week; track date.getTime()) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-day\"\r\n [class.ngx-datex-day-other-month]=\"\r\n isOtherMonth(date, rightCalendarMonth())\r\n \"\r\n [class.ngx-datex-day-today]=\"isToday(date)\"\r\n [class.ngx-datex-day-selected]=\"isSelected(date)\"\r\n [class.ngx-datex-day-active]=\"isSelected(date)\"\r\n [class.ngx-datex-day-in-range]=\"isInRange(date)\"\r\n [class.ngx-datex-day-range-start]=\"isRangeStart(date)\"\r\n [class.ngx-datex-day-range-end]=\"isRangeEnd(date)\"\r\n [class.ngx-datex-day-hover-range]=\"\r\n isHovered(date) && !isHoverStart(date) && !isHoverEnd(date)\r\n \"\r\n [class.ngx-datex-day-hover-start]=\"isHoverStart(date)\"\r\n [class.ngx-datex-day-hover-end]=\"isHoverEnd(date)\"\r\n [class.ngx-datex-day-disabled]=\"isDisabled(date)\"\r\n [disabled]=\"isDisabled(date)\"\r\n (click)=\"selectDate(date)\"\r\n (mouseenter)=\"onDateHover(date)\"\r\n [attr.aria-label]=\"formatDateForAria(date)\"\r\n >\r\n {{ date.getDate() }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Time Picker for Right Calendar -->\r\n @if (timePicker() && !singleDatePicker()) {\r\n <div class=\"ngx-datex-time-picker\">\r\n <div class=\"ngx-datex-time-controls\">\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-hour-select\"\r\n [value]=\"selectedEndHour()\"\r\n [disabled]=\"!hasEndDate()\"\r\n (change)=\"onEndHourChange($event)\"\r\n aria-label=\"Seleccionar hora\"\r\n >\r\n @if (timePicker24Hour()) {\r\n @for (hourOption of availableEndHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedEndHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n } @else {\r\n @for (hourOption of availableEndHours(); track hourOption.value) {\r\n <option\r\n [value]=\"hourOption.value\"\r\n [selected]=\"selectedEndHour() === hourOption.value\"\r\n [disabled]=\"hourOption.disabled\"\r\n >\r\n {{ hourOption.value }}\r\n </option>\r\n }\r\n }\r\n </select>\r\n\r\n <span class=\"ngx-datex-time-separator\">:</span>\r\n\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-minute-select\"\r\n [value]=\"selectedEndMinute()\"\r\n [disabled]=\"!hasEndDate()\"\r\n (change)=\"onEndMinuteChange($event)\"\r\n aria-label=\"Seleccionar minuto\"\r\n >\r\n @for (minuteOption of availableEndMinutes(); track minuteOption.value) {\r\n <option\r\n [value]=\"minuteOption.value\"\r\n [selected]=\"selectedEndMinute() === minuteOption.value\"\r\n [disabled]=\"minuteOption.disabled\"\r\n >\r\n {{ minuteOption.value.toString().padStart(2, '0') }}\r\n </option>\r\n }\r\n </select>\r\n\r\n @if (!timePicker24Hour()) {\r\n <select\r\n class=\"ngx-datex-time-select ngx-datex-ampm-select\"\r\n [value]=\"selectedEndAmPm()\"\r\n [disabled]=\"!hasEndDate()\"\r\n (change)=\"onEndAmPmChange($event)\"\r\n aria-label=\"Seleccionar AM/PM\"\r\n >\r\n <option value=\"AM\">AM</option>\r\n <option value=\"PM\">PM</option>\r\n </select>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Footer with selected range and buttons (desktop only) -->\r\n @if (!isMobileDevice()) {\r\n <div class=\"ngx-datex-footer\">\r\n <div class=\"ngx-datex-selected-range\">\r\n @if (hasStartDate()) {\r\n {{ formattedSelectedRange() }}\r\n } @else {\r\n Selecciona un rango de fechas\r\n }\r\n </div>\r\n <div class=\"ngx-datex-footer-buttons\">\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-cancel-button\"\r\n [ngClass]=\"[buttonClasses(), cancelButtonClasses()]\"\r\n (click)=\"cancel()\"\r\n >\r\n {{ locale().cancelLabel || 'Cancelar' }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"ngx-datex-apply-button\"\r\n [ngClass]=\"[buttonClasses(), applyButtonClasses()]\"\r\n [disabled]=\"!canApplyValue()\"\r\n (click)=\"apply()\"\r\n >\r\n {{ locale().applyLabel || 'Aplicar' }}\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n</div>\r\n", styles: [".ngx-datex-container{position:relative;display:inline-block;width:100%}.ngx-datex-input-field{width:100%}.ngx-datex-calendar-icon{cursor:pointer;transition:color .2s ease;font-size:20px}.ngx-datex-calendar-icon:hover,.ngx-datex-calendar-icon.ngx-datex-icon-active{color:var(--ngx-datex-primary-color, #1976d2)}.ngx-datex-overlay-panel{z-index:1000}.ngx-datex-mobile-overlay{z-index:1001}.ngx-datex-overlay{background:#fff;border-radius:8px;box-shadow:0 5px 15px #00000026,0 2px 4px #0000001f;border:1px solid #e0e0e0;width:650px;font-family:Roboto,sans-serif;font-size:12px;line-height:1em;position:relative;color:#212121}.ngx-datex-overlay:before,.ngx-datex-overlay:after{position:absolute;content:\"\";display:none;z-index:10}.ngx-datex-overlay.ngx-datex-arrow-down:before{display:block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #e0e0e0;top:-7px}.ngx-datex-overlay.ngx-datex-arrow-down:after{display:block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;top:-6px}.ngx-datex-overlay.ngx-datex-arrow-up:before{display:block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #e0e0e0;bottom:-7px}.ngx-datex-overlay.ngx-datex-arrow-up:after{display:block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #ffffff;bottom:-6px}.ngx-datex-overlay.ngx-datex-arrow-right:before{display:block;border-top:7px solid transparent;border-bottom:7px solid transparent;border-right:7px solid #e0e0e0;left:-7px}.ngx-datex-overlay.ngx-datex-arrow-right:after{display:block;border-top:6px solid transparent;border-bottom:6px solid transparent;border-right:6px solid #ffffff;left:-6px}.ngx-datex-overlay.ngx-datex-arrow-left:before{display:block;border-top:7px solid transparent;border-bottom:7px solid transparent;border-left:7px solid #e0e0e0;right:-7px}.ngx-datex-overlay.ngx-datex-arrow-left:after{display:block;border-top:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid #ffffff;right:-6px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-start:before,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-start:before{left:20px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-start:after,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-start:after{left:21px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-center:before,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-center:before{left:50%;transform:translate(-50%)}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-center:after,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-center:after{left:50%;transform:translate(-50%)}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-end:before,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-end:before{right:20px}.ngx-datex-overlay.ngx-datex-arrow-up.ngx-datex-arrow-align-end:after,.ngx-datex-overlay.ngx-datex-arrow-down.ngx-datex-arrow-align-end:after{right:21px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-start:before,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-start:before{top:20px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-start:after,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-start:after{top:21px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-center:before,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-center:before{top:50%;transform:translateY(-50%)}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-center:after,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-center:after{top:50%;transform:translateY(-50%)}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-end:before,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-end:before{bottom:20px}.ngx-datex-overlay.ngx-datex-arrow-left.ngx-datex-arrow-align-end:after,.ngx-datex-overlay.ngx-datex-arrow-right.ngx-datex-arrow-align-end:after{bottom:21px}.ngx-datex-overlay.ngx-datex-single{width:300px}.ngx-datex-overlay.ngx-datex-single .ngx-datex-ranges-sidebar{display:none}.ngx-datex-overlay.ngx-datex-overlay-mobile{width:100vw;max-width:100vw;min-width:100vw;border-radius:16px 16px 0 0;max-height:90vh;overflow-y:auto;box-shadow:0 -8px 32px #0003;border:none;margin:0;padding:0}.ngx-datex-overlay.ngx-datex-overlay-mobile:before,.ngx-datex-overlay.ngx-datex-overlay-mobile:after{display:none}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-content{min-height:auto;padding:0;margin:0;width:100%;display:flex;flex-direction:column}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-calendar-section{padding:0;margin:0;width:100%;flex:1}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-calendars{padding:16px;gap:20px;margin:0;width:100%;box-sizing:border-box}.ngx-datex-overlay.ngx-datex-overlay-mobile .ngx-datex-calendar{padding:0;margin:0;border-radius:0;background:transparent;border:none;box-shadow:none;width:100%}.ngx-datex-content{display:flex;min-height:350px}.ngx-datex-ranges-sidebar{width:140px;background:#fff;border-right:1px solid #e0e0e0;padding:0;border-radius:8px 0 0 8px;display:flex;flex-direction:column}.ngx-datex-range-item{background:none;border:none;padding:8px 12px;text-align:left;cursor:pointer;font-size:12px;color:#212121;transition:all .2s ease;border-radius:0;margin:2px 0}.ngx-datex-range-item:hover{background:#f5f5f5;color:#1976d2}.ngx-datex-range-item.ngx-datex-range-active{background:#1976d2;color:#fff;font-weight:500}.ngx-datex-ranges-chips{width:100%;background:#fff;border-bottom:1px solid #e0e0e0;padding:16px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none}.ngx-datex-ranges-chips::-webkit-scrollbar{display:none}.ngx-datex-ranges-chips-container{display:flex;gap:12px;padding:0 16px;min-width:max-content}.ngx-datex-range-chip{background:#f8f9fa;border:1px solid #dee2e6;border-radius:20px;padding:10px 16px;font-size:14px;font-weight:500;color:#495057;cursor:pointer;transition:all .3s ease;white-space:nowrap;flex-shrink:0;min-height:40px;display:flex;align-items:center;justify-content:center;min-width:80px;text-align:center}.ngx-datex-range-chip:hover{background:#e3f2fd;border-color:#2196f3;color:#1976d2;transform:translateY(-2px);box-shadow:0 4px 12px #2196f340}.ngx-datex-range-chip.ngx-datex-range-chip-active{background:linear-gradient(135deg,#2196f3,#1976d2);border-color:#1976d2;color:#fff;font-weight:600;box-shadow:0 4px 16px #1976d266;transform:translateY(-1px)}.ngx-datex-range-chip:active{transform:translateY(0)}.ngx-datex-calendar-section{flex:1;display:flex;flex-direction:column}@media(max-width:768px){.ngx-datex-ranges-chips+.ngx-datex-calendar-section{padding-top:0}.ngx-datex-calendar-section{padding:0;background:#fff;width:100%;margin:0}}.ngx-datex-calendars{display:flex;padding:0;gap:0;flex:1}@media(max-width:768px){.ngx-datex-calendars{flex-direction:column;gap:16px;padding:12px;width:100%;margin:0;box-sizing:border-box}}.ngx-datex-calendar{flex:1;padding:8px;min-width:0}.ngx-datex-calendar.ngx-datex-calendar-right{padding:8px}@media(max-width:768px){.ngx-datex-calendar{padding:0;background:transparent;border:none;box-shadow:none;width:100%;flex:none;min-width:unset;box-sizing:border-box;margin:0}.ngx-datex-calendar .ngx-datex-calendar-grid{width:100%;margin:0}.ngx-datex-calendar .ngx-datex-week{width:100%;display:table-row;margin:0}.ngx-datex-calendar .ngx-datex-day{display:table-cell;width:14.28%;margin:0;padding:0}.ngx-datex-calendar .ngx-datex-days-header{width:100%;margin:0 0 8px}.ngx-datex-calendar .ngx-datex-calendar-header{margin:0 0 16px;padding:0}}.ngx-datex-calendar.ngx-datex-calendar-single{padding:8px}.ngx-datex-calendar-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;padding:0;height:28px}@media(max-width:768px){.ngx-datex-calendar-header{height:40px;margin-bottom:12px;padding:0 4px}}.ngx-datex-nav-button{background:none;border:none;cursor:pointer;padding:4px;border-radius:3px;display:flex;align-items:center;justify-content:center;transition:background-color .2s ease;color:#757575;width:32px;height:28px}@media(max-width:768px){.ngx-datex-nav-button{width:40px;height:40px;border-radius:6px;background:#f5f5f5}.ngx-datex-nav-button mat-icon{font-size:18px}}.ngx-datex-nav-button:hover:not(:disabled){background:#f5f5f5;color:#1976d2}.ngx-datex-nav-button:disabled{opacity:.5;cursor:not-allowed}.ngx-datex-nav-button mat-icon{font-size:18px;width:18px;height:18px;color:inherit}.ngx-datex-month-year{display:flex;align-items:center;gap:4px;font-weight:500;font-size:12px}@media(max-width:768px){.ngx-datex-month-year{font-size:16px;font-weight:600;gap:8px}}.ngx-datex-month-select,.ngx-datex-year-select{border:1px solid #e0e0e0;border-radius:3px;padding:1px 2px;font-size:12px;background:#fff;cursor:pointer;color:#212121;height:auto;margin:0;outline:none;appearance:auto;-webkit-appearance:menulist;-moz-appearance:menulist;min-height:20px}@media(max-width:768px){.ngx-datex-month-select,.ngx-datex-year-select{font-size:14px;padding:4px 8px;border-radius:6px;min-height:32px;border:2px solid #e0e0e0}}.ngx-datex-month-select:focus,.ngx-datex-year-select:focus{outline:none;border-color:#1976d2;box-shadow:0 0 0 2px #1976d233}.ngx-datex-month-select option,.ngx-datex-year-select option{background:#fff;color:#212121}.ngx-datex-month-select{margin-right:2%;width:56%;min-width:50px}.ngx-datex-year-select{width:40%;min-width:50px}.ngx-datex-days-header{display:table;width:100%;margin-bottom:0;border-collapse:collapse;border-spacing:0}@media(max-width:768px){.ngx-datex-days-header{width:100%;margin:0 0 8px;padding:0}}.ngx-datex-days-header-row{display:table-row}.ngx-datex-day-header{display:table-cell;text-align:center;font-size:12px;font-weight:500;width:14.28%;height:28px;line-height:28px;background:#fff;color:#212121;box-sizing:border-box;vertical-align:middle;padding:0}@media(max-width:768px){.ngx-datex-day-header{height:36px;line-height:36px;font-size:13px;font-weight:600;background:#f8f9fa;color:#495057;border-bottom:1px solid #e0e0e0}}.ngx-datex-calendar-grid{display:table;width:100%;margin:0;padding:0;border-collapse:collapse;border-spacing:0;table-layout:fixed}@media(max-width:768px){.ngx-datex-calendar-grid{border-radius:0;overflow:visible;width:100%;margin:0;padding:0}}.ngx-datex-week{display:table-row}.ngx-datex-day{display:table-cell;width:14.28%;height:28px;text-align:center;font-size:12px;padding:0;margin:0;box-sizing:border-box;border:1px solid transparent;background:#fff;cursor:pointer}@media(max-width:768px){.ngx-datex-day{height:40px;font-size:15px;font-weight:500;line-height:40px;vertical-align:middle;border:1px solid #f0f0f0}}.ngx-datex-day{transition:all .15s ease;position:relative;border-radius:4px;line-height:28px;white-space:nowrap;color:#212121;vertical-align:middle}.ngx-datex-day:hover:not(:disabled){background:#f5f5f5;color:#212121}.ngx-datex-day.ngx-datex-day-other-month{background-color:#fff!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important}.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-in-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-end,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-selected,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-active,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-end{background-color:#fff!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;border-radius:4px!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-other-month:hover{background-color:#eee!important;border:1px solid transparent!important;border-color:transparent!important;color:inherit!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-today{font-weight:700;background:#1976d21a;color:#1976d2}.ngx-datex-day.ngx-datex-day-selected,.ngx-datex-day.ngx-datex-day-active{background-color:#1976d2;border-color:transparent;color:#fff}.ngx-datex-day.ngx-datex-day-selected:hover,.ngx-datex-day.ngx-datex-day-active:hover{background-color:#1976d2;opacity:.9}.ngx-datex-day.ngx-datex-day-in-range{background-color:#ebf4f8;border-color:transparent;color:#212121;border-radius:0}.ngx-datex-day.ngx-datex-day-range-start{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:4px 0 0 4px}.ngx-datex-day.ngx-datex-day-range-end{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:0 4px 4px 0}.ngx-datex-day.ngx-datex-day-range-start.ngx-datex-day-range-end{border-radius:4px}.ngx-datex-day.ngx-datex-day-hover-range{background-color:#ebf4f8;border-color:transparent;color:#212121;border-radius:0}.ngx-datex-day.ngx-datex-day-hover-start{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:4px 0 0 4px}.ngx-datex-day.ngx-datex-day-hover-end{background-color:#1976d2;border-color:transparent;color:#fff;border-radius:0 4px 4px 0}.ngx-datex-day.ngx-datex-day-hover-start.ngx-datex-day-hover-end{border-radius:4px}.ngx-datex-day.ngx-datex-day-disabled{color:#bdbdbd;cursor:not-allowed;text-decoration:line-through}.ngx-datex-day.ngx-datex-day-disabled:hover{background:#fff;color:#bdbdbd}.ngx-datex-footer{padding:8px;border-top:1px solid #e0e0e0;background:#fff;border-radius:0 0 8px 8px;display:flex;justify-content:space-between;align-items:center;line-height:10px;vertical-align:middle;margin:0}.ngx-datex-selected-range{font-size:12px;color:#212121;font-weight:400;display:inline-block;padding-right:8px}.ngx-datex-footer-buttons{display:flex;gap:4px}.ngx-datex-cancel-button,.ngx-datex-apply-button{min-width:70px;font-weight:700;font-size:12px;padding:7px 8px;border-radius:3px;border:1px solid transparent;cursor:pointer;transition:all .15s ease-in-out;margin-left:4px}.ngx-datex-cancel-button{background-color:#dc3545;border-color:#dc3545;color:#fff}.ngx-datex-cancel-button:hover:not(:disabled){background-color:#dc3545;border-color:#dc3545;opacity:.8}.ngx-datex-apply-button{background-color:#22c55e;border-color:#22c55e;color:#fff}.ngx-datex-apply-button:disabled{background:#9ca3af;border-color:#9ca3af;opacity:.6;cursor:not-allowed}.ngx-datex-apply-button:hover:not(:disabled){background-color:#22c55e;border-color:#22c55e;opacity:.8}.ngx-datex-mobile .ngx-datex-overlay{position:fixed;inset:auto 0 0;width:100%;max-width:100vw;min-width:100vw;border-radius:12px 12px 0 0;margin:0;max-height:85vh;overflow-y:auto;z-index:999999;box-shadow:0 -4px 20px #00000026}.ngx-datex-mobile .ngx-datex-overlay:before,.ngx-datex-mobile .ngx-datex-overlay:after{display:none}.ngx-datex-mobile .ngx-datex-content{flex-direction:column}.ngx-datex-mobile .ngx-datex-ranges-sidebar{width:100%;border-right:none;border-bottom:1px solid #e0e0e0;border-radius:12px 12px 0 0;flex-direction:row;overflow-x:auto;padding:8px 0;-webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none}.ngx-datex-mobile .ngx-datex-ranges-sidebar::-webkit-scrollbar{display:none}.ngx-datex-mobile .ngx-datex-range-item{white-space:nowrap;padding:8px 12px;margin:0 4px;border-radius:20px;background-color:#f5f5f5;border:1px solid #e0e0e0;flex:0 0 auto;min-width:60px;text-align:center;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}.ngx-datex-mobile .ngx-datex-range-item:hover{background-color:#1976d21a;transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.ngx-datex-mobile .ngx-datex-range-item.ngx-datex-range-active{background-color:#1976d2;color:#fff;border-color:#1976d2;font-weight:600}.ngx-datex-mobile .ngx-datex-calendars{flex-direction:column;padding:6px}.ngx-datex-mobile .ngx-datex-calendar{width:100%;padding:6px}.ngx-datex-mobile .ngx-datex-days-header{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-days-header-row{display:table-row}.ngx-datex-mobile .ngx-datex-calendar-grid{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-week{display:table-row}.ngx-datex-mobile .ngx-datex-week-number-header,.ngx-datex-mobile .ngx-datex-week-number{display:none}.ngx-datex-mobile .ngx-datex-day-header{display:table-cell;height:28px;line-height:28px;font-size:11px;font-weight:600;text-align:center;padding:0;margin:0;box-sizing:border-box;vertical-align:middle}.ngx-datex-mobile .ngx-datex-day{display:table-cell;height:40px;line-height:40px;font-size:15px;font-weight:500;border-radius:6px;-webkit-tap-highlight-color:transparent;touch-action:manipulation;text-align:center;vertical-align:middle}.ngx-datex-mobile .ngx-datex-footer{display:none}.ngx-datex-mobile-header{width:100%;background-color:#fff;color:#333;border-radius:16px 16px 0 0;box-sizing:border-box;position:relative;z-index:3;border-bottom:1px solid #e0e0e0;display:flex;flex-direction:column;gap:0;padding:12px 0 8px}.ngx-datex-mobile-header-content{padding:0 16px 8px;text-align:center;flex:1}.ngx-datex-mobile-header-buttons{display:flex;justify-content:space-between;align-items:center;padding:8px 16px 0;gap:12px;border-top:1px solid #f0f0f0;background-color:#fff}.ngx-datex-mobile-selected-range{display:block;font-size:16px;font-weight:600;line-height:1.3;color:#1a1a1a;margin-bottom:2px}.ngx-datex-mobile-range-label{display:block;font-size:13px;font-weight:500;color:#2196f3;margin-top:2px}.ngx-datex-mobile-cancel-button,.ngx-datex-mobile-apply-button{flex:1;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;transition:all .3s ease;border:2px solid transparent;text-transform:none;letter-spacing:0;min-height:40px;display:flex;align-items:center;justify-content:center}.ngx-datex-mobile-cancel-button{background:#dc3545;border:2px solid #dc3545;color:#fff}.ngx-datex-mobile-cancel-button:hover{background:#c82333;border-color:#c82333;transform:translateY(-1px);box-shadow:0 4px 12px #dc35454d}.ngx-datex-mobile-cancel-button:active{transform:translateY(0);box-shadow:0 2px 4px #dc354533}.ngx-datex-mobile-apply-button{background:#28a745;border:2px solid #28a745;color:#fff}.ngx-datex-mobile-apply-button:hover{background:#218838;border-color:#218838;transform:translateY(-1px);box-shadow:0 4px 16px #28a7454d}.ngx-datex-mobile-apply-button:active{transform:translateY(0);box-shadow:0 2px 8px #28a74533}.ngx-datex-mobile-apply-button:disabled{background:#e0e0e0;border-color:#e0e0e0;color:#9e9e9e;cursor:not-allowed;transform:none;box-shadow:none}.ngx-datex-day:focus{outline:2px solid #1976d2;outline-offset:-2px;z-index:1}.ngx-datex-nav-button:focus{outline:2px solid #1976d2;outline-offset:2px}.ngx-datex-range-item:focus{outline:2px solid #1976d2;outline-offset:-2px}.ngx-datex-overlay{animation:fadeInScale .2s ease-out}.ngx-datex-overlay:before,.ngx-datex-overlay:after{animation:fadeInArrow .2s ease-out .1s both}.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-up:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-up:after,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-down:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-down:after{animation:fadeInArrowCenterHorizontal .2s ease-out .1s both}.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-left:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-left:after,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-right:before,.ngx-datex-overlay.ngx-datex-arrow-align-center.ngx-datex-arrow-right:after{animation:fadeInArrowCenterVertical .2s ease-out .1s both}@keyframes fadeInScale{0%{opacity:0;transform:scale(.95) translateY(-10px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes fadeInArrow{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes fadeInArrowCenterHorizontal{0%{opacity:0;transform:translate(-50%) scale(.8)}to{opacity:1;transform:translate(-50%) scale(1)}}@keyframes fadeInArrowCenterVertical{0%{opacity:0;transform:translateY(-50%) scale(.8)}to{opacity:1;transform:translateY(-50%) scale(1)}}.ngx-datex-overlay.ngx-datex-single .ngx-datex-calendar.ngx-datex-calendar-right{display:none}.ngx-datex-overlay.ngx-datex-single .ngx-datex-calendar.ngx-datex-calendar-left{padding:8px}.ngx-datex-time-picker{border-top:1px solid #e0e0e0;padding:8px;background:#fff;margin-top:8px}.ngx-datex-time-controls{display:flex;justify-content:center;align-items:center;gap:4px;flex-wrap:nowrap}.ngx-datex-time-separator{font-size:14px;font-weight:700;color:#333;margin:0 2px;line-height:1}.ngx-datex-time-select{border:1px solid #ccc;border-radius:3px;padding:2px 4px;font-size:11px;background:#fff;color:#333;outline:none;cursor:pointer;appearance:auto;-webkit-appearance:menulist;-moz-appearance:menulist;min-height:22px;line-height:1}.ngx-datex-time-select:focus{border-color:#1976d2;box-shadow:0 0 0 1px #1976d233}.ngx-datex-time-select:disabled{background:#f5f5f5;color:#999;cursor:not-allowed;opacity:.6}.ngx-datex-time-select option{padding:2px 4px;font-size:11px}.ngx-datex-hour-select,.ngx-datex-minute-select{width:50px;text-align:center}.ngx-datex-ampm-select{width:45px;text-align:center}.ngx-datex-mobile .ngx-datex-time-picker{margin-top:12px;padding:12px}.ngx-datex-mobile .ngx-datex-time-controls{gap:6px}.ngx-datex-mobile .ngx-datex-time-separator{font-size:16px;margin:0 4px}.ngx-datex-mobile .ngx-datex-time-select{font-size:14px;padding:4px 6px;min-height:32px;border-radius:4px}.ngx-datex-mobile .ngx-datex-hour-select,.ngx-datex-mobile .ngx-datex-minute-select{width:60px}.ngx-datex-mobile .ngx-datex-ampm-select{width:55px}@media(min-width:564px)and (max-width:768px){.ngx-datex-overlay.ngx-datex-overlay-mobile{position:fixed;width:90%;max-width:500px;inset:auto auto 20px 50%;transform:translate(-50%);border-radius:8px}}@media(min-width:564px){.ngx-datex-mobile .ngx-datex-overlay{position:absolute;inset:100% auto auto 0;width:650px;max-width:90vw;border-radius:8px;margin-top:7px;max-height:none}.ngx-datex-mobile .ngx-datex-overlay:before,.ngx-datex-mobile .ngx-datex-overlay:after{display:inline-block}.ngx-datex-mobile .ngx-datex-footer{display:flex}.ngx-datex-mobile .ngx-datex-mobile-header{display:none}.ngx-datex-mobile .ngx-datex-ranges-sidebar{width:140px;flex-direction:column;border-right:1px solid #e0e0e0;border-bottom:none;border-radius:8px 0 0 8px;overflow-x:visible;padding:0}.ngx-datex-mobile .ngx-datex-range-item{white-space:normal;padding:8px 12px;margin:2px 0;border-radius:0;background:none;border:none;text-align:left;min-width:auto}.ngx-datex-mobile .ngx-datex-calendars{flex-direction:row;padding:0}.ngx-datex-mobile .ngx-datex-calendar{padding:8px 0 8px 8px}.ngx-datex-mobile .ngx-datex-calendar.ngx-datex-calendar-right{padding:8px 8px 8px 0}.ngx-datex-mobile .ngx-datex-days-header{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-days-header-row{display:table-row}.ngx-datex-mobile .ngx-datex-calendar-grid{display:table;width:100%;border-collapse:collapse;border-spacing:0}.ngx-datex-mobile .ngx-datex-week{display:table-row}.ngx-datex-mobile .ngx-datex-week-number-header,.ngx-datex-mobile .ngx-datex-week-number{display:none}.ngx-datex-mobile .ngx-datex-day-header{display:table-cell;width:14.28%;height:28px;line-height:28px;font-size:12px;box-sizing:border-box;text-align:center;vertical-align:middle}.ngx-datex-mobile .ngx-datex-day{display:table-cell;width:14.28%;height:28px;line-height:28px;font-size:12px;border-radius:4px;box-sizing:border-box;text-align:center;vertical-align:middle}}.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-selected,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-active,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-in-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-range-end,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-range,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-start,.ngx-datex-day.ngx-datex-day-other-month.ngx-datex-day-hover-end{background-color:#fff!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;border-radius:4px!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-other-month:hover,.ngx-datex-day.ngx-datex-day-other-month:focus{background-color:#eee!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;box-shadow:none!important;outline:none!important}.ngx-datex-day.ngx-datex-day-other-month:focus{background-color:#eee!important;border:1px solid transparent!important;border-color:transparent!important;color:#999!important;box-shadow:none!important;outline:none!important}\n"] }]
3544
+ }], ctorParameters: () => [], propDecorators: { inputElement: [{
3545
+ type: ViewChild,
3546
+ args: ['inputElement']
3547
+ }], calendarTemplate: [{
3548
+ type: ViewChild,
3549
+ args: ['calendarTemplate']
3550
+ }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], floatLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatLabel", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], calendarIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarIcon", required: false }] }], showCalendarIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCalendarIcon", required: false }] }], calendarIconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarIconPosition", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], checkboxPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkboxPosition", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], showFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooter", required: false }] }], singleDatePicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "singleDatePicker", required: false }] }], autoApply: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoApply", required: false }] }], showDropdowns: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDropdowns", required: false }] }], timePicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "timePicker", required: false }] }], timePicker24Hour: [{ type: i0.Input, args: [{ isSignal: true, alias: "timePicker24Hour", required: false }] }], timePickerIncrement: [{ type: i0.Input, args: [{ isSignal: true, alias: "timePickerIncrement", required: false }] }], timePickerSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "timePickerSeconds", required: false }] }], linkedCalendars: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkedCalendars", required: false }] }], autoUpdateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoUpdateInput", required: false }] }], alwaysShowCalendars: [{ type: i0.Input, args: [{ isSignal: true, alias: "alwaysShowCalendars", required: false }] }], showCustomRangeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCustomRangeLabel", required: false }] }], startDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "startDate", required: false }] }], endDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "endDate", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], maxSpan: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSpan", required: false }] }], showWeekNumbers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showWeekNumbers", required: false }] }], showISOWeekNumbers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showISOWeekNumbers", required: false }] }], buttonClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonClasses", required: false }] }], applyButtonClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "applyButtonClasses", required: false }] }], cancelButtonClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelButtonClasses", required: false }] }], isInvalidDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "isInvalidDate", required: false }] }], isCustomDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCustomDate", required: false }] }], minYear: [{ type: i0.Input, args: [{ isSignal: true, alias: "minYear", required: false }] }], maxYear: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxYear", required: false }] }], ranges: [{ type: i0.Input, args: [{ isSignal: true, alias: "ranges", required: false }] }], opens: [{ type: i0.Input, args: [{ isSignal: true, alias: "opens", required: false }] }], drops: [{ type: i0.Input, args: [{ isSignal: true, alias: "drops", required: false }] }], headerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTemplate", required: false }] }], footerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerTemplate", required: false }] }], dayTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dayTemplate", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }], rangeChange: [{ type: i0.Output, args: ["rangeChange"] }], openEvent: [{ type: i0.Output, args: ["openEvent"] }], closeEvent: [{ type: i0.Output, args: ["closeEvent"] }], monthChange: [{ type: i0.Output, args: ["monthChange"] }], yearChange: [{ type: i0.Output, args: ["yearChange"] }], dateHover: [{ type: i0.Output, args: ["dateHover"] }], validationError: [{ type: i0.Output, args: ["validationError"] }], checkboxChange: [{ type: i0.Output, args: ["checkboxChange"] }] } });
3551
+
3552
+ /*
3553
+ * Public API Surface of ngx-datex
3554
+ */
3555
+ // Main component
3556
+
3557
+ /**
3558
+ * Generated bundle index. Do not edit.
3559
+ */
3560
+
3561
+ export { DEFAULT_RANGES, MATERIAL_LIGHT_THEME, NgxDatex, NgxDatexCalendarService, NgxDatexOverlayService, NgxDatexService, NgxDatexTimePickerService, SPANISH_LOCALE, addDays, addMonths, endOfDay, formatDateValue, getStartOfMonth, getStartOfWeek, isBeforeDate, isMobileDevice, isSameDate, isSameDay, isSameMonth, isSameYear, isValidDate, parseDateValue, startOfDay, startOfMonth, startOfWeek };
3562
+ //# sourceMappingURL=ngx-datex.mjs.map