@oneluiz/dual-datepicker 3.3.0 → 3.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 +220 -7
- package/core/dual-date-range.store.d.ts +104 -0
- package/core/index.d.ts +7 -0
- package/core/preset.engine.d.ts +54 -0
- package/core/range.validator.d.ts +37 -0
- package/dual-datepicker.component.d.ts +29 -1
- package/esm2022/core/dual-date-range.store.mjs +293 -0
- package/esm2022/core/index.mjs +8 -0
- package/esm2022/core/preset.engine.mjs +209 -0
- package/esm2022/core/range.validator.mjs +105 -0
- package/esm2022/dual-datepicker.component.mjs +194 -9
- package/esm2022/public-api.mjs +5 -2
- package/fesm2022/oneluiz-dual-datepicker.mjs +801 -9
- package/fesm2022/oneluiz-dual-datepicker.mjs.map +1 -1
- package/package.json +2 -2
- package/public-api.d.ts +1 -0
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
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
|
+
> **🆕 NEW in v3.5.0**: [**Headless Architecture**](HEADLESS.md) - Use date range state WITHOUT the UI component. Perfect for SSR, services, and global dashboard filters! 🎯
|
|
6
|
+
|
|
5
7
|
[](https://www.npmjs.com/package/@oneluiz/dual-datepicker)
|
|
6
8
|
[](https://www.npmjs.com/package/@oneluiz/dual-datepicker)
|
|
7
9
|

|
|
@@ -17,6 +19,35 @@ npm install @oneluiz/dual-datepicker
|
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
|
22
|
+
## 🌟 What's New
|
|
23
|
+
|
|
24
|
+
### Headless Architecture (v3.5.0)
|
|
25
|
+
|
|
26
|
+
Use date range logic **without the UI component**:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// Inject the store anywhere - no UI needed!
|
|
30
|
+
const rangeStore = inject(DualDateRangeStore);
|
|
31
|
+
|
|
32
|
+
// Apply preset
|
|
33
|
+
rangeStore.applyPreset('THIS_MONTH');
|
|
34
|
+
|
|
35
|
+
// Use in API calls
|
|
36
|
+
const range = rangeStore.range();
|
|
37
|
+
http.get(`/api/sales?start=${range.start}&end=${range.end}`);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Perfect for:**
|
|
41
|
+
- 📊 Dashboard filters (control multiple charts)
|
|
42
|
+
- 🏢 SSR applications
|
|
43
|
+
- 🔄 Global state management
|
|
44
|
+
- 🎯 Service-layer filtering
|
|
45
|
+
- 📈 Analytics and BI tools
|
|
46
|
+
|
|
47
|
+
**[📖 Read the Headless Architecture Guide →](HEADLESS.md)**
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
20
51
|
## 📋 Table of Contents
|
|
21
52
|
|
|
22
53
|
- [Features](#-features)
|
|
@@ -26,11 +57,13 @@ npm install @oneluiz/dual-datepicker
|
|
|
26
57
|
- [Basic Usage](#basic-usage)
|
|
27
58
|
- [Reactive Forms](#with-reactive-forms)
|
|
28
59
|
- [Angular Signals](#with-angular-signals)
|
|
60
|
+
- [Headless Usage](#headless-usage-new) ⭐ NEW
|
|
29
61
|
- [Advanced Features](#-advanced-features)
|
|
30
62
|
- [Multi-Range Selection](#multi-range-support)
|
|
31
63
|
- [Disabled Dates](#disabled-dates)
|
|
32
64
|
- [Display Format](#display-format)
|
|
33
65
|
- [Apply/Confirm Button](#applyconfirm-button)
|
|
66
|
+
- [Time Picker](#time-picker)
|
|
34
67
|
- [Hover Range Preview](#hover-range-preview)
|
|
35
68
|
- [Custom Presets](#custom-presets)
|
|
36
69
|
- [Date Adapter System](#date-adapter-system)
|
|
@@ -168,6 +201,52 @@ dateRange = signal<DateRange | null>(null);
|
|
|
168
201
|
</ngx-dual-datepicker>
|
|
169
202
|
```
|
|
170
203
|
|
|
204
|
+
### Headless Usage (NEW) ⭐
|
|
205
|
+
|
|
206
|
+
**Use date range state WITHOUT the UI component** - perfect for SSR, services, and global filters!
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { Component, inject } from '@angular/core';
|
|
210
|
+
import { DualDateRangeStore } from '@oneluiz/dual-datepicker';
|
|
211
|
+
import { HttpClient } from '@angular/common/http';
|
|
212
|
+
|
|
213
|
+
@Component({
|
|
214
|
+
template: `
|
|
215
|
+
<div class="dashboard">
|
|
216
|
+
<button (click)="setPreset('TODAY')">Today</button>
|
|
217
|
+
<button (click)="setPreset('THIS_MONTH')">This Month</button>
|
|
218
|
+
<p>{{ rangeText() }}</p>
|
|
219
|
+
</div>
|
|
220
|
+
`
|
|
221
|
+
})
|
|
222
|
+
export class DashboardComponent {
|
|
223
|
+
private rangeStore = inject(DualDateRangeStore);
|
|
224
|
+
private http = inject(HttpClient);
|
|
225
|
+
|
|
226
|
+
// Expose signals for template
|
|
227
|
+
rangeText = this.rangeStore.rangeText;
|
|
228
|
+
|
|
229
|
+
setPreset(key: string) {
|
|
230
|
+
this.rangeStore.applyPreset(key);
|
|
231
|
+
|
|
232
|
+
// Use in API call
|
|
233
|
+
const range = this.rangeStore.range();
|
|
234
|
+
this.http.get(`/api/sales`, {
|
|
235
|
+
params: { start: range.start, end: range.end }
|
|
236
|
+
}).subscribe(data => console.log(data));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Benefits:**
|
|
242
|
+
- ✅ No UI component needed
|
|
243
|
+
- ✅ SSR-compatible
|
|
244
|
+
- ✅ Global state management
|
|
245
|
+
- ✅ Perfect for services and guards
|
|
246
|
+
- ✅ Testable and deterministic
|
|
247
|
+
|
|
248
|
+
**[📖 Full Headless Architecture Guide →](HEADLESS.md)** | **[💻 Code Examples →](HEADLESS_EXAMPLES.ts)**
|
|
249
|
+
|
|
171
250
|
---
|
|
172
251
|
|
|
173
252
|
## 🎯 Advanced Features
|
|
@@ -470,6 +549,109 @@ export class DashboardExample {
|
|
|
470
549
|
| User control | Automatic | Explicit confirmation |
|
|
471
550
|
| Best for | Simple forms | Dashboards, reports |
|
|
472
551
|
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
### Time Picker
|
|
555
|
+
|
|
556
|
+
**Select precise datetime ranges with optional time picker.** Choose exact start and end times in addition to dates, with support for 12h/24h formats and configurable minute steps.
|
|
557
|
+
|
|
558
|
+
#### Basic Usage
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
import { Component } from '@angular/core';
|
|
562
|
+
import { DateRange } from '@oneluiz/dual-datepicker';
|
|
563
|
+
|
|
564
|
+
@Component({
|
|
565
|
+
template: `
|
|
566
|
+
<ngx-dual-datepicker
|
|
567
|
+
[enableTimePicker]="true"
|
|
568
|
+
(dateRangeChange)="onDateRangeChange($event)">
|
|
569
|
+
</ngx-dual-datepicker>
|
|
570
|
+
`
|
|
571
|
+
})
|
|
572
|
+
export class AppointmentComponent {
|
|
573
|
+
onDateRangeChange(range: DateRange) {
|
|
574
|
+
console.log('Start:', range.startDate, 'at', range.startTime);
|
|
575
|
+
console.log('End:', range.endDate, 'at', range.endTime);
|
|
576
|
+
// startTime and endTime are in 'HH:mm' format
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
#### Configuration Options
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// 12-hour format with AM/PM
|
|
585
|
+
<ngx-dual-datepicker
|
|
586
|
+
[enableTimePicker]="true"
|
|
587
|
+
timeFormat="12h"
|
|
588
|
+
defaultStartTime="09:00"
|
|
589
|
+
defaultEndTime="17:00"
|
|
590
|
+
(dateRangeChange)="onDateRangeChange($event)">
|
|
591
|
+
</ngx-dual-datepicker>
|
|
592
|
+
|
|
593
|
+
// 30-minute intervals
|
|
594
|
+
<ngx-dual-datepicker
|
|
595
|
+
[enableTimePicker]="true"
|
|
596
|
+
[minuteStep]="30"
|
|
597
|
+
(dateRangeChange)="onDateRangeChange($event)">
|
|
598
|
+
</ngx-dual-datepicker>
|
|
599
|
+
|
|
600
|
+
// With Apply button for controlled changes
|
|
601
|
+
<ngx-dual-datepicker
|
|
602
|
+
[enableTimePicker]="true"
|
|
603
|
+
[requireApply]="true"
|
|
604
|
+
(dateRangeChange)="onDateRangeChange($event)">
|
|
605
|
+
</ngx-dual-datepicker>
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
#### DateRange with Time
|
|
609
|
+
|
|
610
|
+
When `enableTimePicker` is true, the DateRange includes optional time properties:
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
interface DateRange {
|
|
614
|
+
startDate: string; // 'YYYY-MM-DD'
|
|
615
|
+
endDate: string; // 'YYYY-MM-DD'
|
|
616
|
+
rangeText: string; // Display text
|
|
617
|
+
startTime?: string; // 'HH:mm' or 'HH:mm AM/PM'
|
|
618
|
+
endTime?: string; // 'HH:mm' or 'HH:mm AM/PM'
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
#### Configuration Inputs
|
|
623
|
+
|
|
624
|
+
| Input | Type | Default | Description |
|
|
625
|
+
|-------|------|---------|-------------|
|
|
626
|
+
| `enableTimePicker` | boolean | `false` | Enable time selection |
|
|
627
|
+
| `timeFormat` | `'12h'` \| `'24h'` | `'24h'` | Time display format |
|
|
628
|
+
| `minuteStep` | number | `15` | Minute intervals (1, 5, 15, or 30) |
|
|
629
|
+
| `defaultStartTime` | string | `'00:00'` | Default start time (HH:mm) |
|
|
630
|
+
| `defaultEndTime` | string | `'23:59'` | Default end time (HH:mm) |
|
|
631
|
+
|
|
632
|
+
#### Perfect Use Cases
|
|
633
|
+
|
|
634
|
+
- 📅 **Appointment booking systems** - Schedule meetings with exact times
|
|
635
|
+
- 🎫 **Event scheduling** - Create events with start and end times
|
|
636
|
+
- 🏢 **Meeting planners** - Book conference rooms with time slots
|
|
637
|
+
- 📊 **Time-based reporting** - Generate reports for specific time ranges
|
|
638
|
+
- 👥 **Shift management** - Assign work shifts with precise times
|
|
639
|
+
- 🎬 **Reservation systems** - Book resources with time constraints
|
|
640
|
+
|
|
641
|
+
#### Key Features
|
|
642
|
+
|
|
643
|
+
- ✅ Optional - disabled by default (backward compatible)
|
|
644
|
+
- ✅ 12h (AM/PM) or 24h time format
|
|
645
|
+
- ✅ Configurable minute steps (1, 5, 15, 30)
|
|
646
|
+
- ✅ Default start/end times support
|
|
647
|
+
- ✅ Works with all themes
|
|
648
|
+
- ✅ Integrates with requireApply mode
|
|
649
|
+
- ✅ Fully accessible with keyboard navigation
|
|
650
|
+
|
|
651
|
+
**For complete documentation, see [TIME_PICKER.md](TIME_PICKER.md)**
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
|
|
473
655
|
### Hover Range Preview
|
|
474
656
|
|
|
475
657
|
**Automatic visual feedback while selecting dates.** Provides instant visual preview of the date range when hovering over dates before clicking to confirm.
|
|
@@ -759,6 +941,12 @@ spanishLocale: LocaleConfig = {
|
|
|
759
941
|
| `disabledDates` | `Date[] \| ((date: Date) => boolean)` | `undefined` | Array of dates or function to disable specific dates |
|
|
760
942
|
| `displayFormat` | `string` | `'D MMM'` | Format for displaying dates in input (tokens: YYYY, YY, MMMM, MMM, MM, M, DD, D) |
|
|
761
943
|
| `requireApply` | `boolean` | `false` | Require Apply button confirmation before emitting changes |
|
|
944
|
+
| `enableTimePicker` | `boolean` | `false` | Enable time selection |
|
|
945
|
+
| `timeFormat` | `'12h' \| '24h'` | `'24h'` | Time display format (12-hour with AM/PM or 24-hour) |
|
|
946
|
+
| `minuteStep` | `number` | `15` | Step for minute selector (1, 5, 15, or 30) |
|
|
947
|
+
| `defaultStartTime` | `string` | `'00:00'` | Default start time in HH:mm format |
|
|
948
|
+
| `defaultEndTime` | `string` | `'23:59'` | Default end time in HH:mm format |
|
|
949
|
+
| `theme` | `ThemeType` | `'default'` | Theme preset: 'default', 'bootstrap', 'bulma', 'foundation', 'tailwind', 'custom' |
|
|
762
950
|
| `enableKeyboardNavigation` | `boolean` | `true` | Enable keyboard navigation |
|
|
763
951
|
| `inputBackgroundColor` | `string` | `'#fff'` | Input background color |
|
|
764
952
|
| `inputTextColor` | `string` | `'#495057'` | Input text color |
|
|
@@ -809,6 +997,8 @@ interface DateRange {
|
|
|
809
997
|
startDate: string; // ISO format: 'YYYY-MM-DD'
|
|
810
998
|
endDate: string; // ISO format: 'YYYY-MM-DD'
|
|
811
999
|
rangeText: string; // Display text: 'DD Mon - DD Mon'
|
|
1000
|
+
startTime?: string; // Optional: 'HH:mm' or 'HH:mm AM/PM' (when enableTimePicker=true)
|
|
1001
|
+
endTime?: string; // Optional: 'HH:mm' or 'HH:mm AM/PM' (when enableTimePicker=true)
|
|
812
1002
|
}
|
|
813
1003
|
|
|
814
1004
|
interface MultiDateRange {
|
|
@@ -1886,20 +2076,43 @@ export class ExampleComponent {
|
|
|
1886
2076
|
|
|
1887
2077
|
Recently shipped:
|
|
1888
2078
|
|
|
2079
|
+
**v3.4.0:**
|
|
2080
|
+
- ✅ **Time Picker** - Select precise datetime ranges with 12h/24h format
|
|
2081
|
+
- ✅ **Configurable Minute Steps** - Choose 1, 5, 15, or 30-minute intervals
|
|
2082
|
+
- ✅ **Default Times** - Set default start/end times
|
|
2083
|
+
- ✅ **Full Theme Support** - Works seamlessly with all built-in themes
|
|
2084
|
+
|
|
2085
|
+
**v3.3.0:**
|
|
2086
|
+
- ✅ **Theming System** - Pre-built themes for Bootstrap, Bulma, Foundation, Tailwind CSS, and Custom
|
|
2087
|
+
- ✅ **CSS Variables Support** - 13 customizable variables for branding
|
|
2088
|
+
- ✅ **Framework Integration** - Match your existing design system seamlessly
|
|
2089
|
+
|
|
2090
|
+
**v3.2.0:**
|
|
2091
|
+
- ✅ **Hover Range Preview** - Visual feedback before confirming selection
|
|
2092
|
+
- ✅ **Apply/Confirm Button** - Require confirmation for enterprise dashboards
|
|
2093
|
+
- ✅ **Display Format** - Customize date display (DD/MM/YYYY, MM/DD/YYYY, etc.)
|
|
2094
|
+
- ✅ **Disabled Dates** - Block weekends, holidays, or custom logic
|
|
2095
|
+
|
|
2096
|
+
**v3.1.0:**
|
|
2097
|
+
- ✅ **Complete Keyboard Navigation** - Arrow keys, Enter/Space, Tab, Escape, Home/End, PageUp/Down
|
|
2098
|
+
- ✅ **Full Accessibility Audit** - WCAG 2.1 Level AA compliance
|
|
2099
|
+
|
|
2100
|
+
**v2.7.0:**
|
|
2101
|
+
- ✅ **Multi-range Support** - Select UNLIMITED date ranges (Material CAN'T do this!)
|
|
2102
|
+
|
|
1889
2103
|
**v2.6.0:**
|
|
1890
|
-
- ✅ **Flexible Preset System** - `getValue()` pattern for custom date logic
|
|
2104
|
+
- ✅ **Flexible Preset System** - `getValue()` pattern for custom date logic
|
|
1891
2105
|
- ✅ **Pre-built Preset Utilities** - CommonPresets for Dashboard, Reporting, Financial, Analytics
|
|
1892
|
-
- ✅ **Real Differentiator** - Perfect for ERP, BI, POS, and Reporting systems
|
|
1893
2106
|
|
|
1894
2107
|
**v2.5.0:**
|
|
1895
2108
|
- ✅ **Date Adapter System** - Support for DayJS, date-fns, Luxon, and custom date libraries
|
|
1896
2109
|
|
|
1897
|
-
Planned features
|
|
2110
|
+
Planned features:
|
|
1898
2111
|
|
|
1899
|
-
- ⬜ **
|
|
1900
|
-
- ⬜ **
|
|
1901
|
-
- ⬜ **
|
|
1902
|
-
- ⬜ **
|
|
2112
|
+
- ⬜ **Mobile Optimizations** - Enhanced touch gestures and responsive layout
|
|
2113
|
+
- ⬜ **Range Shortcuts** - Quick selection buttons (Today, This Week, etc.)
|
|
2114
|
+
- ⬜ **Time Constraints** - Min/max time validation and business hours
|
|
2115
|
+
- ⬜ **Multi-range + Time Picker** - Combined support for multiple datetime ranges
|
|
1903
2116
|
|
|
1904
2117
|
## 📄 License
|
|
1905
2118
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export interface DateRangeState {
|
|
3
|
+
start: string;
|
|
4
|
+
end: string;
|
|
5
|
+
startTime?: string;
|
|
6
|
+
endTime?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DateRangeConfig {
|
|
9
|
+
minDate?: Date;
|
|
10
|
+
maxDate?: Date;
|
|
11
|
+
disabledDates?: Date[] | ((date: Date) => boolean);
|
|
12
|
+
enableTimePicker?: boolean;
|
|
13
|
+
defaultStartTime?: string;
|
|
14
|
+
defaultEndTime?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class DualDateRangeStore {
|
|
17
|
+
private config;
|
|
18
|
+
private _startDate;
|
|
19
|
+
private _endDate;
|
|
20
|
+
private _leftMonth;
|
|
21
|
+
private _rightMonth;
|
|
22
|
+
private _selectingStart;
|
|
23
|
+
private _startTime;
|
|
24
|
+
private _endTime;
|
|
25
|
+
private _pendingStart;
|
|
26
|
+
private _pendingEnd;
|
|
27
|
+
private _hasPendingChanges;
|
|
28
|
+
readonly startDate: import("@angular/core").Signal<Date>;
|
|
29
|
+
readonly endDate: import("@angular/core").Signal<Date>;
|
|
30
|
+
readonly leftMonth: import("@angular/core").Signal<Date>;
|
|
31
|
+
readonly rightMonth: import("@angular/core").Signal<Date>;
|
|
32
|
+
readonly selectingStart: import("@angular/core").Signal<boolean>;
|
|
33
|
+
readonly startTime: import("@angular/core").Signal<string>;
|
|
34
|
+
readonly endTime: import("@angular/core").Signal<string>;
|
|
35
|
+
readonly hasPendingChanges: import("@angular/core").Signal<boolean>;
|
|
36
|
+
readonly range: import("@angular/core").Signal<DateRangeState>;
|
|
37
|
+
readonly isValid: import("@angular/core").Signal<boolean>;
|
|
38
|
+
readonly rangeText: import("@angular/core").Signal<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Configure the store
|
|
41
|
+
*/
|
|
42
|
+
configure(config: Partial<DateRangeConfig>): void;
|
|
43
|
+
/**
|
|
44
|
+
* Set start date (with validation)
|
|
45
|
+
*/
|
|
46
|
+
setStart(date: Date | string | null): void;
|
|
47
|
+
/**
|
|
48
|
+
* Set end date (with validation)
|
|
49
|
+
*/
|
|
50
|
+
setEnd(date: Date | string | null): void;
|
|
51
|
+
/**
|
|
52
|
+
* Set complete range at once
|
|
53
|
+
*/
|
|
54
|
+
setRange(start: Date | string | null, end: Date | string | null): void;
|
|
55
|
+
/**
|
|
56
|
+
* Set pending selection (for requireApply mode)
|
|
57
|
+
*/
|
|
58
|
+
setPendingStart(date: Date | string | null): void;
|
|
59
|
+
setPendingEnd(date: Date | string | null): void;
|
|
60
|
+
/**
|
|
61
|
+
* Apply pending changes
|
|
62
|
+
*/
|
|
63
|
+
applyPending(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Cancel pending changes
|
|
66
|
+
*/
|
|
67
|
+
cancelPending(): void;
|
|
68
|
+
private clearPending;
|
|
69
|
+
/**
|
|
70
|
+
* Reset to empty state
|
|
71
|
+
*/
|
|
72
|
+
reset(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Apply a preset by key
|
|
75
|
+
*/
|
|
76
|
+
applyPreset(presetKey: string, now?: Date): void;
|
|
77
|
+
/**
|
|
78
|
+
* Navigate left calendar month
|
|
79
|
+
*/
|
|
80
|
+
setLeftMonth(date: Date): void;
|
|
81
|
+
/**
|
|
82
|
+
* Navigate right calendar month
|
|
83
|
+
*/
|
|
84
|
+
setRightMonth(date: Date): void;
|
|
85
|
+
/**
|
|
86
|
+
* Set time values
|
|
87
|
+
*/
|
|
88
|
+
setStartTime(time: string): void;
|
|
89
|
+
setEndTime(time: string): void;
|
|
90
|
+
/**
|
|
91
|
+
* Get current state as snapshot
|
|
92
|
+
*/
|
|
93
|
+
getSnapshot(): DateRangeState;
|
|
94
|
+
/**
|
|
95
|
+
* Load state from snapshot
|
|
96
|
+
*/
|
|
97
|
+
loadSnapshot(snapshot: DateRangeState): void;
|
|
98
|
+
private parseDate;
|
|
99
|
+
private getNextMonth;
|
|
100
|
+
private getPreviousMonth;
|
|
101
|
+
private formatDateShort;
|
|
102
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DualDateRangeStore, never>;
|
|
103
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<DualDateRangeStore>;
|
|
104
|
+
}
|
package/core/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless Preset Engine
|
|
3
|
+
* Pure functions that resolve date ranges WITHOUT render dependency
|
|
4
|
+
* Perfect for SSR, global state, dashboard filters
|
|
5
|
+
*/
|
|
6
|
+
export interface RangePreset {
|
|
7
|
+
/**
|
|
8
|
+
* Resolve preset to actual date range
|
|
9
|
+
* @param now - Current date for deterministic calculation
|
|
10
|
+
*/
|
|
11
|
+
resolve(now: Date): {
|
|
12
|
+
start: Date;
|
|
13
|
+
end: Date;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface PresetRange {
|
|
17
|
+
start: string;
|
|
18
|
+
end: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Registry of built-in presets
|
|
22
|
+
* Can be extended by consumers
|
|
23
|
+
*/
|
|
24
|
+
export declare class PresetEngine {
|
|
25
|
+
private presets;
|
|
26
|
+
constructor();
|
|
27
|
+
/**
|
|
28
|
+
* Register a custom preset
|
|
29
|
+
*/
|
|
30
|
+
register(key: string, preset: RangePreset): void;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a preset to date range
|
|
33
|
+
*/
|
|
34
|
+
resolve(key: string, now?: Date): PresetRange | null;
|
|
35
|
+
/**
|
|
36
|
+
* Get all available preset keys
|
|
37
|
+
*/
|
|
38
|
+
getPresetKeys(): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Register all built-in presets
|
|
41
|
+
*/
|
|
42
|
+
private registerBuiltInPresets;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a custom preset from a function
|
|
46
|
+
*/
|
|
47
|
+
export declare function createPreset(resolver: (now: Date) => {
|
|
48
|
+
start: Date;
|
|
49
|
+
end: Date;
|
|
50
|
+
}): RangePreset;
|
|
51
|
+
/**
|
|
52
|
+
* Singleton preset engine instance
|
|
53
|
+
*/
|
|
54
|
+
export declare const presetEngine: PresetEngine;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure validation functions for date ranges
|
|
3
|
+
* No dependencies, no side effects - just logic
|
|
4
|
+
* Perfect for SSR, testing, and reusability
|
|
5
|
+
*/
|
|
6
|
+
export interface ValidationResult {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate that end date is not before start date
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateRangeOrder(start: Date | null, end: Date | null): ValidationResult;
|
|
14
|
+
/**
|
|
15
|
+
* Validate that date is within min/max bounds
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateDateBounds(date: Date | null, minDate?: Date, maxDate?: Date): ValidationResult;
|
|
18
|
+
/**
|
|
19
|
+
* Validate that a range is within bounds
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateRangeBounds(start: Date | null, end: Date | null, minDate?: Date, maxDate?: Date): ValidationResult;
|
|
22
|
+
/**
|
|
23
|
+
* Check if a date is disabled
|
|
24
|
+
*/
|
|
25
|
+
export declare function isDateDisabled(date: Date, disabledDates?: Date[] | ((date: Date) => boolean)): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Apply bounds to a date (clamp it)
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyBounds(date: Date, minDate?: Date, maxDate?: Date): Date;
|
|
30
|
+
/**
|
|
31
|
+
* Parse ISO date string to Date object (deterministic)
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseISODate(dateStr: string): Date | null;
|
|
34
|
+
/**
|
|
35
|
+
* Format Date to ISO string (YYYY-MM-DD)
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatISODate(date: Date | null): string;
|
|
@@ -5,6 +5,8 @@ export interface DateRange {
|
|
|
5
5
|
startDate: string;
|
|
6
6
|
endDate: string;
|
|
7
7
|
rangeText: string;
|
|
8
|
+
startTime?: string;
|
|
9
|
+
endTime?: string;
|
|
8
10
|
}
|
|
9
11
|
export interface MultiDateRange {
|
|
10
12
|
ranges: DateRange[];
|
|
@@ -49,6 +51,11 @@ export declare class DualDatepickerComponent implements OnInit, OnChanges, Contr
|
|
|
49
51
|
disabledDates: Date[] | ((date: Date) => boolean) | undefined;
|
|
50
52
|
displayFormat: string;
|
|
51
53
|
requireApply: boolean;
|
|
54
|
+
enableTimePicker: boolean;
|
|
55
|
+
timeFormat: '12h' | '24h';
|
|
56
|
+
minuteStep: number;
|
|
57
|
+
defaultStartTime: string;
|
|
58
|
+
defaultEndTime: string;
|
|
52
59
|
dateRangeChange: EventEmitter<DateRange>;
|
|
53
60
|
dateRangeSelected: EventEmitter<DateRange>;
|
|
54
61
|
multiDateRangeChange: EventEmitter<MultiDateRange>;
|
|
@@ -65,6 +72,12 @@ export declare class DualDatepickerComponent implements OnInit, OnChanges, Contr
|
|
|
65
72
|
pendingStartDate: string;
|
|
66
73
|
pendingEndDate: string;
|
|
67
74
|
hasPendingChanges: import("@angular/core").WritableSignal<boolean>;
|
|
75
|
+
startHour: number;
|
|
76
|
+
startMinute: number;
|
|
77
|
+
endHour: number;
|
|
78
|
+
endMinute: number;
|
|
79
|
+
showStartTimePicker: import("@angular/core").WritableSignal<boolean>;
|
|
80
|
+
showEndTimePicker: import("@angular/core").WritableSignal<boolean>;
|
|
68
81
|
hoverDate: import("@angular/core").WritableSignal<string>;
|
|
69
82
|
selectedRanges: import("@angular/core").WritableSignal<DateRange[]>;
|
|
70
83
|
currentRangeIndex: import("@angular/core").WritableSignal<number>;
|
|
@@ -123,10 +136,25 @@ export declare class DualDatepickerComponent implements OnInit, OnChanges, Contr
|
|
|
123
136
|
private emitMultiChange;
|
|
124
137
|
private emitMultiSelection;
|
|
125
138
|
private getDateRangeValue;
|
|
139
|
+
toggleStartTimePicker(): void;
|
|
140
|
+
toggleEndTimePicker(): void;
|
|
141
|
+
incrementStartHour(): void;
|
|
142
|
+
decrementStartHour(): void;
|
|
143
|
+
incrementStartMinute(): void;
|
|
144
|
+
decrementStartMinute(): void;
|
|
145
|
+
incrementEndHour(): void;
|
|
146
|
+
decrementEndHour(): void;
|
|
147
|
+
incrementEndMinute(): void;
|
|
148
|
+
decrementEndMinute(): void;
|
|
149
|
+
formatTime(hour: number, minute: number): string;
|
|
150
|
+
parseTime(timeString: string): {
|
|
151
|
+
hour: number;
|
|
152
|
+
minute: number;
|
|
153
|
+
};
|
|
126
154
|
writeValue(value: DateRange | null): void;
|
|
127
155
|
registerOnChange(fn: (value: DateRange | null) => void): void;
|
|
128
156
|
registerOnTouched(fn: () => void): void;
|
|
129
157
|
setDisabledState(isDisabled: boolean): void;
|
|
130
158
|
static ɵfac: i0.ɵɵFactoryDeclaration<DualDatepickerComponent, never>;
|
|
131
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<DualDatepickerComponent, "ngx-dual-datepicker", never, { "placeholder": { "alias": "placeholder"; "required": false; }; "startDate": { "alias": "startDate"; "required": false; }; "endDate": { "alias": "endDate"; "required": false; }; "showPresets": { "alias": "showPresets"; "required": false; }; "showClearButton": { "alias": "showClearButton"; "required": false; }; "multiRange": { "alias": "multiRange"; "required": false; }; "closeOnSelection": { "alias": "closeOnSelection"; "required": false; }; "closeOnPresetSelection": { "alias": "closeOnPresetSelection"; "required": false; }; "closeOnClickOutside": { "alias": "closeOnClickOutside"; "required": false; }; "enableKeyboardNavigation": { "alias": "enableKeyboardNavigation"; "required": false; }; "presets": { "alias": "presets"; "required": false; }; "theme": { "alias": "theme"; "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; }; "disabledDates": { "alias": "disabledDates"; "required": false; }; "displayFormat": { "alias": "displayFormat"; "required": false; }; "requireApply": { "alias": "requireApply"; "required": false; }; }, { "dateRangeChange": "dateRangeChange"; "dateRangeSelected": "dateRangeSelected"; "multiDateRangeChange": "multiDateRangeChange"; "multiDateRangeSelected": "multiDateRangeSelected"; }, never, never, true, never>;
|
|
159
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<DualDatepickerComponent, "ngx-dual-datepicker", never, { "placeholder": { "alias": "placeholder"; "required": false; }; "startDate": { "alias": "startDate"; "required": false; }; "endDate": { "alias": "endDate"; "required": false; }; "showPresets": { "alias": "showPresets"; "required": false; }; "showClearButton": { "alias": "showClearButton"; "required": false; }; "multiRange": { "alias": "multiRange"; "required": false; }; "closeOnSelection": { "alias": "closeOnSelection"; "required": false; }; "closeOnPresetSelection": { "alias": "closeOnPresetSelection"; "required": false; }; "closeOnClickOutside": { "alias": "closeOnClickOutside"; "required": false; }; "enableKeyboardNavigation": { "alias": "enableKeyboardNavigation"; "required": false; }; "presets": { "alias": "presets"; "required": false; }; "theme": { "alias": "theme"; "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; }; "disabledDates": { "alias": "disabledDates"; "required": false; }; "displayFormat": { "alias": "displayFormat"; "required": false; }; "requireApply": { "alias": "requireApply"; "required": false; }; "enableTimePicker": { "alias": "enableTimePicker"; "required": false; }; "timeFormat": { "alias": "timeFormat"; "required": false; }; "minuteStep": { "alias": "minuteStep"; "required": false; }; "defaultStartTime": { "alias": "defaultStartTime"; "required": false; }; "defaultEndTime": { "alias": "defaultEndTime"; "required": false; }; }, { "dateRangeChange": "dateRangeChange"; "dateRangeSelected": "dateRangeSelected"; "multiDateRangeChange": "multiDateRangeChange"; "multiDateRangeSelected": "multiDateRangeSelected"; }, never, never, true, never>;
|
|
132
160
|
}
|