@oneluiz/dual-datepicker 3.0.0 → 3.0.1
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/package.json +19 -23
- package/.angular/cache/18.2.21/demo/angular-compiler.db +0 -0
- package/.angular/cache/18.2.21/demo/angular-compiler.db-lock +0 -0
- package/.nojekyll +0 -0
- package/CHANGELOG.md +0 -254
- package/GITHUB_PAGES.md +0 -82
- package/MIGRATION_V3.md +0 -210
- package/angular.json +0 -75
- package/dist/LICENSE +0 -21
- package/dist/README.md +0 -925
- package/ng-package.json +0 -7
- /package/{dist/date-adapter.d.ts → date-adapter.d.ts} +0 -0
- /package/{dist/dual-datepicker.component.d.ts → dual-datepicker.component.d.ts} +0 -0
- /package/{dist/esm2022 → esm2022}/date-adapter.mjs +0 -0
- /package/{dist/esm2022 → esm2022}/dual-datepicker.component.mjs +0 -0
- /package/{dist/esm2022 → esm2022}/native-date-adapter.mjs +0 -0
- /package/{dist/esm2022 → esm2022}/oneluiz-dual-datepicker.mjs +0 -0
- /package/{dist/esm2022 → esm2022}/preset-utils.mjs +0 -0
- /package/{dist/esm2022 → esm2022}/public-api.mjs +0 -0
- /package/{dist/fesm2022 → fesm2022}/oneluiz-dual-datepicker.mjs +0 -0
- /package/{dist/fesm2022 → fesm2022}/oneluiz-dual-datepicker.mjs.map +0 -0
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/native-date-adapter.d.ts → native-date-adapter.d.ts} +0 -0
- /package/{dist/preset-utils.d.ts → preset-utils.d.ts} +0 -0
- /package/{dist/public-api.d.ts → public-api.d.ts} +0 -0
package/dist/README.md
DELETED
|
@@ -1,925 +0,0 @@
|
|
|
1
|
-
# ng-dual-datepicker
|
|
2
|
-
|
|
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
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@oneluiz/dual-datepicker)
|
|
6
|
-

|
|
7
|
-

