@oneluiz/dual-datepicker 2.3.0 → 2.5.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.
package/README.md CHANGED
@@ -1,28 +1,84 @@
1
- # @oneluiz/dual-datepicker
1
+ # ng-dual-datepicker
2
2
 
3
- A beautiful, customizable dual-calendar date range picker for Angular 17+. Built as a standalone component with full TypeScript support.
3
+ A lightweight, zero-dependency date range picker for Angular 17+. Built with standalone components, Reactive Forms, and Angular Signals. No Angular Material required.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@oneluiz/dual-datepicker)](https://www.npmjs.com/package/@oneluiz/dual-datepicker)
6
6
  ![license](https://img.shields.io/npm/l/@oneluiz/dual-datepicker)
7
7
  ![Angular](https://img.shields.io/badge/Angular-17%2B-red)
8
8
 
9
+ ```bash
10
+ npm install @oneluiz/dual-datepicker
11
+ ```
12
+
9
13
  ## 🎯 [Live Demo](https://oneluiz.github.io/ng-dual-datepicker/)
10
14
 
11
15
  **[Check out the interactive examples →](https://oneluiz.github.io/ng-dual-datepicker/)**
12
16
 
13
- ## Features
17
+ ## Why ng-dual-datepicker?
18
+
19
+ | Feature | ng-dual-datepicker | Angular Material DateRangePicker |
20
+ |---------|-------------------|----------------------------------|
21
+ | **Bundle Size** | ~60 KB gzipped | ~300+ KB (with dependencies) |
22
+ | **Dependencies** | Zero | Requires @angular/material, @angular/cdk |
23
+ | **Standalone** | ✅ Native | ⚠️ Requires module setup |
24
+ | **Signals Support** | ✅ Built-in | ❌ Not yet |
25
+ | **Customization** | Full styling control | Theme-constrained |
26
+ | **Learning Curve** | Minimal | Requires Material knowledge |
27
+ | **Change Detection** | OnPush optimized | Default |
28
+ | **Setup Time** | < 1 minute | ~10+ minutes (theming, modules) |
29
+
30
+ ## ✨ Key Features
31
+
32
+ - 🪶 **Zero Dependencies** – No external libraries required
33
+ - 🎯 **Standalone Component** – No NgModule imports needed
34
+ - ⚡ **Angular Signals** – Modern reactive state management
35
+ - 🔄 **Reactive Forms** – Full ControlValueAccessor implementation
36
+ - 🎨 **Fully Customizable** – Every color, padding, border configurable
37
+ - 📦 **Lightweight** – ~60 KB gzipped total bundle
38
+ - 🚀 **Performance** – OnPush change detection + trackBy optimization
39
+ - ♿ **Accessible** – ARIA labels, semantic HTML, keyboard navigation (in progress)
40
+ - 🌍 **i18n Ready** – Customizable month/day names
41
+ - 📱 **Responsive** – Works on desktop and mobile
42
+
43
+ ## 🤔 When Should I Use This?
44
+
45
+ **Use ng-dual-datepicker if you:**
46
+ - Don't want to install Angular Material just for a date picker
47
+ - Need precise control over styling and behavior
48
+ - Want minimal bundle size impact
49
+ - Prefer standalone components over NgModules
50
+ - Need Angular Signals support now
51
+ - Are building a custom design system
52
+
53
+ **Use Angular Material DateRangePicker if you:**
54
+ - Already use Angular Material throughout your app
55
+ - Need Material Design compliance
56
+ - Want a battle-tested enterprise solution with extensive ecosystem support
57
+
58
+ ## ⚡ Performance
59
+
60
+ ```typescript
61
+ @Component({
62
+ changeDetection: ChangeDetectionStrategy.OnPush, // ✅ Optimized
63
+ standalone: true // ✅ No module overhead
64
+ })
65
+ ```
66
+
67
+ - **OnPush change detection** – Minimal re-renders
68
+ - **trackBy functions** – Efficient list rendering
69
+ - **No external CSS** – No runtime stylesheet downloads
70
+ - **Tree-shakeable** – Only import what you use
14
71
 
15
- - 📅 **Dual Calendar Display** - Side-by-side month view for easy range selection
16
- - 🎨 **Fully Customizable** - Color scheme, padding, and styling
17
- - ⚡ **Preset Ranges** - Configurable quick-select options
18
- - 🧹 **Clear Button** - Built-in button to reset selection
19
- - 🎯 **Standalone Component** - No module imports required
20
- - 🚀 **Zero Dependencies** - No Bootstrap or other CSS frameworks required
21
- - 🌍 **i18n Support** - Customizable month and day names for any language
22
- - 📱 **Responsive Design** - Works on desktop and mobile
23
- - 🌐 **TypeScript** - Full type safety
24
- - **Accessible** - Keyboard navigation and ARIA labels
25
- - 🎭 **Flexible Behavior** - Control when the picker closes
72
+ ## Accessibility (A11y)
73
+
74
+ **Current Status:**
75
+ - **Screen reader support** - ARIA labels included for all interactive elements
76
+ - **Semantic HTML** - Proper HTML structure
77
+ - 🚧 **Full keyboard navigation** - In active development (see [Roadmap](#-roadmap))
78
+ - Mouse/touch interaction: Fully supported
79
+ - Keyboard navigation: 🚧 In progress
80
+
81
+ > **Note:** Full keyboard navigation support is planned and will be included in a future release. This includes arrow key navigation, Enter/Space selection, and Escape to close.
26
82
 
27
83
  ## 📦 Installation
28
84
 
@@ -32,7 +88,7 @@ npm install @oneluiz/dual-datepicker
32
88
 
33
89
  ## 🚀 Quick Start
34
90
 
35
- ### 1. Import the Component
91
+ ### Basic Usage
36
92
 
37
93
  ```typescript
38
94
  import { Component } from '@angular/core';
@@ -44,50 +100,332 @@ import { DualDatepickerComponent, DateRange } from '@oneluiz/dual-datepicker';
44
100
  imports: [DualDatepickerComponent],
45
101
  template: `
46
102
  <ngx-dual-datepicker
47
- [(ngModel)]="dateRange"
48
- placeholder="Select date range">
103
+ (dateRangeChange)="onRangeChange($event)">
49
104
  </ngx-dual-datepicker>
50
105
  `
51
106
  })
52
107
  export class AppComponent {
53
- dateRange: DateRange = { start: null, end: null };
108
+ onRangeChange(range: DateRange) {
109
+ console.log('Start:', range.fechaInicio);
110
+ console.log('End:', range.fechaFin);
111
+ }
54
112
  }
55
113
  ```
56
114
 
57
- ### 2. Use with Forms
115
+ ### With Reactive Forms
58
116
 
59
117
  ```typescript
60
- import { Component } from '@angular/core';
61
- import { FormsModule } from '@angular/forms';
62
- import { DualDatepickerComponent } from '@oneluiz/dual-datepicker';
118
+ import { FormControl } from '@angular/forms';
119
+ import { DateRange } from '@oneluiz/dual-datepicker';
120
+
121
+ dateRange = new FormControl<DateRange | null>(null);
122
+ ```
123
+
124
+ ```html
125
+ <ngx-dual-datepicker [formControl]="dateRange"></ngx-dual-datepicker>
126
+ ```
127
+
128
+ ### With Angular Signals
129
+
130
+ ```typescript
131
+ import { signal } from '@angular/core';
132
+
133
+ dateRange = signal<DateRange | null>(null);
134
+ ```
135
+
136
+ ```html
137
+ <ngx-dual-datepicker
138
+ [(ngModel)]="dateRange()"
139
+ (dateRangeChange)="dateRange.set($event)">
140
+ </ngx-dual-datepicker>
141
+ ```
142
+
143
+ ### Custom Styling
144
+
145
+ ```html
146
+ <ngx-dual-datepicker
147
+ inputBackgroundColor="#1a1a2e"
148
+ inputTextColor="#eee"
149
+ inputBorderColor="#4a5568"
150
+ inputBorderColorFocus="#3182ce">
151
+ </ngx-dual-datepicker>
152
+ ```
153
+
154
+ ## 📚 Advanced Usage
155
+ }
156
+ ```
157
+
158
+ ### 4. Use with Angular Signals ⚡ New!
159
+
160
+ The component now uses Angular Signals internally for better performance and reactivity:
161
+
162
+ ```typescript
163
+ import { Component, signal, computed } from '@angular/core';
164
+ import { DualDatepickerComponent, DateRange } from '@oneluiz/dual-datepicker';
63
165
 
64
166
  @Component({
65
- selector: 'app-example',
167
+ selector: 'app-signals-example',
66
168
  standalone: true,
67
- imports: [FormsModule, DualDatepickerComponent],
169
+ imports: [DualDatepickerComponent],
68
170
  template: `
69
171
  <ngx-dual-datepicker
70
- [(ngModel)]="selectedRange"
71
- [presets]="customPresets"
72
- (ngModelChange)="onDateChange($event)">
172
+ [fechaInicio]="fechaInicio()"
173
+ [fechaFin]="fechaFin()"
174
+ (dateRangeChange)="onDateChange($event)">
73
175
  </ngx-dual-datepicker>
176
+
177
+ @if (isRangeSelected()) {
178
+ <div>
179
+ <p>{{ rangeText() }}</p>
180
+ <p>Days selected: {{ daysDifference() }}</p>
181
+ </div>
182
+ }
74
183
  `
75
184
  })
76
- export class ExampleComponent {
77
- selectedRange = { start: null, end: null };
185
+ export class SignalsExampleComponent {
186
+ fechaInicio = signal('');
187
+ fechaFin = signal('');
188
+
189
+ // Computed values
190
+ isRangeSelected = computed(() =>
191
+ this.fechaInicio() !== '' && this.fechaFin() !== ''
192
+ );
193
+
194
+ rangeText = computed(() =>
195
+ this.isRangeSelected()
196
+ ? `${this.fechaInicio()} to ${this.fechaFin()}`
197
+ : 'No range selected'
198
+ );
78
199
 
79
- customPresets = [
80
- { label: 'Last 7 days', daysAgo: 7 },
81
- { label: 'Last 30 days', daysAgo: 30 },
82
- { label: 'Last 90 days', daysAgo: 90 }
83
- ];
200
+ daysDifference = computed(() => {
201
+ if (!this.isRangeSelected()) return 0;
202
+ const start = new Date(this.fechaInicio());
203
+ const end = new Date(this.fechaFin());
204
+ return Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
205
+ });
206
+
207
+ onDateChange(range: DateRange) {
208
+ this.fechaInicio.set(range.fechaInicio);
209
+ this.fechaFin.set(range.fechaFin);
210
+ }
211
+ }
212
+ ```
213
+
214
+ ## 🔌 Date Adapter System
215
+
216
+ The library supports custom date adapters, allowing you to use different date libraries (DayJS, date-fns, Luxon) or custom backend models instead of native JavaScript `Date` objects.
217
+
218
+ ### Using Native Date (Default)
84
219
 
85
- onDateChange(range: any) {
86
- console.log('Date range selected:', range);
220
+ By default, the component uses `NativeDateAdapter` which works with JavaScript `Date` objects:
221
+
222
+ ```typescript
223
+ import { DualDatepickerComponent } from '@oneluiz/dual-datepicker';
224
+
225
+ @Component({
226
+ standalone: true,
227
+ imports: [DualDatepickerComponent],
228
+ template: `<ngx-dual-datepicker></ngx-dual-datepicker>`
229
+ })
230
+ export class AppComponent {}
231
+ ```
232
+
233
+ ### Creating a Custom Adapter
234
+
235
+ Example using **date-fns**:
236
+
237
+ ```typescript
238
+ import { Injectable } from '@angular/core';
239
+ import { DateAdapter } from '@oneluiz/dual-datepicker';
240
+ import {
241
+ parse, format, addDays, addMonths,
242
+ getYear, getMonth, getDate, getDay,
243
+ isSameDay, isBefore, isAfter, isWithinInterval,
244
+ isValid
245
+ } from 'date-fns';
246
+
247
+ @Injectable()
248
+ export class DateFnsAdapter extends DateAdapter<Date> {
249
+ parse(value: any): Date | null {
250
+ if (!value) return null;
251
+ if (value instanceof Date) return value;
252
+
253
+ const parsed = parse(value, 'yyyy-MM-dd', new Date());
254
+ return isValid(parsed) ? parsed : null;
255
+ }
256
+
257
+ format(date: Date, formatStr: string = 'yyyy-MM-dd'): string {
258
+ return format(date, formatStr);
259
+ }
260
+
261
+ addDays(date: Date, days: number): Date {
262
+ return addDays(date, days);
263
+ }
264
+
265
+ addMonths(date: Date, months: number): Date {
266
+ return addMonths(date, months);
267
+ }
268
+
269
+ getYear(date: Date): number {
270
+ return getYear(date);
271
+ }
272
+
273
+ getMonth(date: Date): number {
274
+ return getMonth(date);
275
+ }
276
+
277
+ getDate(date: Date): number {
278
+ return getDate(date);
279
+ }
280
+
281
+ getDay(date: Date): number {
282
+ return getDay(date);
283
+ }
284
+
285
+ createDate(year: number, month: number, day: number): Date {
286
+ return new Date(year, month, day);
287
+ }
288
+
289
+ today(): Date {
290
+ return new Date();
291
+ }
292
+
293
+ isSameDay(a: Date | null, b: Date | null): boolean {
294
+ if (!a || !b) return false;
295
+ return isSameDay(a, b);
296
+ }
297
+
298
+ isBefore(a: Date | null, b: Date | null): boolean {
299
+ if (!a || !b) return false;
300
+ return isBefore(a, b);
301
+ }
302
+
303
+ isAfter(a: Date | null, b: Date | null): boolean {
304
+ if (!a || !b) return false;
305
+ return isAfter(a, b);
306
+ }
307
+
308
+ isBetween(date: Date | null, start: Date | null, end: Date | null): boolean {
309
+ if (!date || !start || !end) return false;
310
+ return isWithinInterval(date, { start, end });
311
+ }
312
+
313
+ clone(date: Date): Date {
314
+ return new Date(date);
315
+ }
316
+
317
+ isValid(date: any): boolean {
318
+ return isValid(date);
319
+ }
320
+ }
321
+ ```
322
+
323
+ ### Providing Custom Adapter
324
+
325
+ ```typescript
326
+ import { Component } from '@angular/core';
327
+ import { DualDatepickerComponent, DATE_ADAPTER } from '@oneluiz/dual-datepicker';
328
+ import { DateFnsAdapter } from './date-fns-adapter';
329
+
330
+ @Component({
331
+ standalone: true,
332
+ imports: [DualDatepickerComponent],
333
+ providers: [
334
+ { provide: DATE_ADAPTER, useClass: DateFnsAdapter }
335
+ ],
336
+ template: `<ngx-dual-datepicker></ngx-dual-datepicker>`
337
+ })
338
+ export class AppComponent {}
339
+ ```
340
+
341
+ ### Example: DayJS Adapter
342
+
343
+ ```typescript
344
+ import { Injectable } from '@angular/core';
345
+ import { DateAdapter } from '@oneluiz/dual-datepicker';
346
+ import dayjs, { Dayjs } from 'dayjs';
347
+
348
+ @Injectable()
349
+ export class DayJSAdapter extends DateAdapter<Dayjs> {
350
+ parse(value: any): Dayjs | null {
351
+ if (!value) return null;
352
+ const parsed = dayjs(value);
353
+ return parsed.isValid() ? parsed : null;
354
+ }
355
+
356
+ format(date: Dayjs, format: string = 'YYYY-MM-DD'): string {
357
+ return date.format(format);
358
+ }
359
+
360
+ addDays(date: Dayjs, days: number): Dayjs {
361
+ return date.add(days, 'day');
362
+ }
363
+
364
+ addMonths(date: Dayjs, months: number): Dayjs {
365
+ return date.add(months, 'month');
366
+ }
367
+
368
+ getYear(date: Dayjs): number {
369
+ return date.year();
370
+ }
371
+
372
+ getMonth(date: Dayjs): number {
373
+ return date.month();
374
+ }
375
+
376
+ getDate(date: Dayjs): number {
377
+ return date.date();
378
+ }
379
+
380
+ getDay(date: Dayjs): number {
381
+ return date.day();
382
+ }
383
+
384
+ createDate(year: number, month: number, day: number): Dayjs {
385
+ return dayjs().year(year).month(month).date(day);
386
+ }
387
+
388
+ today(): Dayjs {
389
+ return dayjs();
390
+ }
391
+
392
+ isSameDay(a: Dayjs | null, b: Dayjs | null): boolean {
393
+ if (!a || !b) return false;
394
+ return a.isSame(b, 'day');
395
+ }
396
+
397
+ isBefore(a: Dayjs | null, b: Dayjs | null): boolean {
398
+ if (!a || !b) return false;
399
+ return a.isBefore(b);
400
+ }
401
+
402
+ isAfter(a: Dayjs | null, b: Dayjs | null): boolean {
403
+ if (!a || !b) return false;
404
+ return a.isAfter(b);
405
+ }
406
+
407
+ isBetween(date: Dayjs | null, start: Dayjs | null, end: Dayjs | null): boolean {
408
+ if (!date || !start || !end) return false;
409
+ return date.isAfter(start) && date.isBefore(end) || date.isSame(start) || date.isSame(end);
410
+ }
411
+
412
+ clone(date: Dayjs): Dayjs {
413
+ return date.clone();
414
+ }
415
+
416
+ isValid(date: any): boolean {
417
+ return dayjs.isDayjs(date) && date.isValid();
87
418
  }
88
419
  }
89
420
  ```
90
421
 
422
+ ### Benefits of Date Adapters
423
+
424
+ - ✅ **Zero vendor lock-in** - Use any date library you prefer
425
+ - ✅ **Consistency** - Use the same date library across your entire app
426
+ - ✅ **Custom backend models** - Adapt to your API's date format
427
+ - ✅ **Type safety** - Full TypeScript support with generics
428
+
91
429
  ## 🎨 Customization
92
430
 
93
431
  ### Custom Colors (Bootstrap Style)
@@ -146,7 +484,7 @@ customPresets: PresetConfig[] = [
146
484
  | `placeholder` | `string` | `'Select date range'` | Input placeholder text |
147
485
  | `presets` | `PresetConfig[]` | Default presets | Array of preset configurations |
148
486
  | `showPresets` | `boolean` | `true` | Show/hide the presets sidebar |
149
- | `showClearButton` | `boolean` | `true` | Show/hide the Clear button in dropdown |
487
+ | `showClearButton` | `boolean` | `false` | Show/hide the Clear button in dropdown |
150
488
  | `closeOnSelection` | `boolean` | `false` | Close picker when both dates selected |
151
489
  | `closeOnPresetSelection` | `boolean` | `false` | Close picker when preset is clicked |
152
490
  | `closeOnClickOutside` | `boolean` | `true` | Close picker when clicking outside |
@@ -340,7 +678,17 @@ export class ExampleComponent {
340
678
  - Angular 19.0.0 or higher
341
679
  - Angular 20.0.0 or higher
342
680
 
343
- ## 📄 License
681
+ ## �️ Roadmap
682
+
683
+ Planned features and improvements:
684
+
685
+ - ⬜ **Complete keyboard navigation** - Arrow keys, Enter/Space, Tab, Escape
686
+ - ⬜ **Full accessibility audit** - WCAG 2.1 AA compliance
687
+ - ⬜ **Presets improvements** - More flexible preset configurations
688
+ - ⬜ **Multi-range support** - Select multiple date ranges
689
+ - ⬜ **Theming system** - Pre-built theme presets
690
+
691
+ ## �📄 License
344
692
 
345
693
  MIT © Luis Cortes
346
694
 
@@ -0,0 +1,116 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ /**
3
+ * Abstract class for date adapters.
4
+ * Allows the component to work with different date libraries (Date, DayJS, date-fns, Luxon, etc.)
5
+ */
6
+ export declare abstract class DateAdapter<T = any> {
7
+ /**
8
+ * Parses a value into a date object
9
+ * @param value - Value to parse (string, number, or date object)
10
+ * @returns Parsed date object
11
+ */
12
+ abstract parse(value: any): T | null;
13
+ /**
14
+ * Formats a date object into a string
15
+ * @param date - Date object to format
16
+ * @param format - Optional format string
17
+ * @returns Formatted date string
18
+ */
19
+ abstract format(date: T, format?: string): string;
20
+ /**
21
+ * Adds days to a date
22
+ * @param date - Date object
23
+ * @param days - Number of days to add (can be negative)
24
+ * @returns New date object
25
+ */
26
+ abstract addDays(date: T, days: number): T;
27
+ /**
28
+ * Adds months to a date
29
+ * @param date - Date object
30
+ * @param months - Number of months to add (can be negative)
31
+ * @returns New date object
32
+ */
33
+ abstract addMonths(date: T, months: number): T;
34
+ /**
35
+ * Gets the year from a date
36
+ * @param date - Date object
37
+ * @returns Year number
38
+ */
39
+ abstract getYear(date: T): number;
40
+ /**
41
+ * Gets the month from a date (0-11)
42
+ * @param date - Date object
43
+ * @returns Month number (0-11)
44
+ */
45
+ abstract getMonth(date: T): number;
46
+ /**
47
+ * Gets the day of month from a date
48
+ * @param date - Date object
49
+ * @returns Day of month (1-31)
50
+ */
51
+ abstract getDate(date: T): number;
52
+ /**
53
+ * Gets the day of week from a date (0-6, Sunday = 0)
54
+ * @param date - Date object
55
+ * @returns Day of week (0-6)
56
+ */
57
+ abstract getDay(date: T): number;
58
+ /**
59
+ * Creates a new date object
60
+ * @param year - Year
61
+ * @param month - Month (0-11)
62
+ * @param date - Day of month (1-31)
63
+ * @returns New date object
64
+ */
65
+ abstract createDate(year: number, month: number, date: number): T;
66
+ /**
67
+ * Gets today's date
68
+ * @returns Today's date object
69
+ */
70
+ abstract today(): T;
71
+ /**
72
+ * Checks if two dates are the same day
73
+ * @param a - First date
74
+ * @param b - Second date
75
+ * @returns True if same day
76
+ */
77
+ abstract isSameDay(a: T | null, b: T | null): boolean;
78
+ /**
79
+ * Checks if a date is before another
80
+ * @param a - First date
81
+ * @param b - Second date
82
+ * @returns True if a is before b
83
+ */
84
+ abstract isBefore(a: T | null, b: T | null): boolean;
85
+ /**
86
+ * Checks if a date is after another
87
+ * @param a - First date
88
+ * @param b - Second date
89
+ * @returns True if a is after b
90
+ */
91
+ abstract isAfter(a: T | null, b: T | null): boolean;
92
+ /**
93
+ * Checks if a date is between two other dates
94
+ * @param date - Date to check
95
+ * @param start - Start date
96
+ * @param end - End date
97
+ * @returns True if date is between start and end
98
+ */
99
+ abstract isBetween(date: T | null, start: T | null, end: T | null): boolean;
100
+ /**
101
+ * Clones a date object
102
+ * @param date - Date to clone
103
+ * @returns Cloned date object
104
+ */
105
+ abstract clone(date: T): T;
106
+ /**
107
+ * Checks if a value is a valid date
108
+ * @param date - Value to check
109
+ * @returns True if valid date
110
+ */
111
+ abstract isValid(date: any): boolean;
112
+ }
113
+ /**
114
+ * Injection token for DateAdapter
115
+ */
116
+ export declare const DATE_ADAPTER: InjectionToken<DateAdapter<any>>;
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter, OnInit, OnChanges, SimpleChanges, ElementRef } from '@angular/core';
2
+ import { ControlValueAccessor } from '@angular/forms';
2
3
  import * as i0 from "@angular/core";
3
4
  export interface DateRange {
4
5
  fechaInicio: string;
@@ -16,7 +17,7 @@ export interface LocaleConfig {
16
17
  dayNamesShort?: string[];
17
18
  firstDayOfWeek?: number;
18
19
  }
19
- export declare class DualDatepickerComponent implements OnInit, OnChanges {
20
+ export declare class DualDatepickerComponent implements OnInit, OnChanges, ControlValueAccessor {
20
21
  private elementRef;
21
22
  placeholder: string;
22
23
  fechaInicio: string;
@@ -36,17 +37,24 @@ export declare class DualDatepickerComponent implements OnInit, OnChanges {
36
37
  locale: LocaleConfig;
37
38
  dateRangeChange: EventEmitter<DateRange>;
38
39
  dateRangeSelected: EventEmitter<DateRange>;
39
- mostrarDatePicker: boolean;
40
- rangoFechas: string;
41
- fechaSeleccionandoInicio: boolean;
42
- mesActual: Date;
43
- mesAnterior: Date;
44
- diasMesActual: any[];
45
- diasMesAnterior: any[];
40
+ private dateAdapter;
41
+ mostrarDatePicker: import("@angular/core").WritableSignal<boolean>;
42
+ rangoFechas: import("@angular/core").WritableSignal<string>;
43
+ fechaSeleccionandoInicio: import("@angular/core").WritableSignal<boolean>;
44
+ mesActual: import("@angular/core").WritableSignal<any>;
45
+ mesAnterior: import("@angular/core").WritableSignal<any>;
46
+ diasMesActual: import("@angular/core").WritableSignal<any[]>;
47
+ diasMesAnterior: import("@angular/core").WritableSignal<any[]>;
48
+ isDisabled: import("@angular/core").WritableSignal<boolean>;
49
+ nombreMesActual: import("@angular/core").Signal<string>;
50
+ nombreMesAnterior: import("@angular/core").Signal<string>;
51
+ diasSemana: import("@angular/core").Signal<string[]>;
46
52
  private readonly defaultMonthNames;
47
53
  private readonly defaultMonthNamesShort;
48
54
  private readonly defaultDayNames;
49
55
  private readonly defaultDayNamesShort;
56
+ private onChange;
57
+ private onTouched;
50
58
  constructor(elementRef: ElementRef);
51
59
  onClickOutside(event: MouseEvent): void;
52
60
  ngOnInit(): void;
@@ -67,6 +75,11 @@ export declare class DualDatepickerComponent implements OnInit, OnChanges {
67
75
  limpiar(): void;
68
76
  private emitirCambio;
69
77
  private emitirSeleccion;
78
+ private getDateRangeValue;
79
+ writeValue(value: DateRange | null): void;
80
+ registerOnChange(fn: (value: DateRange | null) => void): void;
81
+ registerOnTouched(fn: () => void): void;
82
+ setDisabledState(isDisabled: boolean): void;
70
83
  static ɵfac: i0.ɵɵFactoryDeclaration<DualDatepickerComponent, never>;
71
84
  static ɵcmp: i0.ɵɵComponentDeclaration<DualDatepickerComponent, "ngx-dual-datepicker", never, { "placeholder": { "alias": "placeholder"; "required": false; }; "fechaInicio": { "alias": "fechaInicio"; "required": false; }; "fechaFin": { "alias": "fechaFin"; "required": false; }; "showPresets": { "alias": "showPresets"; "required": false; }; "showClearButton": { "alias": "showClearButton"; "required": false; }; "closeOnSelection": { "alias": "closeOnSelection"; "required": false; }; "closeOnPresetSelection": { "alias": "closeOnPresetSelection"; "required": false; }; "closeOnClickOutside": { "alias": "closeOnClickOutside"; "required": false; }; "presets": { "alias": "presets"; "required": false; }; "inputBackgroundColor": { "alias": "inputBackgroundColor"; "required": false; }; "inputTextColor": { "alias": "inputTextColor"; "required": false; }; "inputBorderColor": { "alias": "inputBorderColor"; "required": false; }; "inputBorderColorHover": { "alias": "inputBorderColorHover"; "required": false; }; "inputBorderColorFocus": { "alias": "inputBorderColorFocus"; "required": false; }; "inputPadding": { "alias": "inputPadding"; "required": false; }; "locale": { "alias": "locale"; "required": false; }; }, { "dateRangeChange": "dateRangeChange"; "dateRangeSelected": "dateRangeSelected"; }, never, never, true, never>;
72
85
  }
@@ -0,0 +1,12 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ /**
3
+ * Abstract class for date adapters.
4
+ * Allows the component to work with different date libraries (Date, DayJS, date-fns, Luxon, etc.)
5
+ */
6
+ export class DateAdapter {
7
+ }
8
+ /**
9
+ * Injection token for DateAdapter
10
+ */
11
+ export const DATE_ADAPTER = new InjectionToken('DATE_ADAPTER');
12
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RhdGUtYWRhcHRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRS9DOzs7R0FHRztBQUNILE1BQU0sT0FBZ0IsV0FBVztDQXlIaEM7QUFFRDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFlBQVksR0FBRyxJQUFJLGNBQWMsQ0FBYyxjQUFjLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGlvblRva2VuIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbi8qKlxuICogQWJzdHJhY3QgY2xhc3MgZm9yIGRhdGUgYWRhcHRlcnMuXG4gKiBBbGxvd3MgdGhlIGNvbXBvbmVudCB0byB3b3JrIHdpdGggZGlmZmVyZW50IGRhdGUgbGlicmFyaWVzIChEYXRlLCBEYXlKUywgZGF0ZS1mbnMsIEx1eG9uLCBldGMuKVxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgRGF0ZUFkYXB0ZXI8VCA9IGFueT4ge1xuICAvKipcbiAgICogUGFyc2VzIGEgdmFsdWUgaW50byBhIGRhdGUgb2JqZWN0XG4gICAqIEBwYXJhbSB2YWx1ZSAtIFZhbHVlIHRvIHBhcnNlIChzdHJpbmcsIG51bWJlciwgb3IgZGF0ZSBvYmplY3QpXG4gICAqIEByZXR1cm5zIFBhcnNlZCBkYXRlIG9iamVjdFxuICAgKi9cbiAgYWJzdHJhY3QgcGFyc2UodmFsdWU6IGFueSk6IFQgfCBudWxsO1xuXG4gIC8qKlxuICAgKiBGb3JtYXRzIGEgZGF0ZSBvYmplY3QgaW50byBhIHN0cmluZ1xuICAgKiBAcGFyYW0gZGF0ZSAtIERhdGUgb2JqZWN0IHRvIGZvcm1hdFxuICAgKiBAcGFyYW0gZm9ybWF0IC0gT3B0aW9uYWwgZm9ybWF0IHN0cmluZ1xuICAgKiBAcmV0dXJucyBGb3JtYXR0ZWQgZGF0ZSBzdHJpbmdcbiAgICovXG4gIGFic3RyYWN0IGZvcm1hdChkYXRlOiBULCBmb3JtYXQ/OiBzdHJpbmcpOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEFkZHMgZGF5cyB0byBhIGRhdGVcbiAgICogQHBhcmFtIGRhdGUgLSBEYXRlIG9iamVjdFxuICAgKiBAcGFyYW0gZGF5cyAtIE51bWJlciBvZiBkYXlzIHRvIGFkZCAoY2FuIGJlIG5lZ2F0aXZlKVxuICAgKiBAcmV0dXJucyBOZXcgZGF0ZSBvYmplY3RcbiAgICovXG4gIGFic3RyYWN0IGFkZERheXMoZGF0ZTogVCwgZGF5czogbnVtYmVyKTogVDtcblxuICAvKipcbiAgICogQWRkcyBtb250aHMgdG8gYSBkYXRlXG4gICAqIEBwYXJhbSBkYXRlIC0gRGF0ZSBvYmplY3RcbiAgICogQHBhcmFtIG1vbnRocyAtIE51bWJlciBvZiBtb250aHMgdG8gYWRkIChjYW4gYmUgbmVnYXRpdmUpXG4gICAqIEByZXR1cm5zIE5ldyBkYXRlIG9iamVjdFxuICAgKi9cbiAgYWJzdHJhY3QgYWRkTW9udGhzKGRhdGU6IFQsIG1vbnRoczogbnVtYmVyKTogVDtcblxuICAvKipcbiAgICogR2V0cyB0aGUgeWVhciBmcm9tIGEgZGF0ZVxuICAgKiBAcGFyYW0gZGF0ZSAtIERhdGUgb2JqZWN0XG4gICAqIEByZXR1cm5zIFllYXIgbnVtYmVyXG4gICAqL1xuICBhYnN0cmFjdCBnZXRZZWFyKGRhdGU6IFQpOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIG1vbnRoIGZyb20gYSBkYXRlICgwLTExKVxuICAgKiBAcGFyYW0gZGF0ZSAtIERhdGUgb2JqZWN0XG4gICAqIEByZXR1cm5zIE1vbnRoIG51bWJlciAoMC0xMSlcbiAgICovXG4gIGFic3RyYWN0IGdldE1vbnRoKGRhdGU6IFQpOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIGRheSBvZiBtb250aCBmcm9tIGEgZGF0ZVxuICAgKiBAcGFyYW0gZGF0ZSAtIERhdGUgb2JqZWN0XG4gICAqIEByZXR1cm5zIERheSBvZiBtb250aCAoMS0zMSlcbiAgICovXG4gIGFic3RyYWN0IGdldERhdGUoZGF0ZTogVCk6IG51bWJlcjtcblxuICAvKipcbiAgICogR2V0cyB0aGUgZGF5IG9mIHdlZWsgZnJvbSBhIGRhdGUgKDAtNiwgU3VuZGF5ID0gMClcbiAgICogQHBhcmFtIGRhdGUgLSBEYXRlIG9iamVjdFxuICAgKiBAcmV0dXJucyBEYXkgb2Ygd2VlayAoMC02KVxuICAgKi9cbiAgYWJzdHJhY3QgZ2V0RGF5KGRhdGU6IFQpOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuZXcgZGF0ZSBvYmplY3RcbiAgICogQHBhcmFtIHllYXIgLSBZZWFyXG4gICAqIEBwYXJhbSBtb250aCAtIE1vbnRoICgwLTExKVxuICAgKiBAcGFyYW0gZGF0ZSAtIERheSBvZiBtb250aCAoMS0zMSlcbiAgICogQHJldHVybnMgTmV3IGRhdGUgb2JqZWN0XG4gICAqL1xuICBhYnN0cmFjdCBjcmVhdGVEYXRlKHllYXI6IG51bWJlciwgbW9udGg6IG51bWJlciwgZGF0ZTogbnVtYmVyKTogVDtcblxuICAvKipcbiAgICogR2V0cyB0b2RheSdzIGRhdGVcbiAgICogQHJldHVybnMgVG9kYXkncyBkYXRlIG9iamVjdFxuICAgKi9cbiAgYWJzdHJhY3QgdG9kYXkoKTogVDtcblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHR3byBkYXRlcyBhcmUgdGhlIHNhbWUgZGF5XG4gICAqIEBwYXJhbSBhIC0gRmlyc3QgZGF0ZVxuICAgKiBAcGFyYW0gYiAtIFNlY29uZCBkYXRlXG4gICAqIEByZXR1cm5zIFRydWUgaWYgc2FtZSBkYXlcbiAgICovXG4gIGFic3RyYWN0IGlzU2FtZURheShhOiBUIHwgbnVsbCwgYjogVCB8IG51bGwpOiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgYSBkYXRlIGlzIGJlZm9yZSBhbm90aGVyXG4gICAqIEBwYXJhbSBhIC0gRmlyc3QgZGF0ZVxuICAgKiBAcGFyYW0gYiAtIFNlY29uZCBkYXRlXG4gICAqIEByZXR1cm5zIFRydWUgaWYgYSBpcyBiZWZvcmUgYlxuICAgKi9cbiAgYWJzdHJhY3QgaXNCZWZvcmUoYTogVCB8IG51bGwsIGI6IFQgfCBudWxsKTogYm9vbGVhbjtcblxuICAvKipcbiAgICogQ2hlY2tzIGlmIGEgZGF0ZSBpcyBhZnRlciBhbm90aGVyXG4gICAqIEBwYXJhbSBhIC0gRmlyc3QgZGF0ZVxuICAgKiBAcGFyYW0gYiAtIFNlY29uZCBkYXRlXG4gICAqIEByZXR1cm5zIFRydWUgaWYgYSBpcyBhZnRlciBiXG4gICAqL1xuICBhYnN0cmFjdCBpc0FmdGVyKGE6IFQgfCBudWxsLCBiOiBUIHwgbnVsbCk6IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIENoZWNrcyBpZiBhIGRhdGUgaXMgYmV0d2VlbiB0d28gb3RoZXIgZGF0ZXNcbiAgICogQHBhcmFtIGRhdGUgLSBEYXRlIHRvIGNoZWNrXG4gICAqIEBwYXJhbSBzdGFydCAtIFN0YXJ0IGRhdGVcbiAgICogQHBhcmFtIGVuZCAtIEVuZCBkYXRlXG4gICAqIEByZXR1cm5zIFRydWUgaWYgZGF0ZSBpcyBiZXR3ZWVuIHN0YXJ0IGFuZCBlbmRcbiAgICovXG4gIGFic3RyYWN0IGlzQmV0d2VlbihkYXRlOiBUIHwgbnVsbCwgc3RhcnQ6IFQgfCBudWxsLCBlbmQ6IFQgfCBudWxsKTogYm9vbGVhbjtcblxuICAvKipcbiAgICogQ2xvbmVzIGEgZGF0ZSBvYmplY3RcbiAgICogQHBhcmFtIGRhdGUgLSBEYXRlIHRvIGNsb25lXG4gICAqIEByZXR1cm5zIENsb25lZCBkYXRlIG9iamVjdFxuICAgKi9cbiAgYWJzdHJhY3QgY2xvbmUoZGF0ZTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENoZWNrcyBpZiBhIHZhbHVlIGlzIGEgdmFsaWQgZGF0ZVxuICAgKiBAcGFyYW0gZGF0ZSAtIFZhbHVlIHRvIGNoZWNrXG4gICAqIEByZXR1cm5zIFRydWUgaWYgdmFsaWQgZGF0ZVxuICAgKi9cbiAgYWJzdHJhY3QgaXNWYWxpZChkYXRlOiBhbnkpOiBib29sZWFuO1xufVxuXG4vKipcbiAqIEluamVjdGlvbiB0b2tlbiBmb3IgRGF0ZUFkYXB0ZXJcbiAqL1xuZXhwb3J0IGNvbnN0IERBVEVfQURBUFRFUiA9IG5ldyBJbmplY3Rpb25Ub2tlbjxEYXRlQWRhcHRlcj4oJ0RBVEVfQURBUFRFUicpO1xuIl19