@oneluiz/dual-datepicker 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,129 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, signal, computed, effect, forwardRef, HostListener, Output, Input, Component } from '@angular/core';
2
+ import { InjectionToken, Injectable, EventEmitter, inject, signal, computed, effect, forwardRef, HostListener, Output, Input, Component } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
6
6
 
7
+ /**
8
+ * Abstract class for date adapters.
9
+ * Allows the component to work with different date libraries (Date, DayJS, date-fns, Luxon, etc.)
10
+ */
11
+ class DateAdapter {
12
+ }
13
+ /**
14
+ * Injection token for DateAdapter
15
+ */
16
+ const DATE_ADAPTER = new InjectionToken('DATE_ADAPTER');
17
+
18
+ /**
19
+ * Date adapter implementation for native JavaScript Date objects
20
+ * This is the default adapter used by the component
21
+ */
22
+ class NativeDateAdapter extends DateAdapter {
23
+ parse(value) {
24
+ if (!value)
25
+ return null;
26
+ if (value instanceof Date) {
27
+ return new Date(value);
28
+ }
29
+ if (typeof value === 'string' || typeof value === 'number') {
30
+ const date = new Date(value);
31
+ return this.isValid(date) ? date : null;
32
+ }
33
+ return null;
34
+ }
35
+ format(date, format = 'YYYY-MM-DD') {
36
+ if (!this.isValid(date))
37
+ return '';
38
+ const year = date.getFullYear();
39
+ const month = String(date.getMonth() + 1).padStart(2, '0');
40
+ const day = String(date.getDate()).padStart(2, '0');
41
+ // Simple format implementation
42
+ switch (format) {
43
+ case 'YYYY-MM-DD':
44
+ return `${year}-${month}-${day}`;
45
+ case 'MM/DD/YYYY':
46
+ return `${month}/${day}/${year}`;
47
+ case 'DD/MM/YYYY':
48
+ return `${day}/${month}/${year}`;
49
+ default:
50
+ return `${year}-${month}-${day}`;
51
+ }
52
+ }
53
+ addDays(date, days) {
54
+ const result = this.clone(date);
55
+ result.setDate(result.getDate() + days);
56
+ return result;
57
+ }
58
+ addMonths(date, months) {
59
+ const result = this.clone(date);
60
+ result.setMonth(result.getMonth() + months);
61
+ return result;
62
+ }
63
+ getYear(date) {
64
+ return date.getFullYear();
65
+ }
66
+ getMonth(date) {
67
+ return date.getMonth();
68
+ }
69
+ getDate(date) {
70
+ return date.getDate();
71
+ }
72
+ getDay(date) {
73
+ return date.getDay();
74
+ }
75
+ createDate(year, month, date) {
76
+ return new Date(year, month, date);
77
+ }
78
+ today() {
79
+ return new Date();
80
+ }
81
+ isSameDay(a, b) {
82
+ if (!a || !b)
83
+ return false;
84
+ if (!this.isValid(a) || !this.isValid(b))
85
+ return false;
86
+ return (a.getFullYear() === b.getFullYear() &&
87
+ a.getMonth() === b.getMonth() &&
88
+ a.getDate() === b.getDate());
89
+ }
90
+ isBefore(a, b) {
91
+ if (!a || !b)
92
+ return false;
93
+ if (!this.isValid(a) || !this.isValid(b))
94
+ return false;
95
+ return a.getTime() < b.getTime();
96
+ }
97
+ isAfter(a, b) {
98
+ if (!a || !b)
99
+ return false;
100
+ if (!this.isValid(a) || !this.isValid(b))
101
+ return false;
102
+ return a.getTime() > b.getTime();
103
+ }
104
+ isBetween(date, start, end) {
105
+ if (!date || !start || !end)
106
+ return false;
107
+ if (!this.isValid(date) || !this.isValid(start) || !this.isValid(end))
108
+ return false;
109
+ const dateTime = date.getTime();
110
+ const startTime = start.getTime();
111
+ const endTime = end.getTime();
112
+ return dateTime >= startTime && dateTime <= endTime;
113
+ }
114
+ clone(date) {
115
+ return new Date(date.getTime());
116
+ }
117
+ isValid(date) {
118
+ return date instanceof Date && !isNaN(date.getTime());
119
+ }
120
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NativeDateAdapter, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
121
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NativeDateAdapter });
122
+ }
123
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NativeDateAdapter, decorators: [{
124
+ type: Injectable
125
+ }] });
126
+
7
127
  class DualDatepickerComponent {
8
128
  elementRef;
9
129
  placeholder = 'Select date range';
@@ -28,12 +148,14 @@ class DualDatepickerComponent {
28
148
  locale = {};
29
149
  dateRangeChange = new EventEmitter();
30
150
  dateRangeSelected = new EventEmitter();
151
+ // Date adapter injection
152
+ dateAdapter = inject(DATE_ADAPTER);
31
153
  // Signals for reactive state
32
154
  mostrarDatePicker = signal(false);
33
155
  rangoFechas = signal('');
34
156
  fechaSeleccionandoInicio = signal(true);
35
- mesActual = signal(new Date());
36
- mesAnterior = signal(new Date());
157
+ mesActual = signal(this.dateAdapter.today());
158
+ mesAnterior = signal(this.dateAdapter.today());
37
159
  diasMesActual = signal([]);
38
160
  diasMesAnterior = signal([]);
39
161
  isDisabled = signal(false);
@@ -84,17 +206,19 @@ class DualDatepickerComponent {
84
206
  }
85
207
  }
86
208
  formatearFecha(fecha) {
87
- const year = fecha.getFullYear();
88
- const month = String(fecha.getMonth() + 1).padStart(2, '0');
89
- const day = String(fecha.getDate()).padStart(2, '0');
209
+ const year = this.dateAdapter.getYear(fecha);
210
+ const month = String(this.dateAdapter.getMonth(fecha) + 1).padStart(2, '0');
211
+ const day = String(this.dateAdapter.getDate(fecha)).padStart(2, '0');
90
212
  return `${year}-${month}-${day}`;
91
213
  }
92
214
  formatearFechaDisplay(fechaStr) {
93
215
  if (!fechaStr)
94
216
  return '';
95
- const fecha = new Date(fechaStr + 'T00:00:00');
217
+ const fecha = this.dateAdapter.parse(fechaStr);
218
+ if (!fecha)
219
+ return '';
96
220
  const monthNames = this.locale.monthNamesShort || this.defaultMonthNamesShort;
97
- return `${fecha.getDate()} ${monthNames[fecha.getMonth()]}`;
221
+ return `${this.dateAdapter.getDate(fecha)} ${monthNames[this.dateAdapter.getMonth(fecha)]}`;
98
222
  }
99
223
  actualizarRangoFechasTexto() {
100
224
  if (this.fechaInicio && this.fechaFin) {
@@ -111,7 +235,10 @@ class DualDatepickerComponent {
111
235
  if (this.mostrarDatePicker()) {
112
236
  this.fechaSeleccionandoInicio.set(true);
113
237
  const mesActualValue = this.mesActual();
114
- this.mesAnterior.set(new Date(mesActualValue.getFullYear(), mesActualValue.getMonth() - 1, 1));
238
+ const año = this.dateAdapter.getYear(mesActualValue);
239
+ const mes = this.dateAdapter.getMonth(mesActualValue);
240
+ const mesAnteriorDate = this.dateAdapter.createDate(año, mes - 1, 1);
241
+ this.mesAnterior.set(mesAnteriorDate);
115
242
  this.generarCalendarios();
116
243
  }
117
244
  this.onTouched();
@@ -125,18 +252,18 @@ class DualDatepickerComponent {
125
252
  this.diasMesActual.set(this.generarCalendarioMes(this.mesActual()));
126
253
  }
127
254
  generarCalendarioMes(fecha) {
128
- const año = fecha.getFullYear();
129
- const mes = fecha.getMonth();
130
- const primerDia = new Date(año, mes, 1);
131
- const ultimoDia = new Date(año, mes + 1, 0);
132
- const diasEnMes = ultimoDia.getDate();
133
- const primerDiaSemana = primerDia.getDay();
255
+ const año = this.dateAdapter.getYear(fecha);
256
+ const mes = this.dateAdapter.getMonth(fecha);
257
+ const primerDia = this.dateAdapter.createDate(año, mes, 1);
258
+ const ultimoDia = this.dateAdapter.createDate(año, mes + 1, 0);
259
+ const diasEnMes = this.dateAdapter.getDate(ultimoDia);
260
+ const primerDiaSemana = this.dateAdapter.getDay(primerDia);
134
261
  const diasMes = [];
135
262
  for (let i = 0; i < primerDiaSemana; i++) {
136
263
  diasMes.push({ dia: null, esMesActual: false });
137
264
  }
138
265
  for (let dia = 1; dia <= diasEnMes; dia++) {
139
- const fechaDia = new Date(año, mes, dia);
266
+ const fechaDia = this.dateAdapter.createDate(año, mes, dia);
140
267
  const fechaStr = this.formatearFecha(fechaDia);
141
268
  diasMes.push({
142
269
  dia: dia,
@@ -184,24 +311,45 @@ class DualDatepickerComponent {
184
311
  }
185
312
  cambiarMes(direccion) {
186
313
  const mesActualValue = this.mesActual();
187
- this.mesActual.set(new Date(mesActualValue.getFullYear(), mesActualValue.getMonth() + direccion, 1));
188
- const nuevoMesActual = this.mesActual();
189
- this.mesAnterior.set(new Date(nuevoMesActual.getFullYear(), nuevoMesActual.getMonth() - 1, 1));
314
+ const año = this.dateAdapter.getYear(mesActualValue);
315
+ const mes = this.dateAdapter.getMonth(mesActualValue);
316
+ const nuevoMesActual = this.dateAdapter.createDate(año, mes + direccion, 1);
317
+ this.mesActual.set(nuevoMesActual);
318
+ const añoNuevo = this.dateAdapter.getYear(nuevoMesActual);
319
+ const mesNuevo = this.dateAdapter.getMonth(nuevoMesActual);
320
+ const mesAnteriorNuevo = this.dateAdapter.createDate(añoNuevo, mesNuevo - 1, 1);
321
+ this.mesAnterior.set(mesAnteriorNuevo);
190
322
  this.generarCalendarios();
191
323
  }
192
324
  getNombreMes(fecha) {
193
325
  const monthNames = this.locale.monthNames || this.defaultMonthNames;
194
- return `${monthNames[fecha.getMonth()]} ${fecha.getFullYear()}`;
326
+ return `${monthNames[this.dateAdapter.getMonth(fecha)]} ${this.dateAdapter.getYear(fecha)}`;
195
327
  }
196
328
  getDayNames() {
197
329
  return this.locale.dayNamesShort || this.defaultDayNamesShort;
198
330
  }
199
331
  seleccionarRangoPredefinido(preset) {
200
- const hoy = new Date();
201
- const fechaInicio = new Date(hoy);
202
- fechaInicio.setDate(hoy.getDate() - preset.daysAgo);
203
- this.fechaInicio = this.formatearFecha(fechaInicio);
204
- this.fechaFin = this.formatearFecha(hoy);
332
+ let start;
333
+ let end;
334
+ // New flexible pattern with getValue()
335
+ if (preset.getValue) {
336
+ const range = preset.getValue();
337
+ start = range.start;
338
+ end = range.end;
339
+ }
340
+ // Backward compatibility with daysAgo pattern
341
+ else if (preset.daysAgo !== undefined) {
342
+ const hoy = this.dateAdapter.today();
343
+ const fechaInicio = this.dateAdapter.addDays(hoy, -preset.daysAgo);
344
+ start = this.formatearFecha(fechaInicio);
345
+ end = this.formatearFecha(hoy);
346
+ }
347
+ else {
348
+ console.error('PresetConfig must have either getValue() or daysAgo');
349
+ return;
350
+ }
351
+ this.fechaInicio = start;
352
+ this.fechaFin = end;
205
353
  this.actualizarRangoFechasTexto();
206
354
  this.generarCalendarios();
207
355
  if (this.closeOnPresetSelection) {
@@ -270,6 +418,10 @@ class DualDatepickerComponent {
270
418
  provide: NG_VALUE_ACCESSOR,
271
419
  useExisting: forwardRef(() => DualDatepickerComponent),
272
420
  multi: true
421
+ },
422
+ {
423
+ provide: DATE_ADAPTER,
424
+ useClass: NativeDateAdapter
273
425
  }
274
426
  ], usesOnChanges: true, ngImport: i0, template: "<div class=\"datepicker-wrapper\" \n [style.--input-border-hover]=\"inputBorderColorHover\"\n [style.--input-border-focus]=\"inputBorderColorFocus\">\n <input \n type=\"text\" \n class=\"datepicker-input\" \n [value]=\"rangoFechas()\" \n (click)=\"toggleDatePicker()\" \n [placeholder]=\"placeholder\"\n [disabled]=\"isDisabled()\"\n [ngStyle]=\"{\n 'background-color': inputBackgroundColor,\n 'color': inputTextColor,\n 'border-color': inputBorderColor,\n 'padding': inputPadding\n }\"\n readonly>\n\n @if (mostrarDatePicker()) {\n <div class=\"date-picker-dropdown\">\n @if (showPresets) {\n <div class=\"date-picker-presets\">\n @for (preset of presets; track preset.label) {\n <button type=\"button\" (click)=\"seleccionarRangoPredefinido(preset)\">{{ preset.label }}</button>\n }\n <button type=\"button\" class=\"btn-close-calendar\" (click)=\"cerrarDatePicker()\" title=\"Close\">\n \u00D7\n </button>\n </div>\n }\n\n @if (!showPresets) {\n <div class=\"date-picker-header-only-close\">\n <button type=\"button\" class=\"btn-close-calendar\" (click)=\"cerrarDatePicker()\" title=\"Close\">\n \u00D7\n </button>\n </div>\n }\n\n <!-- Calendars -->\n <div class=\"date-picker-calendars\">\n <!-- Previous month calendar -->\n <div class=\"date-picker-calendar\">\n <div class=\"date-picker-header\">\n <button type=\"button\" (click)=\"cambiarMes(-1)\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z\"/>\n </svg>\n </button>\n <span>{{ nombreMesAnterior() }}</span>\n <button type=\"button\" style=\"visibility: hidden;\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z\"/>\n </svg>\n </button>\n </div>\n <div class=\"date-picker-weekdays\">\n @for (dayName of diasSemana(); track $index) {\n <span>{{ dayName }}</span>\n }\n </div>\n <div class=\"date-picker-days\">\n @for (diaObj of diasMesAnterior(); track diaObj.fecha || $index) {\n <button \n type=\"button\"\n class=\"date-picker-day\" \n [class.empty]=\"!diaObj.esMesActual\"\n [class.selected]=\"diaObj.esInicio || diaObj.esFin\"\n [class.in-range]=\"diaObj.enRango && !diaObj.esInicio && !diaObj.esFin\"\n (click)=\"seleccionarDia(diaObj)\"\n [disabled]=\"!diaObj.esMesActual\">\n {{ diaObj.dia }}\n </button>\n }\n </div>\n </div>\n\n <!-- Current month calendar -->\n <div class=\"date-picker-calendar\">\n <div class=\"date-picker-header\">\n <button type=\"button\" style=\"visibility: hidden;\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z\"/>\n </svg>\n </button>\n <span>{{ nombreMesActual() }}</span>\n <button type=\"button\" (click)=\"cambiarMes(1)\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z\"/>\n </svg>\n </button>\n </div>\n <div class=\"date-picker-weekdays\">\n @for (dayName of diasSemana(); track $index) {\n <span>{{ dayName }}</span>\n }\n </div>\n <div class=\"date-picker-days\">\n @for (diaObj of diasMesActual(); track diaObj.fecha || $index) {\n <button \n type=\"button\"\n class=\"date-picker-day\" \n [class.empty]=\"!diaObj.esMesActual\"\n [class.selected]=\"diaObj.esInicio || diaObj.esFin\"\n [class.in-range]=\"diaObj.enRango && !diaObj.esInicio && !diaObj.esFin\"\n (click)=\"seleccionarDia(diaObj)\"\n [disabled]=\"!diaObj.esMesActual\">\n {{ diaObj.dia }}\n </button>\n }\n </div>\n </div>\n </div>\n\n <!-- Footer with clear button -->\n @if (showClearButton) {\n <div class=\"date-picker-footer\">\n <button type=\"button\" class=\"btn-clear\" (click)=\"limpiar()\" title=\"Clear selection\">\n Clear\n </button>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".datepicker-wrapper{position:relative;width:100%}.datepicker-wrapper .datepicker-input{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;cursor:pointer}.datepicker-wrapper .datepicker-input:hover{border-color:var(--input-border-hover, #ced4da)}.datepicker-wrapper .datepicker-input:focus{border-color:var(--input-border-focus, #80bdff);box-shadow:0 0 0 .2rem #007bff40;outline:0}.datepicker-wrapper .datepicker-input::placeholder{color:#6c757d;opacity:1}.datepicker-wrapper .datepicker-input:disabled,.datepicker-wrapper .datepicker-input[readonly]{background-color:#e9ecef;opacity:1}.date-picker-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e1e4e8;border-radius:8px;box-shadow:0 4px 12px #00000014,0 0 1px #00000014;padding:16px;z-index:1060;min-width:680px}@media (max-width: 768px){.date-picker-dropdown{min-width:100%;left:0;right:0}}.date-picker-header-only-close{display:flex;justify-content:flex-end;margin-bottom:12px}.date-picker-header-only-close .btn-close-calendar{background-color:transparent;border:1px solid transparent;color:#6b7280;padding:6px 10px;border-radius:6px;cursor:pointer;transition:all .15s ease;font-size:1.5rem;line-height:1}.date-picker-header-only-close .btn-close-calendar:hover{background-color:#fee;border-color:#fcc;color:#dc2626;transform:translateY(-1px);box-shadow:0 2px 4px #dc26261a}.date-picker-header-only-close .btn-close-calendar:active{transform:translateY(0);box-shadow:none}.date-picker-presets{display:flex;gap:6px;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid #e5e7eb;align-items:center}@media (max-width: 768px){.date-picker-presets{flex-wrap:wrap}}.date-picker-presets button{font-size:.75rem;padding:6px 14px;border:none;background-color:#f9fafb;color:#374151;border-radius:6px;transition:all .15s ease;font-weight:500;cursor:pointer;border:1px solid #e5e7eb}.date-picker-presets button:hover{background-color:#f3f4f6;border-color:#d1d5db;transform:translateY(-1px);box-shadow:0 2px 4px #0000000f}.date-picker-presets button:active{transform:translateY(0);box-shadow:none}.date-picker-presets .btn-close-calendar{margin-left:auto;background-color:transparent;border:1px solid transparent;color:#6b7280;padding:6px 10px;font-size:1.5rem;line-height:1}.date-picker-presets .btn-close-calendar:hover{background-color:#fee;border-color:#fcc;color:#dc2626;transform:translateY(-1px);box-shadow:0 2px 4px #dc26261a}.date-picker-presets .btn-close-calendar:active{transform:translateY(0);box-shadow:none}.date-picker-calendars{display:flex;gap:32px}@media (max-width: 768px){.date-picker-calendars{flex-direction:column;gap:16px}}.date-picker-calendar{flex:1}.date-picker-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding:0 4px}.date-picker-header span{font-size:.813rem;font-weight:600;color:#111827}.date-picker-header button{padding:4px;color:#6b7280;text-decoration:none;border-radius:6px;transition:all .15s ease;border:none;background:transparent;cursor:pointer}.date-picker-header button:hover{background-color:#f3f4f6;color:#111827}.date-picker-weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;margin-bottom:4px}.date-picker-weekdays span{text-align:center;font-size:.625rem;font-weight:600;color:#6b7280;padding:6px}.date-picker-days{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}.date-picker-day{aspect-ratio:1;border:none;background:transparent;border-radius:50%;font-size:.75rem;cursor:pointer;transition:all .15s ease;color:#374151;font-weight:400}.date-picker-day:hover:not(:disabled):not(.selected){background-color:#f3f4f6;color:#111827}.date-picker-day.empty{visibility:hidden}.date-picker-day.selected{background-color:#222;color:#fff;font-weight:600}.date-picker-day.in-range{background-color:#f9fafb;border-radius:0}.date-picker-day:disabled{cursor:not-allowed;opacity:.3}.date-picker-footer{padding:12px;border-top:1px solid #e1e4e8;display:flex;justify-content:center;gap:8px}.date-picker-footer .btn-clear{padding:8px 16px;background-color:#f6f8fa;border:1px solid #d0d7de;border-radius:6px;font-size:.875rem;font-weight:500;color:#24292f;cursor:pointer;transition:all .15s ease}.date-picker-footer .btn-clear:hover{background-color:#f3f4f6;border-color:#8c959f;transform:translateY(-1px);box-shadow:0 2px 4px #0000000d}.date-picker-footer .btn-clear:active{transform:translateY(0);box-shadow:none;background-color:#e9ecef}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }] });
275
427
  }
@@ -280,6 +432,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
280
432
  provide: NG_VALUE_ACCESSOR,
281
433
  useExisting: forwardRef(() => DualDatepickerComponent),
282
434
  multi: true
435
+ },
436
+ {
437
+ provide: DATE_ADAPTER,
438
+ useClass: NativeDateAdapter
283
439
  }
284
440
  ], template: "<div class=\"datepicker-wrapper\" \n [style.--input-border-hover]=\"inputBorderColorHover\"\n [style.--input-border-focus]=\"inputBorderColorFocus\">\n <input \n type=\"text\" \n class=\"datepicker-input\" \n [value]=\"rangoFechas()\" \n (click)=\"toggleDatePicker()\" \n [placeholder]=\"placeholder\"\n [disabled]=\"isDisabled()\"\n [ngStyle]=\"{\n 'background-color': inputBackgroundColor,\n 'color': inputTextColor,\n 'border-color': inputBorderColor,\n 'padding': inputPadding\n }\"\n readonly>\n\n @if (mostrarDatePicker()) {\n <div class=\"date-picker-dropdown\">\n @if (showPresets) {\n <div class=\"date-picker-presets\">\n @for (preset of presets; track preset.label) {\n <button type=\"button\" (click)=\"seleccionarRangoPredefinido(preset)\">{{ preset.label }}</button>\n }\n <button type=\"button\" class=\"btn-close-calendar\" (click)=\"cerrarDatePicker()\" title=\"Close\">\n \u00D7\n </button>\n </div>\n }\n\n @if (!showPresets) {\n <div class=\"date-picker-header-only-close\">\n <button type=\"button\" class=\"btn-close-calendar\" (click)=\"cerrarDatePicker()\" title=\"Close\">\n \u00D7\n </button>\n </div>\n }\n\n <!-- Calendars -->\n <div class=\"date-picker-calendars\">\n <!-- Previous month calendar -->\n <div class=\"date-picker-calendar\">\n <div class=\"date-picker-header\">\n <button type=\"button\" (click)=\"cambiarMes(-1)\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z\"/>\n </svg>\n </button>\n <span>{{ nombreMesAnterior() }}</span>\n <button type=\"button\" style=\"visibility: hidden;\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z\"/>\n </svg>\n </button>\n </div>\n <div class=\"date-picker-weekdays\">\n @for (dayName of diasSemana(); track $index) {\n <span>{{ dayName }}</span>\n }\n </div>\n <div class=\"date-picker-days\">\n @for (diaObj of diasMesAnterior(); track diaObj.fecha || $index) {\n <button \n type=\"button\"\n class=\"date-picker-day\" \n [class.empty]=\"!diaObj.esMesActual\"\n [class.selected]=\"diaObj.esInicio || diaObj.esFin\"\n [class.in-range]=\"diaObj.enRango && !diaObj.esInicio && !diaObj.esFin\"\n (click)=\"seleccionarDia(diaObj)\"\n [disabled]=\"!diaObj.esMesActual\">\n {{ diaObj.dia }}\n </button>\n }\n </div>\n </div>\n\n <!-- Current month calendar -->\n <div class=\"date-picker-calendar\">\n <div class=\"date-picker-header\">\n <button type=\"button\" style=\"visibility: hidden;\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z\"/>\n </svg>\n </button>\n <span>{{ nombreMesActual() }}</span>\n <button type=\"button\" (click)=\"cambiarMes(1)\">\n <svg width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\" d=\"M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z\"/>\n </svg>\n </button>\n </div>\n <div class=\"date-picker-weekdays\">\n @for (dayName of diasSemana(); track $index) {\n <span>{{ dayName }}</span>\n }\n </div>\n <div class=\"date-picker-days\">\n @for (diaObj of diasMesActual(); track diaObj.fecha || $index) {\n <button \n type=\"button\"\n class=\"date-picker-day\" \n [class.empty]=\"!diaObj.esMesActual\"\n [class.selected]=\"diaObj.esInicio || diaObj.esFin\"\n [class.in-range]=\"diaObj.enRango && !diaObj.esInicio && !diaObj.esFin\"\n (click)=\"seleccionarDia(diaObj)\"\n [disabled]=\"!diaObj.esMesActual\">\n {{ diaObj.dia }}\n </button>\n }\n </div>\n </div>\n </div>\n\n <!-- Footer with clear button -->\n @if (showClearButton) {\n <div class=\"date-picker-footer\">\n <button type=\"button\" class=\"btn-clear\" (click)=\"limpiar()\" title=\"Clear selection\">\n Clear\n </button>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".datepicker-wrapper{position:relative;width:100%}.datepicker-wrapper .datepicker-input{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;cursor:pointer}.datepicker-wrapper .datepicker-input:hover{border-color:var(--input-border-hover, #ced4da)}.datepicker-wrapper .datepicker-input:focus{border-color:var(--input-border-focus, #80bdff);box-shadow:0 0 0 .2rem #007bff40;outline:0}.datepicker-wrapper .datepicker-input::placeholder{color:#6c757d;opacity:1}.datepicker-wrapper .datepicker-input:disabled,.datepicker-wrapper .datepicker-input[readonly]{background-color:#e9ecef;opacity:1}.date-picker-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e1e4e8;border-radius:8px;box-shadow:0 4px 12px #00000014,0 0 1px #00000014;padding:16px;z-index:1060;min-width:680px}@media (max-width: 768px){.date-picker-dropdown{min-width:100%;left:0;right:0}}.date-picker-header-only-close{display:flex;justify-content:flex-end;margin-bottom:12px}.date-picker-header-only-close .btn-close-calendar{background-color:transparent;border:1px solid transparent;color:#6b7280;padding:6px 10px;border-radius:6px;cursor:pointer;transition:all .15s ease;font-size:1.5rem;line-height:1}.date-picker-header-only-close .btn-close-calendar:hover{background-color:#fee;border-color:#fcc;color:#dc2626;transform:translateY(-1px);box-shadow:0 2px 4px #dc26261a}.date-picker-header-only-close .btn-close-calendar:active{transform:translateY(0);box-shadow:none}.date-picker-presets{display:flex;gap:6px;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid #e5e7eb;align-items:center}@media (max-width: 768px){.date-picker-presets{flex-wrap:wrap}}.date-picker-presets button{font-size:.75rem;padding:6px 14px;border:none;background-color:#f9fafb;color:#374151;border-radius:6px;transition:all .15s ease;font-weight:500;cursor:pointer;border:1px solid #e5e7eb}.date-picker-presets button:hover{background-color:#f3f4f6;border-color:#d1d5db;transform:translateY(-1px);box-shadow:0 2px 4px #0000000f}.date-picker-presets button:active{transform:translateY(0);box-shadow:none}.date-picker-presets .btn-close-calendar{margin-left:auto;background-color:transparent;border:1px solid transparent;color:#6b7280;padding:6px 10px;font-size:1.5rem;line-height:1}.date-picker-presets .btn-close-calendar:hover{background-color:#fee;border-color:#fcc;color:#dc2626;transform:translateY(-1px);box-shadow:0 2px 4px #dc26261a}.date-picker-presets .btn-close-calendar:active{transform:translateY(0);box-shadow:none}.date-picker-calendars{display:flex;gap:32px}@media (max-width: 768px){.date-picker-calendars{flex-direction:column;gap:16px}}.date-picker-calendar{flex:1}.date-picker-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding:0 4px}.date-picker-header span{font-size:.813rem;font-weight:600;color:#111827}.date-picker-header button{padding:4px;color:#6b7280;text-decoration:none;border-radius:6px;transition:all .15s ease;border:none;background:transparent;cursor:pointer}.date-picker-header button:hover{background-color:#f3f4f6;color:#111827}.date-picker-weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;margin-bottom:4px}.date-picker-weekdays span{text-align:center;font-size:.625rem;font-weight:600;color:#6b7280;padding:6px}.date-picker-days{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}.date-picker-day{aspect-ratio:1;border:none;background:transparent;border-radius:50%;font-size:.75rem;cursor:pointer;transition:all .15s ease;color:#374151;font-weight:400}.date-picker-day:hover:not(:disabled):not(.selected){background-color:#f3f4f6;color:#111827}.date-picker-day.empty{visibility:hidden}.date-picker-day.selected{background-color:#222;color:#fff;font-weight:600}.date-picker-day.in-range{background-color:#f9fafb;border-radius:0}.date-picker-day:disabled{cursor:not-allowed;opacity:.3}.date-picker-footer{padding:12px;border-top:1px solid #e1e4e8;display:flex;justify-content:center;gap:8px}.date-picker-footer .btn-clear{padding:8px 16px;background-color:#f6f8fa;border:1px solid #d0d7de;border-radius:6px;font-size:.875rem;font-weight:500;color:#24292f;cursor:pointer;transition:all .15s ease}.date-picker-footer .btn-clear:hover{background-color:#f3f4f6;border-color:#8c959f;transform:translateY(-1px);box-shadow:0 2px 4px #0000000d}.date-picker-footer .btn-clear:active{transform:translateY(0);box-shadow:none;background-color:#e9ecef}\n"] }]
285
441
  }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { placeholder: [{
@@ -323,6 +479,282 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
323
479
  args: ['document:click', ['$event']]
324
480
  }] } });
325
481
 
482
+ /**
483
+ * Utility functions for creating common date range presets
484
+ * Perfect for dashboards, reporting, POS, BI apps, and ERP systems
485
+ */
486
+ /**
487
+ * Format a date as YYYY-MM-DD string
488
+ */
489
+ function formatDate(date) {
490
+ const year = date.getFullYear();
491
+ const month = String(date.getMonth() + 1).padStart(2, '0');
492
+ const day = String(date.getDate()).padStart(2, '0');
493
+ return `${year}-${month}-${day}`;
494
+ }
495
+ /**
496
+ * Get the start of today
497
+ */
498
+ function getToday() {
499
+ const today = new Date();
500
+ return {
501
+ start: formatDate(today),
502
+ end: formatDate(today)
503
+ };
504
+ }
505
+ /**
506
+ * Get yesterday's date range
507
+ */
508
+ function getYesterday() {
509
+ const yesterday = new Date();
510
+ yesterday.setDate(yesterday.getDate() - 1);
511
+ return {
512
+ start: formatDate(yesterday),
513
+ end: formatDate(yesterday)
514
+ };
515
+ }
516
+ /**
517
+ * Get last N days (including today)
518
+ */
519
+ function getLastNDays(days) {
520
+ const end = new Date();
521
+ const start = new Date();
522
+ start.setDate(start.getDate() - days + 1);
523
+ return {
524
+ start: formatDate(start),
525
+ end: formatDate(end)
526
+ };
527
+ }
528
+ /**
529
+ * Get this week (Monday to Sunday)
530
+ */
531
+ function getThisWeek() {
532
+ const today = new Date();
533
+ const dayOfWeek = today.getDay();
534
+ const start = new Date(today);
535
+ // Adjust to Monday (1) as first day of week
536
+ const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
537
+ start.setDate(start.getDate() - daysToMonday);
538
+ const end = new Date(start);
539
+ end.setDate(end.getDate() + 6);
540
+ return {
541
+ start: formatDate(start),
542
+ end: formatDate(end)
543
+ };
544
+ }
545
+ /**
546
+ * Get last week (Monday to Sunday)
547
+ */
548
+ function getLastWeek() {
549
+ const today = new Date();
550
+ const dayOfWeek = today.getDay();
551
+ const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
552
+ const lastMonday = new Date(today);
553
+ lastMonday.setDate(lastMonday.getDate() - daysToMonday - 7);
554
+ const lastSunday = new Date(lastMonday);
555
+ lastSunday.setDate(lastSunday.getDate() + 6);
556
+ return {
557
+ start: formatDate(lastMonday),
558
+ end: formatDate(lastSunday)
559
+ };
560
+ }
561
+ /**
562
+ * Get this month (1st to last day)
563
+ */
564
+ function getThisMonth() {
565
+ const today = new Date();
566
+ const start = new Date(today.getFullYear(), today.getMonth(), 1);
567
+ const end = new Date(today.getFullYear(), today.getMonth() + 1, 0);
568
+ return {
569
+ start: formatDate(start),
570
+ end: formatDate(end)
571
+ };
572
+ }
573
+ /**
574
+ * Get last month (1st to last day)
575
+ */
576
+ function getLastMonth() {
577
+ const today = new Date();
578
+ const start = new Date(today.getFullYear(), today.getMonth() - 1, 1);
579
+ const end = new Date(today.getFullYear(), today.getMonth(), 0);
580
+ return {
581
+ start: formatDate(start),
582
+ end: formatDate(end)
583
+ };
584
+ }
585
+ /**
586
+ * Get month to date (1st of current month to today)
587
+ */
588
+ function getMonthToDate() {
589
+ const today = new Date();
590
+ const start = new Date(today.getFullYear(), today.getMonth(), 1);
591
+ return {
592
+ start: formatDate(start),
593
+ end: formatDate(today)
594
+ };
595
+ }
596
+ /**
597
+ * Get this quarter (Q1, Q2, Q3, or Q4)
598
+ */
599
+ function getThisQuarter() {
600
+ const today = new Date();
601
+ const currentMonth = today.getMonth();
602
+ const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
603
+ const start = new Date(today.getFullYear(), quarterStartMonth, 1);
604
+ const end = new Date(today.getFullYear(), quarterStartMonth + 3, 0);
605
+ return {
606
+ start: formatDate(start),
607
+ end: formatDate(end)
608
+ };
609
+ }
610
+ /**
611
+ * Get last quarter
612
+ */
613
+ function getLastQuarter() {
614
+ const today = new Date();
615
+ const currentMonth = today.getMonth();
616
+ const lastQuarterStartMonth = Math.floor(currentMonth / 3) * 3 - 3;
617
+ const start = new Date(today.getFullYear(), lastQuarterStartMonth, 1);
618
+ const end = new Date(today.getFullYear(), lastQuarterStartMonth + 3, 0);
619
+ return {
620
+ start: formatDate(start),
621
+ end: formatDate(end)
622
+ };
623
+ }
624
+ /**
625
+ * Get quarter to date (start of current quarter to today)
626
+ */
627
+ function getQuarterToDate() {
628
+ const today = new Date();
629
+ const currentMonth = today.getMonth();
630
+ const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
631
+ const start = new Date(today.getFullYear(), quarterStartMonth, 1);
632
+ return {
633
+ start: formatDate(start),
634
+ end: formatDate(today)
635
+ };
636
+ }
637
+ /**
638
+ * Get this year (January 1 to December 31)
639
+ */
640
+ function getThisYear() {
641
+ const today = new Date();
642
+ const start = new Date(today.getFullYear(), 0, 1);
643
+ const end = new Date(today.getFullYear(), 11, 31);
644
+ return {
645
+ start: formatDate(start),
646
+ end: formatDate(end)
647
+ };
648
+ }
649
+ /**
650
+ * Get last year
651
+ */
652
+ function getLastYear() {
653
+ const today = new Date();
654
+ const start = new Date(today.getFullYear() - 1, 0, 1);
655
+ const end = new Date(today.getFullYear() - 1, 11, 31);
656
+ return {
657
+ start: formatDate(start),
658
+ end: formatDate(end)
659
+ };
660
+ }
661
+ /**
662
+ * Get year to date (January 1 to today)
663
+ */
664
+ function getYearToDate() {
665
+ const today = new Date();
666
+ const start = new Date(today.getFullYear(), 0, 1);
667
+ return {
668
+ start: formatDate(start),
669
+ end: formatDate(today)
670
+ };
671
+ }
672
+ /**
673
+ * Get last N months (including current partial month)
674
+ */
675
+ function getLastNMonths(months) {
676
+ const today = new Date();
677
+ const start = new Date(today);
678
+ start.setMonth(start.getMonth() - months);
679
+ return {
680
+ start: formatDate(start),
681
+ end: formatDate(today)
682
+ };
683
+ }
684
+ /**
685
+ * Get last N years
686
+ */
687
+ function getLastNYears(years) {
688
+ const today = new Date();
689
+ const start = new Date(today);
690
+ start.setFullYear(start.getFullYear() - years);
691
+ return {
692
+ start: formatDate(start),
693
+ end: formatDate(today)
694
+ };
695
+ }
696
+ /**
697
+ * Pre-built preset configurations for common use cases
698
+ * Import and use these directly in your component
699
+ */
700
+ const CommonPresets = {
701
+ /**
702
+ * Dashboard presets - Perfect for analytics dashboards
703
+ */
704
+ dashboard: [
705
+ { label: 'Today', getValue: getToday },
706
+ { label: 'Yesterday', getValue: getYesterday },
707
+ { label: 'Last 7 days', getValue: () => getLastNDays(7) },
708
+ { label: 'Last 30 days', getValue: () => getLastNDays(30) },
709
+ { label: 'This month', getValue: getThisMonth },
710
+ { label: 'Last month', getValue: getLastMonth }
711
+ ],
712
+ /**
713
+ * Reporting presets - Perfect for business reporting
714
+ */
715
+ reporting: [
716
+ { label: 'Today', getValue: getToday },
717
+ { label: 'This week', getValue: getThisWeek },
718
+ { label: 'Last week', getValue: getLastWeek },
719
+ { label: 'This month', getValue: getThisMonth },
720
+ { label: 'Last month', getValue: getLastMonth },
721
+ { label: 'This quarter', getValue: getThisQuarter },
722
+ { label: 'Last quarter', getValue: getLastQuarter }
723
+ ],
724
+ /**
725
+ * Financial presets - Perfect for ERP and accounting systems
726
+ */
727
+ financial: [
728
+ { label: 'Month to date', getValue: getMonthToDate },
729
+ { label: 'Quarter to date', getValue: getQuarterToDate },
730
+ { label: 'Year to date', getValue: getYearToDate },
731
+ { label: 'Last month', getValue: getLastMonth },
732
+ { label: 'Last quarter', getValue: getLastQuarter },
733
+ { label: 'Last year', getValue: getLastYear }
734
+ ],
735
+ /**
736
+ * Analytics presets - Perfect for BI and data analysis
737
+ */
738
+ analytics: [
739
+ { label: 'Last 7 days', getValue: () => getLastNDays(7) },
740
+ { label: 'Last 14 days', getValue: () => getLastNDays(14) },
741
+ { label: 'Last 30 days', getValue: () => getLastNDays(30) },
742
+ { label: 'Last 60 days', getValue: () => getLastNDays(60) },
743
+ { label: 'Last 90 days', getValue: () => getLastNDays(90) },
744
+ { label: 'Last 180 days', getValue: () => getLastNDays(180) },
745
+ { label: 'Last 365 days', getValue: () => getLastNDays(365) }
746
+ ],
747
+ /**
748
+ * Simple presets - Basic common ranges
749
+ */
750
+ simple: [
751
+ { label: 'Today', getValue: getToday },
752
+ { label: 'Last 7 days', getValue: () => getLastNDays(7) },
753
+ { label: 'Last 30 days', getValue: () => getLastNDays(30) },
754
+ { label: 'This year', getValue: getThisYear }
755
+ ]
756
+ };
757
+
326
758
  /**
327
759
  * Public API Surface of @oneluiz/dual-datepicker
328
760
  */
@@ -331,5 +763,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
331
763
  * Generated bundle index. Do not edit.
332
764
  */
333
765
 
334
- export { DualDatepickerComponent };
766
+ export { CommonPresets, DATE_ADAPTER, DateAdapter, DualDatepickerComponent, NativeDateAdapter, getLastMonth, getLastNDays, getLastNMonths, getLastNYears, getLastQuarter, getLastWeek, getLastYear, getMonthToDate, getQuarterToDate, getThisMonth, getThisQuarter, getThisWeek, getThisYear, getToday, getYearToDate, getYesterday };
335
767
  //# sourceMappingURL=oneluiz-dual-datepicker.mjs.map