@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.
- package/README.md +481 -101
- package/date-adapter.d.ts +116 -0
- package/dual-datepicker.component.d.ts +10 -3
- package/esm2022/date-adapter.mjs +12 -0
- package/esm2022/dual-datepicker.component.mjs +64 -26
- package/esm2022/native-date-adapter.mjs +112 -0
- package/esm2022/preset-utils.mjs +276 -0
- package/esm2022/public-api.mjs +6 -1
- package/fesm2022/oneluiz-dual-datepicker.mjs +458 -26
- package/fesm2022/oneluiz-dual-datepicker.mjs.map +1 -1
- package/native-date-adapter.d.ts +26 -0
- package/package.json +1 -1
- package/preset-utils.d.ts +91 -0
- package/public-api.d.ts +4 -1
package/README.md
CHANGED
|
@@ -1,30 +1,84 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ng-dual-datepicker
|
|
2
2
|
|
|
3
|
-
A
|
|
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
|
[](https://www.npmjs.com/package/@oneluiz/dual-datepicker)
|
|
6
6
|

|
|
7
7
|

|
|
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
|
-
##
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
71
|
+
|
|
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.
|
|
28
82
|
|
|
29
83
|
## π¦ Installation
|
|
30
84
|
|
|
@@ -34,7 +88,7 @@ npm install @oneluiz/dual-datepicker
|
|
|
34
88
|
|
|
35
89
|
## π Quick Start
|
|
36
90
|
|
|
37
|
-
###
|
|
91
|
+
### Basic Usage
|
|
38
92
|
|
|
39
93
|
```typescript
|
|
40
94
|
import { Component } from '@angular/core';
|
|
@@ -46,104 +100,58 @@ import { DualDatepickerComponent, DateRange } from '@oneluiz/dual-datepicker';
|
|
|
46
100
|
imports: [DualDatepickerComponent],
|
|
47
101
|
template: `
|
|
48
102
|
<ngx-dual-datepicker
|
|
49
|
-
|
|
50
|
-
placeholder="Select date range">
|
|
103
|
+
(dateRangeChange)="onRangeChange($event)">
|
|
51
104
|
</ngx-dual-datepicker>
|
|
52
105
|
`
|
|
53
106
|
})
|
|
54
107
|
export class AppComponent {
|
|
55
|
-
|
|
108
|
+
onRangeChange(range: DateRange) {
|
|
109
|
+
console.log('Start:', range.fechaInicio);
|
|
110
|
+
console.log('End:', range.fechaFin);
|
|
111
|
+
}
|
|
56
112
|
}
|
|
57
113
|
```
|
|
58
114
|
|
|
59
|
-
###
|
|
115
|
+
### With Reactive Forms
|
|
60
116
|
|
|
61
117
|
```typescript
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
64
|
-
import { DualDatepickerComponent } from '@oneluiz/dual-datepicker';
|
|
118
|
+
import { FormControl } from '@angular/forms';
|
|
119
|
+
import { DateRange } from '@oneluiz/dual-datepicker';
|
|
65
120
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
standalone: true,
|
|
69
|
-
imports: [FormsModule, DualDatepickerComponent],
|
|
70
|
-
template: `
|
|
71
|
-
<ngx-dual-datepicker
|
|
72
|
-
[fechaInicio]="fechaInicio"
|
|
73
|
-
[fechaFin]="fechaFin"
|
|
74
|
-
[presets]="customPresets"
|
|
75
|
-
(dateRangeChange)="onDateChange($event)">
|
|
76
|
-
</ngx-dual-datepicker>
|
|
77
|
-
`
|
|
78
|
-
})
|
|
79
|
-
export class ExampleComponent {
|
|
80
|
-
fechaInicio = '';
|
|
81
|
-
fechaFin = '';
|
|
82
|
-
|
|
83
|
-
customPresets = [
|
|
84
|
-
{ label: 'Last 7 days', daysAgo: 7 },
|
|
85
|
-
{ label: 'Last 30 days', daysAgo: 30 },
|
|
86
|
-
{ label: 'Last 90 days', daysAgo: 90 }
|
|
87
|
-
];
|
|
121
|
+
dateRange = new FormControl<DateRange | null>(null);
|
|
122
|
+
```
|
|
88
123
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.fechaInicio = range.fechaInicio;
|
|
92
|
-
this.fechaFin = range.fechaFin;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
124
|
+
```html
|
|
125
|
+
<ngx-dual-datepicker [formControl]="dateRange"></ngx-dual-datepicker>
|
|
95
126
|
```
|
|
96
127
|
|
|
97
|
-
###
|
|
128
|
+
### With Angular Signals
|
|
98
129
|
|
|
99
130
|
```typescript
|
|
100
|
-
import {
|
|
101
|
-
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
102
|
-
import { DualDatepickerComponent, DateRange } from '@oneluiz/dual-datepicker';
|
|
131
|
+
import { signal } from '@angular/core';
|
|
103
132
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
standalone: true,
|
|
107
|
-
imports: [ReactiveFormsModule, DualDatepickerComponent],
|
|
108
|
-
template: `
|
|
109
|
-
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
|
110
|
-
<label>Select Date Range:</label>
|
|
111
|
-
<ngx-dual-datepicker
|
|
112
|
-
formControlName="dateRange"
|
|
113
|
-
placeholder="Choose dates"
|
|
114
|
-
[showClearButton]="true">
|
|
115
|
-
</ngx-dual-datepicker>
|
|
116
|
-
|
|
117
|
-
<button type="submit" [disabled]="!form.valid">Submit</button>
|
|
118
|
-
|
|
119
|
-
@if (form.value.dateRange) {
|
|
120
|
-
<div>
|
|
121
|
-
Selected: {{ form.value.dateRange.fechaInicio }} to {{ form.value.dateRange.fechaFin }}
|
|
122
|
-
</div>
|
|
123
|
-
}
|
|
124
|
-
</form>
|
|
125
|
-
`
|
|
126
|
-
})
|
|
127
|
-
export class ReactiveFormComponent implements OnInit {
|
|
128
|
-
form!: FormGroup;
|
|
133
|
+
dateRange = signal<DateRange | null>(null);
|
|
134
|
+
```
|
|
129
135
|
|
|
130
|
-
|
|
136
|
+
```html
|
|
137
|
+
<ngx-dual-datepicker
|
|
138
|
+
[(ngModel)]="dateRange()"
|
|
139
|
+
(dateRangeChange)="dateRange.set($event)">
|
|
140
|
+
</ngx-dual-datepicker>
|
|
141
|
+
```
|
|
131
142
|
|
|
132
|
-
|
|
133
|
-
this.form = this.fb.group({
|
|
134
|
-
dateRange: [null] // Will receive DateRange object
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// Listen to changes
|
|
138
|
-
this.form.get('dateRange')?.valueChanges.subscribe(value => {
|
|
139
|
-
console.log('Date range changed:', value);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
143
|
+
### Custom Styling
|
|
142
144
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
147
155
|
}
|
|
148
156
|
```
|
|
149
157
|
|
|
@@ -203,6 +211,221 @@ export class SignalsExampleComponent {
|
|
|
203
211
|
}
|
|
204
212
|
```
|
|
205
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)
|
|
219
|
+
|
|
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();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
```
|
|
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
|
+
|
|
206
429
|
## π¨ Customization
|
|
207
430
|
|
|
208
431
|
### Custom Colors (Bootstrap Style)
|
|
@@ -233,7 +456,11 @@ export class SignalsExampleComponent {
|
|
|
233
456
|
</ngx-dual-datepicker>
|
|
234
457
|
```
|
|
235
458
|
|
|
236
|
-
### Custom Presets
|
|
459
|
+
### β‘ Custom Presets (Power Feature)
|
|
460
|
+
|
|
461
|
+
**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.
|
|
462
|
+
|
|
463
|
+
#### Simple Pattern (Backward Compatible)
|
|
237
464
|
|
|
238
465
|
```typescript
|
|
239
466
|
customPresets: PresetConfig[] = [
|
|
@@ -244,6 +471,132 @@ customPresets: PresetConfig[] = [
|
|
|
244
471
|
];
|
|
245
472
|
```
|
|
246
473
|
|
|
474
|
+
#### **NEW v2.6.0** - Flexible Pattern with `getValue()` π₯
|
|
475
|
+
|
|
476
|
+
The real power comes with the `getValue()` pattern. Define **any custom logic** you need:
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
import { PresetConfig } from '@oneluiz/dual-datepicker';
|
|
480
|
+
|
|
481
|
+
customPresets: PresetConfig[] = [
|
|
482
|
+
{
|
|
483
|
+
label: 'Today',
|
|
484
|
+
getValue: () => {
|
|
485
|
+
const today = new Date();
|
|
486
|
+
return {
|
|
487
|
+
start: formatDate(today),
|
|
488
|
+
end: formatDate(today)
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
label: 'This Month',
|
|
494
|
+
getValue: () => {
|
|
495
|
+
const today = new Date();
|
|
496
|
+
const start = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
497
|
+
const end = new Date(today.getFullYear(), today.getMonth() + 1, 0);
|
|
498
|
+
return {
|
|
499
|
+
start: formatDate(start),
|
|
500
|
+
end: formatDate(end)
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
label: 'Last Month',
|
|
506
|
+
getValue: () => {
|
|
507
|
+
const today = new Date();
|
|
508
|
+
const start = new Date(today.getFullYear(), today.getMonth() - 1, 1);
|
|
509
|
+
const end = new Date(today.getFullYear(), today.getMonth(), 0);
|
|
510
|
+
return {
|
|
511
|
+
start: formatDate(start),
|
|
512
|
+
end: formatDate(end)
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
label: 'Quarter to Date',
|
|
518
|
+
getValue: () => {
|
|
519
|
+
const today = new Date();
|
|
520
|
+
const currentMonth = today.getMonth();
|
|
521
|
+
const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
|
|
522
|
+
const start = new Date(today.getFullYear(), quarterStartMonth, 1);
|
|
523
|
+
return {
|
|
524
|
+
start: formatDate(start),
|
|
525
|
+
end: formatDate(today)
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
];
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
#### **Even Better** - Use Pre-built Utilities π
|
|
533
|
+
|
|
534
|
+
We provide **ready-to-use preset utilities** for common scenarios:
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
import { CommonPresets } from '@oneluiz/dual-datepicker';
|
|
538
|
+
|
|
539
|
+
// Dashboard presets
|
|
540
|
+
presets = CommonPresets.dashboard;
|
|
541
|
+
// β Today, Yesterday, Last 7 days, Last 30 days, This month, Last month
|
|
542
|
+
|
|
543
|
+
// Reporting presets
|
|
544
|
+
presets = CommonPresets.reporting;
|
|
545
|
+
// β Today, This week, Last week, This month, Last month, This quarter, Last quarter
|
|
546
|
+
|
|
547
|
+
// Financial/ERP presets
|
|
548
|
+
presets = CommonPresets.financial;
|
|
549
|
+
// β Month to date, Quarter to date, Year to date, Last month, Last quarter, Last year
|
|
550
|
+
|
|
551
|
+
// Analytics/BI presets
|
|
552
|
+
presets = CommonPresets.analytics;
|
|
553
|
+
// β Last 7/14/30/60/90/180/365 days
|
|
554
|
+
|
|
555
|
+
// Simple presets
|
|
556
|
+
presets = CommonPresets.simple;
|
|
557
|
+
// β Today, Last 7 days, Last 30 days, This year
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
#### Create Your Own Utilities
|
|
561
|
+
|
|
562
|
+
Import individual utilities and mix them:
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
import {
|
|
566
|
+
getToday,
|
|
567
|
+
getThisMonth,
|
|
568
|
+
getLastMonth,
|
|
569
|
+
getQuarterToDate,
|
|
570
|
+
getYearToDate,
|
|
571
|
+
PresetConfig
|
|
572
|
+
} from '@oneluiz/dual-datepicker';
|
|
573
|
+
|
|
574
|
+
customPresets: PresetConfig[] = [
|
|
575
|
+
{ label: 'Today', getValue: getToday },
|
|
576
|
+
{ label: 'This Month', getValue: getThisMonth },
|
|
577
|
+
{ label: 'Last Month', getValue: getLastMonth },
|
|
578
|
+
{ label: 'Quarter to Date', getValue: getQuarterToDate },
|
|
579
|
+
{ label: 'Year to Date', getValue: getYearToDate },
|
|
580
|
+
{
|
|
581
|
+
label: 'Custom Logic',
|
|
582
|
+
getValue: () => {
|
|
583
|
+
// Your custom date calculation
|
|
584
|
+
return { start: '2026-01-01', end: '2026-12-31' };
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
];
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
#### Why This Is Powerful
|
|
591
|
+
|
|
592
|
+
β
**Perfect for Dashboards** - "Last 7 days", "Month to date", "Quarter to date"
|
|
593
|
+
β
**Perfect for Reporting** - "This week", "Last week", "This quarter"
|
|
594
|
+
β
**Perfect for Financial Systems** - "Quarter to date", "Year to date", "Fiscal year"
|
|
595
|
+
β
**Perfect for Analytics** - Consistent date ranges for BI tools
|
|
596
|
+
β
**Perfect for ERP** - Custom business logic and fiscal calendars
|
|
597
|
+
|
|
598
|
+
**Angular Material doesn't offer this level of flexibility!** π―
|
|
599
|
+
|
|
247
600
|
```html
|
|
248
601
|
<ngx-dual-datepicker
|
|
249
602
|
[(ngModel)]="dateRange"
|
|
@@ -311,14 +664,23 @@ export class MyComponent {
|
|
|
311
664
|
### Types
|
|
312
665
|
|
|
313
666
|
```typescript
|
|
314
|
-
|
|
667
|
+
interface DateRange {
|
|
668
|
+
fechaInicio: string; // ISO date format: 'YYYY-MM-DD'
|
|
315
669
|
fechaFin: string; // ISO date format: 'YYYY-MM-DD'
|
|
316
670
|
rangoTexto: string; // Display text: 'DD Mon - DD Mon'
|
|
317
671
|
}
|
|
318
672
|
|
|
673
|
+
interface PresetRange {
|
|
674
|
+
start: string; // ISO date format: 'YYYY-MM-DD'
|
|
675
|
+
end: string; // ISO date format: 'YYYY-MM-DD'
|
|
676
|
+
}
|
|
677
|
+
|
|
319
678
|
interface PresetConfig {
|
|
320
679
|
label: string;
|
|
321
|
-
|
|
680
|
+
/** @deprecated Use getValue() instead for more flexibility */
|
|
681
|
+
daysAgo?: number;
|
|
682
|
+
/** NEW v2.6.0 - Function that returns date range with custom logic */
|
|
683
|
+
getValue?: () => PresetRange;
|
|
322
684
|
}
|
|
323
685
|
|
|
324
686
|
interface LocaleConfig {
|
|
@@ -327,7 +689,6 @@ interface LocaleConfig {
|
|
|
327
689
|
dayNames?: string[]; // Full day names (7 items, starting Sunday)
|
|
328
690
|
dayNamesShort?: string[]; // Short day names (7 items, starting Sunday)
|
|
329
691
|
firstDayOfWeek?: number; // 0 = Sunday, 1 = Monday, etc. (not yet implemented)
|
|
330
|
-
daysAgo: number;
|
|
331
692
|
}
|
|
332
693
|
```
|
|
333
694
|
|
|
@@ -455,6 +816,25 @@ export class ExampleComponent {
|
|
|
455
816
|
- Angular 19.0.0 or higher
|
|
456
817
|
- Angular 20.0.0 or higher
|
|
457
818
|
|
|
819
|
+
## πΊοΈ Roadmap
|
|
820
|
+
|
|
821
|
+
Recently shipped:
|
|
822
|
+
|
|
823
|
+
**v2.6.0:**
|
|
824
|
+
- β
**Flexible Preset System** - `getValue()` pattern for custom date logic (This month, Last month, Quarter to date, etc.)
|
|
825
|
+
- β
**Pre-built Preset Utilities** - CommonPresets for Dashboard, Reporting, Financial, Analytics
|
|
826
|
+
- β
**Real Differentiator** - Perfect for ERP, BI, POS, and Reporting systems
|
|
827
|
+
|
|
828
|
+
**v2.5.0:**
|
|
829
|
+
- β
**Date Adapter System** - Support for DayJS, date-fns, Luxon, and custom date libraries
|
|
830
|
+
|
|
831
|
+
Planned features and improvements:
|
|
832
|
+
|
|
833
|
+
- β¬ **Complete keyboard navigation** - Arrow keys, Enter/Space, Tab, Escape
|
|
834
|
+
- β¬ **Full accessibility audit** - WCAG 2.1 AA compliance
|
|
835
|
+
- β¬ **Multi-range support** - Select multiple date ranges
|
|
836
|
+
- β¬ **Theming system** - Pre-built theme presets
|
|
837
|
+
|
|
458
838
|
## π License
|
|
459
839
|
|
|
460
840
|
MIT Β© Luis Cortes
|