|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @oneluiz/dual-datepicker
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## 🎯 [Live Demo](https://oneluiz.github.io/ng-dual-datepicker/)
|
|
14
|
-
|
|
15
|
-
**[Check out the interactive examples →](https://oneluiz.github.io/ng-dual-datepicker/)**
|
|
16
|
-
|
|
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
|
-
| **Multi-Range Support** | ✅ NEW v2.7.0 | ❌ Not available |
|
|
26
|
-
| **Customization** | Full styling control | Theme-constrained |
|
|
27
|
-
| **Learning Curve** | Minimal | Requires Material knowledge |
|
|
28
|
-
| **Change Detection** | OnPush optimized | Default |
|
|
29
|
-
| **Setup Time** | < 1 minute | ~10+ minutes (theming, modules) |
|
|
30
|
-
|
|
31
|
-
## ✨ Key Features
|
|
32
|
-
|
|
33
|
-
- 🪶 **Zero Dependencies** – No external libraries required
|
|
34
|
-
- 🎯 **Standalone Component** – No NgModule imports needed
|
|
35
|
-
- ⚡ **Angular Signals** – Modern reactive state management
|
|
36
|
-
- 🔄 **Reactive Forms** – Full ControlValueAccessor implementation
|
|
37
|
-
- 🔥 **Multi-Range Support** – Select multiple date ranges (NEW v2.7.0 - Material CAN'T do this!)
|
|
38
|
-
- 🎨 **Fully Customizable** – Every color, padding, border configurable
|
|
39
|
-
- 📦 **Lightweight** – ~60 KB gzipped total bundle
|
|
40
|
-
- 🚀 **Performance** – OnPush change detection + trackBy optimization
|
|
41
|
-
- ♿ **Accessible** – ARIA labels, semantic HTML, keyboard navigation (in progress)
|
|
42
|
-
- 🌍 **i18n Ready** – Customizable month/day names
|
|
43
|
-
- 📱 **Responsive** – Works on desktop and mobile
|
|
44
|
-
|
|
45
|
-
## 🤔 When Should I Use This?
|
|
46
|
-
|
|
47
|
-
**Use ng-dual-datepicker if you:**
|
|
48
|
-
- Don't want to install Angular Material just for a date picker
|
|
49
|
-
- Need precise control over styling and behavior
|
|
50
|
-
- Want minimal bundle size impact
|
|
51
|
-
- Prefer standalone components over NgModules
|
|
52
|
-
- Need Angular Signals support now
|
|
53
|
-
- Are building a custom design system
|
|
54
|
-
|
|
55
|
-
**Use Angular Material DateRangePicker if you:**
|
|
56
|
-
- Already use Angular Material throughout your app
|
|
57
|
-
- Need Material Design compliance
|
|
58
|
-
- Want a battle-tested enterprise solution with extensive ecosystem support
|
|
59
|
-
|
|
60
|
-
## ⚡ Performance
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
@Component({
|
|
64
|
-
changeDetection: ChangeDetectionStrategy.OnPush, // ✅ Optimized
|
|
65
|
-
standalone: true // ✅ No module overhead
|
|
66
|
-
})
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
- **OnPush change detection** – Minimal re-renders
|
|
70
|
-
- **trackBy functions** – Efficient list rendering
|
|
71
|
-
- **No external CSS** – No runtime stylesheet downloads
|
|
72
|
-
- **Tree-shakeable** – Only import what you use
|
|
73
|
-
|
|
74
|
-
## ♿ Accessibility (A11y)
|
|
75
|
-
|
|
76
|
-
**Current Status:**
|
|
77
|
-
- ✅ **Screen reader support** - ARIA labels included for all interactive elements
|
|
78
|
-
- ✅ **Semantic HTML** - Proper HTML structure
|
|
79
|
-
- 🚧 **Full keyboard navigation** - In active development (see [Roadmap](#-roadmap))
|
|
80
|
-
- Mouse/touch interaction: ✅ Fully supported
|
|
81
|
-
- Keyboard navigation: 🚧 In progress
|
|
82
|
-
|
|
83
|
-
> **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.
|
|
84
|
-
|
|
85
|
-
## 📦 Installation
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
npm install @oneluiz/dual-datepicker
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## 🚀 Quick Start
|
|
92
|
-
|
|
93
|
-
### Basic Usage
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
import { Component } from '@angular/core';
|
|
97
|
-
import { DualDatepickerComponent, DateRange } from '@oneluiz/dual-datepicker';
|
|
98
|
-
|
|
99
|
-
@Component({
|
|
100
|
-
selector: 'app-root',
|
|
101
|
-
standalone: true,
|
|
102
|
-
imports: [DualDatepickerComponent],
|
|
103
|
-
template: `
|
|
104
|
-
<ngx-dual-datepicker
|
|
105
|
-
(dateRangeChange)="onRangeChange($event)">
|
|
106
|
-
</ngx-dual-datepicker>
|
|
107
|
-
`
|
|
108
|
-
})
|
|
109
|
-
export class AppComponent {
|
|
110
|
-
onRangeChange(range: DateRange) {
|
|
111
|
-
console.log('Start:', range.fechaInicio);
|
|
112
|
-
console.log('End:', range.fechaFin);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### With Reactive Forms
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { FormControl } from '@angular/forms';
|
|
121
|
-
import { DateRange } from '@oneluiz/dual-datepicker';
|
|
122
|
-
|
|
123
|
-
dateRange = new FormControl<DateRange | null>(null);
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
```html
|
|
127
|
-
<ngx-dual-datepicker [formControl]="dateRange"></ngx-dual-datepicker>
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### With Angular Signals
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
import { signal } from '@angular/core';
|
|
134
|
-
|
|
135
|
-
dateRange = signal<DateRange | null>(null);
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
```html
|
|
139
|
-
<ngx-dual-datepicker
|
|
140
|
-
[(ngModel)]="dateRange()"
|
|
141
|
-
(dateRangeChange)="dateRange.set($event)">
|
|
142
|
-
</ngx-dual-datepicker>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Custom Styling
|
|
146
|
-
|
|
147
|
-
```html
|
|
148
|
-
<ngx-dual-datepicker
|
|
149
|
-
inputBackgroundColor="#1a1a2e"
|
|
150
|
-
inputTextColor="#eee"
|
|
151
|
-
inputBorderColor="#4a5568"
|
|
152
|
-
inputBorderColorFocus="#3182ce">
|
|
153
|
-
</ngx-dual-datepicker>
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## 📚 Advanced Usage
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### 4. Use with Angular Signals ⚡ New!
|
|
161
|
-
|
|
162
|
-
The component now uses Angular Signals internally for better performance and reactivity:
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
import { Component, signal, computed } from '@angular/core';
|
|
166
|
-
import { DualDatepickerComponent, DateRange } from '@oneluiz/dual-datepicker';
|
|
167
|
-
|
|
168
|
-
@Component({
|
|
169
|
-
selector: 'app-signals-example',
|
|
170
|
-
standalone: true,
|
|
171
|
-
imports: [DualDatepickerComponent],
|
|
172
|
-
template: `
|
|
173
|
-
<ngx-dual-datepicker
|
|
174
|
-
[fechaInicio]="fechaInicio()"
|
|
175
|
-
[fechaFin]="fechaFin()"
|
|
176
|
-
(dateRangeChange)="onDateChange($event)">
|
|
177
|
-
</ngx-dual-datepicker>
|
|
178
|
-
|
|
179
|
-
@if (isRangeSelected()) {
|
|
180
|
-
<div>
|
|
181
|
-
<p>{{ rangeText() }}</p>
|
|
182
|
-
<p>Days selected: {{ daysDifference() }}</p>
|
|
183
|
-
</div>
|
|
184
|
-
}
|
|
185
|
-
`
|
|
186
|
-
})
|
|
187
|
-
export class SignalsExampleComponent {
|
|
188
|
-
fechaInicio = signal('');
|
|
189
|
-
fechaFin = signal('');
|
|
190
|
-
|
|
191
|
-
// Computed values
|
|
192
|
-
isRangeSelected = computed(() =>
|
|
193
|
-
this.fechaInicio() !== '' && this.fechaFin() !== ''
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
rangeText = computed(() =>
|
|
197
|
-
this.isRangeSelected()
|
|
198
|
-
? `${this.fechaInicio()} to ${this.fechaFin()}`
|
|
199
|
-
: 'No range selected'
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
daysDifference = computed(() => {
|
|
203
|
-
if (!this.isRangeSelected()) return 0;
|
|
204
|
-
const start = new Date(this.fechaInicio());
|
|
205
|
-
const end = new Date(this.fechaFin());
|
|
206
|
-
return Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
onDateChange(range: DateRange) {
|
|
210
|
-
this.fechaInicio.set(range.fechaInicio);
|
|
211
|
-
this.fechaFin.set(range.fechaFin);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### 5. Multi-Range Support 🔥 NEW v2.7.0!
|
|
217
|
-
|
|
218
|
-
**Material CAN'T do this!** Select multiple date ranges in a single picker - perfect for booking systems, blackout periods, and complex scheduling.
|
|
219
|
-
|
|
220
|
-
```typescript
|
|
221
|
-
import { Component } from '@angular/core';
|
|
222
|
-
import { DualDatepickerComponent, MultiDateRange } from '@oneluiz/dual-datepicker';
|
|
223
|
-
|
|
224
|
-
@Component({
|
|
225
|
-
selector: 'app-multi-range',
|
|
226
|
-
standalone: true,
|
|
227
|
-
imports: [DualDatepickerComponent],
|
|
228
|
-
template: `
|
|
229
|
-
<ngx-dual-datepicker
|
|
230
|
-
[multiRange]="true"
|
|
231
|
-
[showClearButton]="true"
|
|
232
|
-
(multiDateRangeChange)="onMultiRangeChange($event)">
|
|
233
|
-
</ngx-dual-datepicker>
|
|
234
|
-
|
|
235
|
-
@if (selectedRanges && selectedRanges.ranges.length > 0) {
|
|
236
|
-
<div class="selected-ranges">
|
|
237
|
-
<h3>Selected Ranges ({{ selectedRanges.ranges.length }})</h3>
|
|
238
|
-
@for (range of selectedRanges.ranges; track $index) {
|
|
239
|
-
<div class="range-item">
|
|
240
|
-
<strong>Range {{ $index + 1 }}:</strong> {{ range.rangoTexto }}
|
|
241
|
-
<br>
|
|
242
|
-
<span>{{ range.fechaInicio }} → {{ range.fechaFin }}</span>
|
|
243
|
-
</div>
|
|
244
|
-
}
|
|
245
|
-
</div>
|
|
246
|
-
}
|
|
247
|
-
`
|
|
248
|
-
})
|
|
249
|
-
export class MultiRangeExample {
|
|
250
|
-
selectedRanges: MultiDateRange | null = null;
|
|
251
|
-
|
|
252
|
-
onMultiRangeChange(ranges: MultiDateRange) {
|
|
253
|
-
this.selectedRanges = ranges;
|
|
254
|
-
console.log('Selected ranges:', ranges.ranges);
|
|
255
|
-
// Output example:
|
|
256
|
-
// [
|
|
257
|
-
// { fechaInicio: '2026-01-01', fechaFin: '2026-01-05', rangoTexto: 'Jan 1 – Jan 5' },
|
|
258
|
-
// { fechaInicio: '2026-01-10', fechaFin: '2026-01-15', rangoTexto: 'Jan 10 – Jan 15' },
|
|
259
|
-
// { fechaInicio: '2026-02-01', fechaFin: '2026-02-07', rangoTexto: 'Feb 1 – Feb 7' }
|
|
260
|
-
// ]
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
#### Perfect Use Cases
|
|
266
|
-
|
|
267
|
-
- 🏨 **Hotel Booking Systems** - Block multiple periods for reservations
|
|
268
|
-
- 📅 **Event Blackout Periods** - Mark multiple dates as unavailable
|
|
269
|
-
- 🔧 **Maintenance Windows** - Schedule multiple maintenance periods
|
|
270
|
-
- 📊 **Availability Calendars** - Show multiple available/unavailable periods
|
|
271
|
-
- 👷 **Shift Scheduling** - Select multiple work periods
|
|
272
|
-
- 💼 **Business Meetings** - Block out multiple date ranges
|
|
273
|
-
|
|
274
|
-
#### Key Features
|
|
275
|
-
|
|
276
|
-
- ✅ Select unlimited date ranges
|
|
277
|
-
- ✅ Visual feedback - all ranges highlighted in calendar
|
|
278
|
-
- ✅ Easy management - add/remove ranges with one click
|
|
279
|
-
- ✅ Separate events for multi-range (`multiDateRangeChange`, `multiDateRangeSelected`)
|
|
280
|
-
- ✅ Clear all ranges with one button
|
|
281
|
-
- ❌ **Angular Material CANNOT do this!**
|
|
282
|
-
|
|
283
|
-
## 🔌 Date Adapter System
|
|
284
|
-
|
|
285
|
-
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.
|
|
286
|
-
|
|
287
|
-
### Using Native Date (Default)
|
|
288
|
-
|
|
289
|
-
By default, the component uses `NativeDateAdapter` which works with JavaScript `Date` objects:
|
|
290
|
-
|
|
291
|
-
```typescript
|
|
292
|
-
import { DualDatepickerComponent } from '@oneluiz/dual-datepicker';
|
|
293
|
-
|
|
294
|
-
@Component({
|
|
295
|
-
standalone: true,
|
|
296
|
-
imports: [DualDatepickerComponent],
|
|
297
|
-
template: `<ngx-dual-datepicker></ngx-dual-datepicker>`
|
|
298
|
-
})
|
|
299
|
-
export class AppComponent {}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
### Creating a Custom Adapter
|
|
303
|
-
|
|
304
|
-
Example using **date-fns**:
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
import { Injectable } from '@angular/core';
|
|
308
|
-
import { DateAdapter } from '@oneluiz/dual-datepicker';
|
|
309
|
-
import {
|
|
310
|
-
parse, format, addDays, addMonths,
|
|
311
|
-
getYear, getMonth, getDate, getDay,
|
|
312
|
-
isSameDay, isBefore, isAfter, isWithinInterval,
|
|
313
|
-
isValid
|
|
314
|
-
} from 'date-fns';
|
|
315
|
-
|
|
316
|
-
@Injectable()
|
|
317
|
-
export class DateFnsAdapter extends DateAdapter<Date> {
|
|
318
|
-
parse(value: any): Date | null {
|
|
319
|
-
if (!value) return null;
|
|
320
|
-
if (value instanceof Date) return value;
|
|
321
|
-
|
|
322
|
-
const parsed = parse(value, 'yyyy-MM-dd', new Date());
|
|
323
|
-
return isValid(parsed) ? parsed : null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
format(date: Date, formatStr: string = 'yyyy-MM-dd'): string {
|
|
327
|
-
return format(date, formatStr);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
addDays(date: Date, days: number): Date {
|
|
331
|
-
return addDays(date, days);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
addMonths(date: Date, months: number): Date {
|
|
335
|
-
return addMonths(date, months);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
getYear(date: Date): number {
|
|
339
|
-
return getYear(date);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
getMonth(date: Date): number {
|
|
343
|
-
return getMonth(date);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
getDate(date: Date): number {
|
|
347
|
-
return getDate(date);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
getDay(date: Date): number {
|
|
351
|
-
return getDay(date);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
createDate(year: number, month: number, day: number): Date {
|
|
355
|
-
return new Date(year, month, day);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
today(): Date {
|
|
359
|
-
return new Date();
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
isSameDay(a: Date | null, b: Date | null): boolean {
|
|
363
|
-
if (!a || !b) return false;
|
|
364
|
-
return isSameDay(a, b);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
isBefore(a: Date | null, b: Date | null): boolean {
|
|
368
|
-
if (!a || !b) return false;
|
|
369
|
-
return isBefore(a, b);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
isAfter(a: Date | null, b: Date | null): boolean {
|
|
373
|
-
if (!a || !b) return false;
|
|
374
|
-
return isAfter(a, b);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
isBetween(date: Date | null, start: Date | null, end: Date | null): boolean {
|
|
378
|
-
if (!date || !start || !end) return false;
|
|
379
|
-
return isWithinInterval(date, { start, end });
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
clone(date: Date): Date {
|
|
383
|
-
return new Date(date);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
isValid(date: any): boolean {
|
|
387
|
-
return isValid(date);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### Providing Custom Adapter
|
|
393
|
-
|
|
394
|
-
```typescript
|
|
395
|
-
import { Component } from '@angular/core';
|
|
396
|
-
import { DualDatepickerComponent, DATE_ADAPTER } from '@oneluiz/dual-datepicker';
|
|
397
|
-
import { DateFnsAdapter } from './date-fns-adapter';
|
|
398
|
-
|
|
399
|
-
@Component({
|
|
400
|
-
standalone: true,
|
|
401
|
-
imports: [DualDatepickerComponent],
|
|
402
|
-
providers: [
|
|
403
|
-
{ provide: DATE_ADAPTER, useClass: DateFnsAdapter }
|
|
404
|
-
],
|
|
405
|
-
template: `<ngx-dual-datepicker></ngx-dual-datepicker>`
|
|
406
|
-
})
|
|
407
|
-
export class AppComponent {}
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
### Example: DayJS Adapter
|
|
411
|
-
|
|
412
|
-
```typescript
|
|
413
|
-
import { Injectable } from '@angular/core';
|
|
414
|
-
import { DateAdapter } from '@oneluiz/dual-datepicker';
|
|
415
|
-
import dayjs, { Dayjs } from 'dayjs';
|
|
416
|
-
|
|
417
|
-
@Injectable()
|
|
418
|
-
export class DayJSAdapter extends DateAdapter<Dayjs> {
|
|
419
|
-
parse(value: any): Dayjs | null {
|
|
420
|
-
if (!value) return null;
|
|
421
|
-
const parsed = dayjs(value);
|
|
422
|
-
return parsed.isValid() ? parsed : null;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
format(date: Dayjs, format: string = 'YYYY-MM-DD'): string {
|
|
426
|
-
return date.format(format);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
addDays(date: Dayjs, days: number): Dayjs {
|
|
430
|
-
return date.add(days, 'day');
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
addMonths(date: Dayjs, months: number): Dayjs {
|
|
434
|
-
return date.add(months, 'month');
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
getYear(date: Dayjs): number {
|
|
438
|
-
return date.year();
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
getMonth(date: Dayjs): number {
|
|
442
|
-
return date.month();
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
getDate(date: Dayjs): number {
|
|
446
|
-
return date.date();
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
getDay(date: Dayjs): number {
|
|
450
|
-
return date.day();
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
createDate(year: number, month: number, day: number): Dayjs {
|
|
454
|
-
return dayjs().year(year).month(month).date(day);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
today(): Dayjs {
|
|
458
|
-
return dayjs();
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
isSameDay(a: Dayjs | null, b: Dayjs | null): boolean {
|
|
462
|
-
if (!a || !b) return false;
|
|
463
|
-
return a.isSame(b, 'day');
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
isBefore(a: Dayjs | null, b: Dayjs | null): boolean {
|
|
467
|
-
if (!a || !b) return false;
|
|
468
|
-
return a.isBefore(b);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
isAfter(a: Dayjs | null, b: Dayjs | null): boolean {
|
|
472
|
-
if (!a || !b) return false;
|
|
473
|
-
return a.isAfter(b);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
isBetween(date: Dayjs | null, start: Dayjs | null, end: Dayjs | null): boolean {
|
|
477
|
-
if (!date || !start || !end) return false;
|
|
478
|
-
return date.isAfter(start) && date.isBefore(end) || date.isSame(start) || date.isSame(end);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
clone(date: Dayjs): Dayjs {
|
|
482
|
-
return date.clone();
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
isValid(date: any): boolean {
|
|
486
|
-
return dayjs.isDayjs(date) && date.isValid();
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### Benefits of Date Adapters
|
|
492
|
-
|
|
493
|
-
- ✅ **Zero vendor lock-in** - Use any date library you prefer
|
|
494
|
-
- ✅ **Consistency** - Use the same date library across your entire app
|
|
495
|
-
- ✅ **Custom backend models** - Adapt to your API's date format
|
|
496
|
-
- ✅ **Type safety** - Full TypeScript support with generics
|
|
497
|
-
|
|
498
|
-
## 🎨 Customization
|
|
499
|
-
|
|
500
|
-
### Custom Colors (Bootstrap Style)
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
<ngx-dual-datepicker
|
|
504
|
-
[(ngModel)]="dateRange"
|
|
505
|
-
inputBackgroundColor="#ffffff"
|
|
506
|
-
inputTextColor="#495057"
|
|
507
|
-
inputBorderColor="#ced4da"
|
|
508
|
-
inputBorderColorHover="#80bdff"
|
|
509
|
-
inputBorderColorFocus="#80bdff"
|
|
510
|
-
inputPadding="0.375rem 0.75rem">
|
|
511
|
-
</ngx-dual-datepicker>
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
### Custom Colors (GitHub Style)
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
<ngx-dual-datepicker
|
|
518
|
-
[(ngModel)]="dateRange"
|
|
519
|
-
inputBackgroundColor="#f3f4f6"
|
|
520
|
-
inputTextColor="#24292e"
|
|
521
|
-
inputBorderColor="transparent"
|
|
522
|
-
inputBorderColorHover="#d1d5db"
|
|
523
|
-
inputBorderColorFocus="#80bdff"
|
|
524
|
-
inputPadding="6px 10px">
|
|
525
|
-
</ngx-dual-datepicker>
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
### ⚡ Custom Presets (Power Feature)
|
|
529
|
-
|
|
530
|
-
**This is where our library shines!** Unlike Angular Material, we offer an incredibly flexible preset system perfect for dashboards, reporting, POS, BI apps, and ERP systems.
|
|
531
|
-
|
|
532
|
-
#### Simple Pattern (Backward Compatible)
|
|
533
|
-
|
|
534
|
-
```typescript
|
|
535
|
-
customPresets: PresetConfig[] = [
|
|
536
|
-
{ label: 'Last 15 days', daysAgo: 15 },
|
|
537
|
-
{ label: 'Last 3 months', daysAgo: 90 },
|
|
538
|
-
{ label: 'Last 6 months', daysAgo: 180 },
|
|
539
|
-
{ label: 'Last year', daysAgo: 365 }
|
|
540
|
-
];
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
#### **NEW v2.6.0** - Flexible Pattern with `getValue()` 🔥
|
|
544
|
-
|
|
545
|
-
The real power comes with the `getValue()` pattern. Define **any custom logic** you need:
|
|
546
|
-
|
|
547
|
-
```typescript
|
|
548
|
-
import { PresetConfig } from '@oneluiz/dual-datepicker';
|
|
549
|
-
|
|
550
|
-
customPresets: PresetConfig[] = [
|
|
551
|
-
{
|
|
552
|
-
label: 'Today',
|
|
553
|
-
getValue: () => {
|
|
554
|
-
const today = new Date();
|
|
555
|
-
return {
|
|
556
|
-
start: formatDate(today),
|
|
557
|
-
end: formatDate(today)
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
},
|
|
561
|
-
{
|
|
562
|
-
label: 'This Month',
|
|
563
|
-
getValue: () => {
|
|
564
|
-
const today = new Date();
|
|
565
|
-
const start = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
566
|
-
const end = new Date(today.getFullYear(), today.getMonth() + 1, 0);
|
|
567
|
-
return {
|
|
568
|
-
start: formatDate(start),
|
|
569
|
-
end: formatDate(end)
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
},
|
|
573
|
-
{
|
|
574
|
-
label: 'Last Month',
|
|
575
|
-
getValue: () => {
|
|
576
|
-
const today = new Date();
|
|
577
|
-
const start = new Date(today.getFullYear(), today.getMonth() - 1, 1);
|
|
578
|
-
const end = new Date(today.getFullYear(), today.getMonth(), 0);
|
|
579
|
-
return {
|
|
580
|
-
start: formatDate(start),
|
|
581
|
-
end: formatDate(end)
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
},
|
|
585
|
-
{
|
|
586
|
-
label: 'Quarter to Date',
|
|
587
|
-
getValue: () => {
|
|
588
|
-
const today = new Date();
|
|
589
|
-
const currentMonth = today.getMonth();
|
|
590
|
-
const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
|
|
591
|
-
const start = new Date(today.getFullYear(), quarterStartMonth, 1);
|
|
592
|
-
return {
|
|
593
|
-
start: formatDate(start),
|
|
594
|
-
end: formatDate(today)
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
];
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
#### **Even Better** - Use Pre-built Utilities 🚀
|
|
602
|
-
|
|
603
|
-
We provide **ready-to-use preset utilities** for common scenarios:
|
|
604
|
-
|
|
605
|
-
```typescript
|
|
606
|
-
import { CommonPresets } from '@oneluiz/dual-datepicker';
|
|
607
|
-
|
|
608
|
-
// Dashboard presets
|
|
609
|
-
presets = CommonPresets.dashboard;
|
|
610
|
-
// → Today, Yesterday, Last 7 days, Last 30 days, This month, Last month
|
|
611
|
-
|
|
612
|
-
// Reporting presets
|
|
613
|
-
presets = CommonPresets.reporting;
|
|
614
|
-
// → Today, This week, Last week, This month, Last month, This quarter, Last quarter
|
|
615
|
-
|
|
616
|
-
// Financial/ERP presets
|
|
617
|
-
presets = CommonPresets.financial;
|
|
618
|
-
// → Month to date, Quarter to date, Year to date, Last month, Last quarter, Last year
|
|
619
|
-
|
|
620
|
-
// Analytics/BI presets
|
|
621
|
-
presets = CommonPresets.analytics;
|
|
622
|
-
// → Last 7/14/30/60/90/180/365 days
|
|
623
|
-
|
|
624
|
-
// Simple presets
|
|
625
|
-
presets = CommonPresets.simple;
|
|
626
|
-
// → Today, Last 7 days, Last 30 days, This year
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
#### Create Your Own Utilities
|
|
630
|
-
|
|
631
|
-
Import individual utilities and mix them:
|
|
632
|
-
|
|
633
|
-
```typescript
|
|
634
|
-
import {
|
|
635
|
-
getToday,
|
|
636
|
-
getThisMonth,
|
|
637
|
-
getLastMonth,
|
|
638
|
-
getQuarterToDate,
|
|
639
|
-
getYearToDate,
|
|
640
|
-
PresetConfig
|
|
641
|
-
} from '@oneluiz/dual-datepicker';
|
|
642
|
-
|
|
643
|
-
customPresets: PresetConfig[] = [
|
|
644
|
-
{ label: 'Today', getValue: getToday },
|
|
645
|
-
{ label: 'This Month', getValue: getThisMonth },
|
|
646
|
-
{ label: 'Last Month', getValue: getLastMonth },
|
|
647
|
-
{ label: 'Quarter to Date', getValue: getQuarterToDate },
|
|
648
|
-
{ label: 'Year to Date', getValue: getYearToDate },
|
|
649
|
-
{
|
|
650
|
-
label: 'Custom Logic',
|
|
651
|
-
getValue: () => {
|
|
652
|
-
// Your custom date calculation
|
|
653
|
-
return { start: '2026-01-01', end: '2026-12-31' };
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
];
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
#### Why This Is Powerful
|
|
660
|
-
|
|
661
|
-
✅ **Perfect for Dashboards** - "Last 7 days", "Month to date", "Quarter to date"
|
|
662
|
-
✅ **Perfect for Reporting** - "This week", "Last week", "This quarter"
|
|
663
|
-
✅ **Perfect for Financial Systems** - "Quarter to date", "Year to date", "Fiscal year"
|
|
664
|
-
✅ **Perfect for Analytics** - Consistent date ranges for BI tools
|
|
665
|
-
✅ **Perfect for ERP** - Custom business logic and fiscal calendars
|
|
666
|
-
|
|
667
|
-
**Angular Material doesn't offer this level of flexibility!** 🎯
|
|
668
|
-
|
|
669
|
-
```html
|
|
670
|
-
<ngx-dual-datepicker
|
|
671
|
-
[(ngModel)]="dateRange"
|
|
672
|
-
[presets]="customPresets">
|
|
673
|
-
</ngx-dual-datepicker>
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
## 📖 API Reference
|
|
677
|
-
|
|
678
|
-
### Inputs
|
|
679
|
-
|
|
680
|
-
| Property | Type | Default | Description |
|
|
681
|
-
|----------|------|---------|-------------|
|
|
682
|
-
| `ngModel` | `DateRange` | `{ start: null, end: null }` | Two-way binding for selected date range |
|
|
683
|
-
| `placeholder` | `string` | `'Select date range'` | Input placeholder text |
|
|
684
|
-
| `presets` | `PresetConfig[]` | Default presets | Array of preset configurations |
|
|
685
|
-
| `showPresets` | `boolean` | `true` | Show/hide the presets sidebar |
|
|
686
|
-
| `showClearButton` | `boolean` | `false` | Show/hide the Clear button in dropdown |
|
|
687
|
-
| `closeOnSelection` | `boolean` | `false` | Close picker when both dates selected |
|
|
688
|
-
| `closeOnPresetSelection` | `boolean` | `false` | Close picker when preset is clicked |
|
|
689
|
-
| `closeOnClickOutside` | `boolean` | `true` | Close picker when clicking outside |
|
|
690
|
-
| `inputBackgroundColor` | `string` | `'#fff'` | Input background color |
|
|
691
|
-
| `inputTextColor` | `string` | `'#495057'` | Input text color |
|
|
692
|
-
| `inputBorderColor` | `string` | `'#ced4da'` | Input border color |
|
|
693
|
-
| `inputBorderColorHover` | `string` | `'#9ca3af'` | Input border color on hover |
|
|
694
|
-
| `inputBorderColorFocus` | `string` | `'#80bdff'` | Input border color on focus |
|
|
695
|
-
| `inputPadding` | `string` | `'0.375rem 0.75rem'` | Input padding |
|
|
696
|
-
| `locale` | `LocaleConfig` | English defaults | Custom month/day names for i18n |
|
|
697
|
-
|
|
698
|
-
### Outputs
|
|
699
|
-
|
|
700
|
-
| Event | Type | Description |
|
|
701
|
-
|-------|------|-------------|
|
|
702
|
-
| `ngModelChange` | `EventEmitter<DateRange>` | Emitted when date range changes |
|
|
703
|
-
|
|
704
|
-
### Public Methods
|
|
705
|
-
|
|
706
|
-
You can call these methods programmatically using a template reference or ViewChild:
|
|
707
|
-
|
|
708
|
-
```typescript
|
|
709
|
-
import { Component, ViewChild } from '@angular/core';
|
|
710
|
-
import { DualDatepickerComponent } from '@oneluiz/dual-datepicker';
|
|
711
|
-
|
|
712
|
-
@Component({
|
|
713
|
-
template: `
|
|
714
|
-
<div style="display: flex; gap: 10px;">
|
|
715
|
-
<ngx-dual-datepicker #datepicker></ngx-dual-datepicker>
|
|
716
|
-
<button (click)="clearSelection()">Clear</button>
|
|
717
|
-
</div>
|
|
718
|
-
`
|
|
719
|
-
})
|
|
720
|
-
export class MyComponent {
|
|
721
|
-
@ViewChild('datepicker') datepicker!: DualDatepickerComponent;
|
|
722
|
-
|
|
723
|
-
clearSelection() {
|
|
724
|
-
this.datepicker.limpiar(); // Clears the date selection
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
| Method | Description |
|
|
730
|
-
|--------|-------------|
|
|
731
|
-
| `limpiar()` | Clears the current date selection and resets the component |
|
|
732
|
-
|
|
733
|
-
### Types
|
|
734
|
-
|
|
735
|
-
```typescript
|
|
736
|
-
interface DateRange {
|
|
737
|
-
fechaInicio: string; // ISO date format: 'YYYY-MM-DD'
|
|
738
|
-
fechaFin: string; // ISO date format: 'YYYY-MM-DD'
|
|
739
|
-
rangoTexto: string; // Display text: 'DD Mon - DD Mon'
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
interface PresetRange {
|
|
743
|
-
start: string; // ISO date format: 'YYYY-MM-DD'
|
|
744
|
-
end: string; // ISO date format: 'YYYY-MM-DD'
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
interface PresetConfig {
|
|
748
|
-
label: string;
|
|
749
|
-
/** @deprecated Use getValue() instead for more flexibility */
|
|
750
|
-
daysAgo?: number;
|
|
751
|
-
/** NEW v2.6.0 - Function that returns date range with custom logic */
|
|
752
|
-
getValue?: () => PresetRange;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
interface LocaleConfig {
|
|
756
|
-
monthNames?: string[]; // Full month names (12 items)
|
|
757
|
-
monthNamesShort?: string[]; // Short month names (12 items)
|
|
758
|
-
dayNames?: string[]; // Full day names (7 items, starting Sunday)
|
|
759
|
-
dayNamesShort?: string[]; // Short day names (7 items, starting Sunday)
|
|
760
|
-
firstDayOfWeek?: number; // 0 = Sunday, 1 = Monday, etc. (not yet implemented)
|
|
761
|
-
}
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
### Default Presets
|
|
765
|
-
|
|
766
|
-
```typescript
|
|
767
|
-
[
|
|
768
|
-
{ label: 'Last month', daysAgo: 30 },
|
|
769
|
-
{ label: 'Last 6 months', daysAgo: 180 },
|
|
770
|
-
{ label: 'Last yea
|
|
771
|
-
[fechaInicio]="startDate"
|
|
772
|
-
[fechaFin]="endDate"
|
|
773
|
-
(dateRangeSelected)="onDateRangeSelected($event)">
|
|
774
|
-
</ngx-dual-datepicker>
|
|
775
|
-
```
|
|
776
|
-
|
|
777
|
-
###fechaInicio]="startDate"
|
|
778
|
-
[fechaFin]="endDate"
|
|
779
|
-
[closeOnSelection]="true"
|
|
780
|
-
[closeOnPresetSelection]="true"
|
|
781
|
-
(dateRangeSelected)="onDateRangeSelected($event)
|
|
782
|
-
spanishLocale: LocaleConfig = {
|
|
783
|
-
monthNames: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
|
|
784
|
-
'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
|
|
785
|
-
monthNamesShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun',
|
|
786
|
-
'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
|
|
787
|
-
dayNames: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
|
|
788
|
-
dayNamesShort: ['D', 'L', 'M', 'X', 'J', 'V', 'S']
|
|
789
|
-
};
|
|
790
|
-
```
|
|
791
|
-
|
|
792
|
-
```html
|
|
793
|
-
<ngx-dual-datepicker
|
|
794
|
-
[fechaInicio]="startDate"
|
|
795
|
-
[fechaFin]="endDate"
|
|
796
|
-
[fechaInicio]="startDate"
|
|
797
|
-
[fechaFin]="endDate"
|
|
798
|
-
placeholder="Pick your dates"
|
|
799
|
-
inputBackgroundColor="#fef3c7"
|
|
800
|
-
inputTextColor="#92400e"
|
|
801
|
-
inputBorderColor="#fbbf24"
|
|
802
|
-
inputBorderColorHover="#f59e0b"
|
|
803
|
-
inputBorderColorFocus="#d97706"
|
|
804
|
-
inputPadding="8px 12px"
|
|
805
|
-
(dateRangeSelected)="onDateRangeSelected($event)
|
|
806
|
-
|
|
807
|
-
### Minimal Usage
|
|
808
|
-
|
|
809
|
-
```html
|
|
810
|
-
<ngx-dual-datepicker [(ngModel)]="dateRange"></ngx-dual-datepicker>
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
### With Auto-close
|
|
814
|
-
fechaInicio]="startDate"
|
|
815
|
-
[fechaFin]="endDate"
|
|
816
|
-
(dateRangeSelected)="onDateRangeSelected($event)"
|
|
817
|
-
(dateRangeChange)="onDateRangeChange($event)">
|
|
818
|
-
</ngx-dual-datepicker>
|
|
819
|
-
|
|
820
|
-
<div *ngIf="selectedRange">
|
|
821
|
-
Selected: {{ selectedRange.rangoTexto }}
|
|
822
|
-
</div>
|
|
823
|
-
`
|
|
824
|
-
})
|
|
825
|
-
export class ExampleComponent {
|
|
826
|
-
startDate: string = '';
|
|
827
|
-
endDate: string = '';
|
|
828
|
-
selectedRange: DateRange | null = null;
|
|
829
|
-
|
|
830
|
-
onDateRangeChange(range: DateRange) {
|
|
831
|
-
console.log('Date changed:', range.fechaInicio);
|
|
832
|
-
// Emitted when user selects first date (before completing range)
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
onDateRangeSelected(range: DateRange) {
|
|
836
|
-
console.log('Range selected:', range);
|
|
837
|
-
this.selectedRange = range;
|
|
838
|
-
|
|
839
|
-
// Both dates selected - do something
|
|
840
|
-
this.fetchData(range.fechaInicio, range.fechaFin);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
fetchData(startDate: string, endDate: string) {
|
|
844
|
-
// Your API call here
|
|
845
|
-
// Dates are in 'YYYY-MM-DD' format,
|
|
846
|
-
template: `
|
|
847
|
-
<ngx-dual-datepicker
|
|
848
|
-
[(ngModel)]="dateRange"
|
|
849
|
-
(ngModelChange)="onDateRangeChange($event)">
|
|
850
|
-
</ngx-dual-datepicker>
|
|
851
|
-
|
|
852
|
-
<div *ngIf="dateRange.start && dateRange.end">
|
|
853
|
-
Selected: {{ formatDateRange() }}
|
|
854
|
-
</div>
|
|
855
|
-
`
|
|
856
|
-
})
|
|
857
|
-
export class ExampleComponent {
|
|
858
|
-
dateRange: DateRange = { start: null, end: null };
|
|
859
|
-
|
|
860
|
-
onDateRangeChange(range: DateRange) {
|
|
861
|
-
console.log('Start:', range.start);
|
|
862
|
-
console.log('End:', range.end);
|
|
863
|
-
|
|
864
|
-
if (range.start && range.end) {
|
|
865
|
-
// Both dates selected - do something
|
|
866
|
-
this.fetchData(range.start, range.end);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
formatDateRange(): string {
|
|
871
|
-
if (!this.dateRange.start || !this.dateRange.end) return '';
|
|
872
|
-
return `${this.dateRange.start.toLocaleDateString()} - ${this.dateRange.end.toLocaleDateString()}`;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
fetchData(start: Date, end: Date) {
|
|
876
|
-
// Your API call here
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
```
|
|
880
|
-
|
|
881
|
-
## 🛠️ Requirements
|
|
882
|
-
|
|
883
|
-
- Angular 17.0.0 or higher
|
|
884
|
-
- Angular 18.0.0 or higher
|
|
885
|
-
- Angular 19.0.0 or higher
|
|
886
|
-
- Angular 20.0.0 or higher
|
|
887
|
-
|
|
888
|
-
## 🗺️ Roadmap
|
|
889
|
-
|
|
890
|
-
Recently shipped:
|
|
891
|
-
|
|
892
|
-
**v2.6.0:**
|
|
893
|
-
- ✅ **Flexible Preset System** - `getValue()` pattern for custom date logic (This month, Last month, Quarter to date, etc.)
|
|
894
|
-
- ✅ **Pre-built Preset Utilities** - CommonPresets for Dashboard, Reporting, Financial, Analytics
|
|
895
|
-
- ✅ **Real Differentiator** - Perfect for ERP, BI, POS, and Reporting systems
|
|
896
|
-
|
|
897
|
-
**v2.5.0:**
|
|
898
|
-
- ✅ **Date Adapter System** - Support for DayJS, date-fns, Luxon, and custom date libraries
|
|
899
|
-
|
|
900
|
-
Planned features and improvements:
|
|
901
|
-
|
|
902
|
-
- ⬜ **Complete keyboard navigation** - Arrow keys, Enter/Space, Tab, Escape
|
|
903
|
-
- ⬜ **Full accessibility audit** - WCAG 2.1 AA compliance
|
|
904
|
-
- ⬜ **Multi-range support** - Select multiple date ranges
|
|
905
|
-
- ⬜ **Theming system** - Pre-built theme presets
|
|
906
|
-
|
|
907
|
-
## 📄 License
|
|
908
|
-
|
|
909
|
-
MIT © Luis Cortes
|
|
910
|
-
|
|
911
|
-
## 🤝 Contributing
|
|
912
|
-
|
|
913
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
914
|
-
|
|
915
|
-
## 🐛 Issues
|
|
916
|
-
|
|
917
|
-
Found a bug? Please [open an issue](https://github.com/oneluiz/ng-dual-datepicker/issues).
|
|
918
|
-
|
|
919
|
-
## ⭐ Support
|
|
920
|
-
|
|
921
|
-
If you find this package useful, please give it a star on [GitHub](https://github.com/oneluiz/ng-dual-datepicker)!
|
|
922
|
-
|
|
923
|
-
---
|
|
924
|
-
|
|
925
|
-
Made with ❤️ by [Luis Cortes](https://github.com/oneluiz)
|