@oneluiz/dual-datepicker 3.4.0 → 3.5.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/README.md CHANGED
@@ -2,6 +2,9 @@
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.1**: [**Timezone-Safe Date Adapter**](docs/TIMEZONE_ADAPTER.md) - Fixes enterprise-critical timezone bugs in ERP, BI, POS, and invoicing systems 🛡️
6
+ > **🆕 NEW in v3.5.0**: [**Headless Architecture**](docs/HEADLESS.md) - Use date range state WITHOUT the UI component. Perfect for SSR, services, and global dashboard filters! 🎯
7
+
5
8
  [![npm version](https://img.shields.io/npm/v/@oneluiz/dual-datepicker)](https://www.npmjs.com/package/@oneluiz/dual-datepicker)
6
9
  [![npm provenance](https://img.shields.io/badge/provenance-available-brightgreen)](https://www.npmjs.com/package/@oneluiz/dual-datepicker)
7
10
  ![license](https://img.shields.io/npm/l/@oneluiz/dual-datepicker)
@@ -17,6 +20,54 @@ npm install @oneluiz/dual-datepicker
17
20
 
18
21
  ---
19
22
 
23
+ ## 🌟 What's New
24
+
25
+ ### Timezone-Safe Date Adapter (v3.5.1)
26
+
27
+ **Fixed**: Enterprise timezone bugs that caused date ranges to shift by ±1 day.
28
+
29
+ ```typescript
30
+ // ✅ No more timezone shift bugs!
31
+ const store = inject(DualDateRangeStore);
32
+ store.applyPreset('THIS_MONTH');
33
+ const range = store.range(); // { start: "2024-03-01", end: "2024-03-31" }
34
+ // Always correct, even across timezones and DST transitions
35
+
36
+ // ✅ Optional: Use your preferred date library
37
+ providers: [
38
+ { provide: DATE_ADAPTER, useClass: LuxonDateAdapter }
39
+ ]
40
+ ```
41
+
42
+ **[📖 Read the Timezone Adapter Guide →](docs/TIMEZONE_ADAPTER.md)**
43
+
44
+ ### Headless Architecture (v3.5.0)
45
+
46
+ Use date range logic **without the UI component**:
47
+
48
+ ```typescript
49
+ // Inject the store anywhere - no UI needed!
50
+ const rangeStore = inject(DualDateRangeStore);
51
+
52
+ // Apply preset
53
+ rangeStore.applyPreset('THIS_MONTH');
54
+
55
+ // Use in API calls
56
+ const range = rangeStore.range();
57
+ http.get(`/api/sales?start=${range.start}&end=${range.end}`);
58
+ ```
59
+
60
+ **Perfect for:**
61
+ - 📊 Dashboard filters (control multiple charts)
62
+ - 🏢 SSR applications
63
+ - 🔄 Global state management
64
+ - 🎯 Service-layer filtering
65
+ - 📈 Analytics and BI tools
66
+
67
+ **[📖 Read the Headless Architecture Guide →](docs/HEADLESS.md)**
68
+
69
+ ---
70
+
20
71
  ## 📋 Table of Contents
21
72
 
22
73
  - [Features](#-features)
@@ -26,6 +77,7 @@ npm install @oneluiz/dual-datepicker
26
77
  - [Basic Usage](#basic-usage)
27
78
  - [Reactive Forms](#with-reactive-forms)
28
79
  - [Angular Signals](#with-angular-signals)
80
+ - [Headless Usage](#headless-usage-new) ⭐ NEW
29
81
  - [Advanced Features](#-advanced-features)
30
82
  - [Multi-Range Selection](#multi-range-support)
31
83
  - [Disabled Dates](#disabled-dates)
@@ -169,6 +221,80 @@ dateRange = signal<DateRange | null>(null);
169
221
  </ngx-dual-datepicker>
170
222
  ```
171
223
 
224
+ ### Headless Usage (NEW) ⭐
225
+
226
+ **Use date range state WITHOUT the UI component** - perfect for SSR, services, and global filters!
227
+
228
+ ```typescript
229
+ import { Component, inject } from '@angular/core';
230
+ import { DualDateRangeStore } from '@oneluiz/dual-datepicker';
231
+ import { HttpClient } from '@angular/common/http';
232
+
233
+ @Component({
234
+ template: `
235
+ <div class="dashboard">
236
+ <button (click)="setPreset('TODAY')">Today</button>
237
+ <button (click)="setPreset('THIS_MONTH')">This Month</button>
238
+ <p>{{ rangeText() }}</p>
239
+ </div>
240
+ `
241
+ })
242
+ export class DashboardComponent {
243
+ private rangeStore = inject(DualDateRangeStore);
244
+ private http = inject(HttpClient);
245
+
246
+ // Expose signals for template
247
+ rangeText = this.rangeStore.rangeText;
248
+
249
+ setPreset(key: string) {
250
+ this.rangeStore.applyPreset(key);
251
+
252
+ // Use in API call
253
+ const range = this.rangeStore.range();
254
+ this.http.get(`/api/sales`, {
255
+ params: { start: range.start, end: range.end }
256
+ }).subscribe(data => console.log(data));
257
+ }
258
+ }
259
+ ```
260
+
261
+ **Benefits:**
262
+ - ✅ No UI component needed
263
+ - ✅ SSR-compatible
264
+ - ✅ Global state management
265
+ - ✅ Perfect for services and guards
266
+ - ✅ Testable and deterministic
267
+
268
+ #### SSR-Safe Clock Injection
269
+
270
+ Presets like "Last 7 Days" now use **clock injection** for SSR hydration consistency:
271
+
272
+ ```typescript
273
+ // Server (SSR)
274
+ import { DATE_CLOCK } from '@oneluiz/dual-datepicker';
275
+
276
+ const requestTime = new Date();
277
+
278
+ renderApplication(AppComponent, {
279
+ providers: [
280
+ { provide: DATE_CLOCK, useValue: { now: () => requestTime } }
281
+ ]
282
+ });
283
+ ```
284
+
285
+ ```typescript
286
+ // Client (Browser)
287
+ bootstrapApplication(AppComponent, {
288
+ providers: [
289
+ { provide: DATE_CLOCK, useValue: { now: () => new Date(getServerTime()) } }
290
+ ]
291
+ });
292
+ ```
293
+
294
+ **Result**: Server and client resolve identical presets ✅ No hydration mismatch!
295
+
296
+ **[📖 Full Headless Architecture Guide →](HEADLESS.md)** | **[💻 Code Examples →](HEADLESS_EXAMPLES.ts)** | **[🚀 SSR Clock Injection Guide →](SSR_CLOCK_INJECTION.md)**
297
+
172
298
  ---
173
299
 
174
300
  ## 🎯 Advanced Features
@@ -1998,6 +2124,12 @@ export class ExampleComponent {
1998
2124
 
1999
2125
  Recently shipped:
2000
2126
 
2127
+ **v3.4.0:**
2128
+ - ✅ **Time Picker** - Select precise datetime ranges with 12h/24h format
2129
+ - ✅ **Configurable Minute Steps** - Choose 1, 5, 15, or 30-minute intervals
2130
+ - ✅ **Default Times** - Set default start/end times
2131
+ - ✅ **Full Theme Support** - Works seamlessly with all built-in themes
2132
+
2001
2133
  **v3.3.0:**
2002
2134
  - ✅ **Theming System** - Pre-built themes for Bootstrap, Bulma, Foundation, Tailwind CSS, and Custom
2003
2135
  - ✅ **CSS Variables Support** - 13 customizable variables for branding
@@ -2025,9 +2157,10 @@ Recently shipped:
2025
2157
 
2026
2158
  Planned features:
2027
2159
 
2028
- - ⬜ **Time Picker** - Select date + time ranges
2029
2160
  - ⬜ **Mobile Optimizations** - Enhanced touch gestures and responsive layout
2030
2161
  - ⬜ **Range Shortcuts** - Quick selection buttons (Today, This Week, etc.)
2162
+ - ⬜ **Time Constraints** - Min/max time validation and business hours
2163
+ - ⬜ **Multi-range + Time Picker** - Combined support for multiple datetime ranges
2031
2164
 
2032
2165
  ## 📄 License
2033
2166
 
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Date Adapter Abstraction for Timezone-Safe Date Operations
3
+ *
4
+ * Problem:
5
+ * Using Date natively in calendar/range logic causes enterprise bugs:
6
+ * - Date ranges shift by 1 day due to timezone/DST
7
+ * - Server (UTC) vs Client (local timezone) discrepancies
8
+ * - Inconsistent reporting in BI/ERP/invoicing/hotel systems
9
+ *
10
+ * Solution:
11
+ * All date calculations go through an adapter layer.
12
+ * This allows:
13
+ * - Timezone-safe operations by default (NativeDateAdapter normalizes to day boundaries)
14
+ * - Optional integration with Luxon/DayJS/date-fns for advanced timezone handling
15
+ * - Consistent behavior across SSR and client
16
+ * - Migration path to timezone-aware libraries without breaking changes
17
+ *
18
+ * Architecture:
19
+ * ```
20
+ * DualDateRangeStore
21
+ * ↓ uses
22
+ * DateAdapter ← Inject via DATE_ADAPTER token
23
+ * ↓ implements
24
+ * NativeDateAdapter (default, zero deps)
25
+ * LuxonDateAdapter (optional, full timezone support)
26
+ * DayJSDateAdapter (optional, lightweight)
27
+ * ```
28
+ *
29
+ * Usage:
30
+ * ```typescript
31
+ * // Default (NativeDateAdapter)
32
+ * bootstrapApplication(AppComponent);
33
+ *
34
+ * // Advanced (Luxon with timezone)
35
+ * import { LuxonDateAdapter } from '@oneluiz/dual-datepicker/luxon';
36
+ *
37
+ * bootstrapApplication(AppComponent, {
38
+ * providers: [
39
+ * { provide: DATE_ADAPTER, useClass: LuxonDateAdapter }
40
+ * ]
41
+ * });
42
+ * ```
43
+ */
44
+ import { InjectionToken } from '@angular/core';
45
+ /**
46
+ * Date adapter interface for all calendar/range operations
47
+ *
48
+ * All methods operate on "calendar day" level, ignoring time components.
49
+ * Implementations must ensure timezone-safe behavior.
50
+ *
51
+ * Implementations:
52
+ * - NativeDateAdapter: Default, zero dependencies, uses Date with normalization
53
+ * - LuxonDateAdapter: Optional, full timezone support with Luxon
54
+ * - DayJSDateAdapter: Optional, lightweight timezone support
55
+ * - Custom: Implement for your specific backend/timezone requirements
56
+ */
57
+ export interface DateAdapter {
58
+ /**
59
+ * Normalize date to start of day (00:00:00.000)
60
+ *
61
+ * Critical for timezone-safe comparisons.
62
+ *
63
+ * Example:
64
+ * ```typescript
65
+ * const date = new Date('2026-02-21T15:30:00');
66
+ * const normalized = adapter.normalize(date);
67
+ * // → 2026-02-21T00:00:00.000 (in local timezone)
68
+ * ```
69
+ *
70
+ * @param date - Date to normalize
71
+ * @returns Date with time set to 00:00:00.000
72
+ */
73
+ normalize(date: Date): Date;
74
+ /**
75
+ * Check if two dates represent the same calendar day
76
+ *
77
+ * Ignores time component. Timezone-safe.
78
+ *
79
+ * Example:
80
+ * ```typescript
81
+ * const a = new Date('2026-02-21T23:59:59');
82
+ * const b = new Date('2026-02-21T00:00:01');
83
+ * adapter.isSameDay(a, b); // → true
84
+ * ```
85
+ */
86
+ isSameDay(a: Date, b: Date): boolean;
87
+ /**
88
+ * Check if date A is before date B (calendar day level)
89
+ *
90
+ * Example:
91
+ * ```typescript
92
+ * adapter.isBeforeDay(new Date('2026-02-20'), new Date('2026-02-21')); // → true
93
+ * adapter.isBeforeDay(new Date('2026-02-21'), new Date('2026-02-21')); // → false
94
+ * ```
95
+ */
96
+ isBeforeDay(a: Date, b: Date): boolean;
97
+ /**
98
+ * Check if date A is after date B (calendar day level)
99
+ *
100
+ * Example:
101
+ * ```typescript
102
+ * adapter.isAfterDay(new Date('2026-02-22'), new Date('2026-02-21')); // → true
103
+ * adapter.isAfterDay(new Date('2026-02-21'), new Date('2026-02-21')); // → false
104
+ * ```
105
+ */
106
+ isAfterDay(a: Date, b: Date): boolean;
107
+ /**
108
+ * Add days to a date
109
+ *
110
+ * Must handle DST transitions correctly.
111
+ *
112
+ * Example:
113
+ * ```typescript
114
+ * const date = new Date('2026-02-21');
115
+ * const future = adapter.addDays(date, 7);
116
+ * // → 2026-02-28
117
+ * ```
118
+ */
119
+ addDays(date: Date, days: number): Date;
120
+ /**
121
+ * Add months to a date
122
+ *
123
+ * Must handle month overflow (e.g., Jan 31 + 1 month → Feb 28).
124
+ *
125
+ * Example:
126
+ * ```typescript
127
+ * const date = new Date('2026-01-31');
128
+ * const next = adapter.addMonths(date, 1);
129
+ * // → 2026-02-28 (not March 3rd)
130
+ * ```
131
+ */
132
+ addMonths(date: Date, months: number): Date;
133
+ /**
134
+ * Get start of day (00:00:00.000)
135
+ *
136
+ * Similar to normalize() but explicit intent.
137
+ */
138
+ startOfDay(date: Date): Date;
139
+ /**
140
+ * Get end of day (23:59:59.999)
141
+ *
142
+ * Useful for range queries that need to include entire day.
143
+ *
144
+ * Example:
145
+ * ```typescript
146
+ * const date = new Date('2026-02-21');
147
+ * const end = adapter.endOfDay(date);
148
+ * // → 2026-02-21T23:59:59.999
149
+ * ```
150
+ */
151
+ endOfDay(date: Date): Date;
152
+ /**
153
+ * Get first day of month (00:00:00.000)
154
+ *
155
+ * Example:
156
+ * ```typescript
157
+ * const date = new Date('2026-02-21');
158
+ * const start = adapter.startOfMonth(date);
159
+ * // → 2026-02-01T00:00:00.000
160
+ * ```
161
+ */
162
+ startOfMonth(date: Date): Date;
163
+ /**
164
+ * Get last day of month (23:59:59.999)
165
+ *
166
+ * Example:
167
+ * ```typescript
168
+ * const date = new Date('2026-02-21');
169
+ * const end = adapter.endOfMonth(date);
170
+ * // → 2026-02-28T23:59:59.999
171
+ * ```
172
+ */
173
+ endOfMonth(date: Date): Date;
174
+ /**
175
+ * Get year (4-digit)
176
+ *
177
+ * Example:
178
+ * ```typescript
179
+ * adapter.getYear(new Date('2026-02-21')); // → 2026
180
+ * ```
181
+ */
182
+ getYear(date: Date): number;
183
+ /**
184
+ * Get month (0-11, where 0 = January)
185
+ *
186
+ * Example:
187
+ * ```typescript
188
+ * adapter.getMonth(new Date('2026-02-21')); // → 1 (February)
189
+ * ```
190
+ */
191
+ getMonth(date: Date): number;
192
+ /**
193
+ * Get day of month (1-31)
194
+ *
195
+ * Example:
196
+ * ```typescript
197
+ * adapter.getDate(new Date('2026-02-21')); // → 21
198
+ * ```
199
+ */
200
+ getDate(date: Date): number;
201
+ /**
202
+ * Get day of week (0-6, where 0 = Sunday)
203
+ *
204
+ * Example:
205
+ * ```typescript
206
+ * adapter.getDay(new Date('2026-02-21')); // → 6 (Saturday)
207
+ * ```
208
+ */
209
+ getDay(date: Date): number;
210
+ /**
211
+ * Convert Date to ISO date string (YYYY-MM-DD)
212
+ *
213
+ * CRITICAL: Must be timezone-safe!
214
+ * DO NOT use date.toISOString() as it converts to UTC.
215
+ *
216
+ * Example:
217
+ * ```typescript
218
+ * // Local timezone GMT-6 (CST)
219
+ * const date = new Date('2026-02-21T23:00:00'); // 11 PM CST
220
+ *
221
+ * // ❌ WRONG: date.toISOString().split('T')[0]
222
+ * // Returns "2026-02-22" (shifted to UTC!)
223
+ *
224
+ * // ✅ CORRECT: adapter.toISODate(date)
225
+ * // Returns "2026-02-21" (local date preserved)
226
+ * ```
227
+ *
228
+ * @param date - Date to format (or null)
229
+ * @returns ISO date string in YYYY-MM-DD format (local timezone), empty string if null
230
+ */
231
+ toISODate(date: Date | null): string;
232
+ /**
233
+ * Parse ISO date string (YYYY-MM-DD) to Date
234
+ *
235
+ * CRITICAL: Must be timezone-safe!
236
+ * DO NOT use new Date(isoString) as it may parse as UTC.
237
+ *
238
+ * Example:
239
+ * ```typescript
240
+ * // ❌ WRONG: new Date('2026-02-21')
241
+ * // May parse as UTC midnight, which is previous day in some timezones
242
+ *
243
+ * // ✅ CORRECT: adapter.parseISODate('2026-02-21')
244
+ * // Returns Date representing 2026-02-21 00:00:00 in local timezone
245
+ * ```
246
+ *
247
+ * @param isoDate - ISO date string (YYYY-MM-DD) or null
248
+ * @returns Date object or null if invalid
249
+ */
250
+ parseISODate(isoDate: string | null): Date | null;
251
+ /**
252
+ * Get week start day for locale
253
+ *
254
+ * 0 = Sunday, 1 = Monday, etc.
255
+ *
256
+ * Example:
257
+ * ```typescript
258
+ * adapter.getWeekStart('en-US'); // → 0 (Sunday)
259
+ * adapter.getWeekStart('en-GB'); // → 1 (Monday)
260
+ * ```
261
+ *
262
+ * @param locale - Locale string (e.g., 'en-US', 'es-ES')
263
+ * @returns Day number (0-6)
264
+ */
265
+ getWeekStart(locale?: string): 0 | 1 | 2 | 3 | 4 | 5 | 6;
266
+ }
267
+ /**
268
+ * Injection token for DateAdapter
269
+ *
270
+ * Default: NativeDateAdapter (zero dependencies)
271
+ *
272
+ * Override for advanced timezone handling:
273
+ *
274
+ * ```typescript
275
+ * // Luxon with timezone
276
+ * import { LuxonDateAdapter } from '@oneluiz/dual-datepicker/luxon';
277
+ *
278
+ * bootstrapApplication(AppComponent, {
279
+ * providers: [
280
+ * {
281
+ * provide: DATE_ADAPTER,
282
+ * useClass: LuxonDateAdapter
283
+ * }
284
+ * ]
285
+ * });
286
+ * ```
287
+ *
288
+ * Custom implementation:
289
+ *
290
+ * ```typescript
291
+ * class CustomDateAdapter implements DateAdapter {
292
+ * // Your implementation for backend-specific date handling
293
+ * }
294
+ *
295
+ * provide(DATE_ADAPTER, { useClass: CustomDateAdapter });
296
+ * ```
297
+ */
298
+ export declare const DATE_ADAPTER: InjectionToken<DateAdapter>;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Date Clock Abstraction for SSR-Safe Date Resolution
3
+ *
4
+ * Problem:
5
+ * Presets like "Last 7 Days" or "This Month" use new Date() which causes:
6
+ * - SSR hydration mismatch
7
+ * - Server renders "2026-02-14", client renders "2026-02-15"
8
+ * - Different filters in dashboards
9
+ * - Different queries in ERP/BI
10
+ * - Cache inconsistency
11
+ *
12
+ * Solution:
13
+ * Inject a DateClock to control time deterministically:
14
+ *
15
+ * Server (SSR):
16
+ * provide(DATE_CLOCK, {
17
+ * useValue: { now: () => new Date('2026-02-21T00:00:00Z') }
18
+ * })
19
+ *
20
+ * Client (Browser):
21
+ * Uses SystemClock by default (new Date())
22
+ *
23
+ * Testing:
24
+ * provide(DATE_CLOCK, {
25
+ * useValue: { now: () => new Date('2026-01-15T10:30:00Z') }
26
+ * })
27
+ *
28
+ * Architecture:
29
+ * - PresetEngine receives DateClock via DI
30
+ * - All preset calculations use clock.now() instead of new Date()
31
+ * - Deterministic: Same clock.now() → Same preset result
32
+ * - SSR-compatible: Server and client resolve identical ranges
33
+ */
34
+ import { InjectionToken } from '@angular/core';
35
+ /**
36
+ * Clock abstraction for deterministic date resolution
37
+ *
38
+ * Use cases:
39
+ * - SSR: Ensure server and client generate identical presets
40
+ * - Testing: Control time for predictable test results
41
+ * - Replay: Reproduce exact user state from past sessions
42
+ * - Demo: Freeze time for reproducible demos
43
+ */
44
+ export interface DateClock {
45
+ /**
46
+ * Get current date/time
47
+ *
48
+ * Default implementation returns new Date()
49
+ * Override for SSR, testing, or time-travel scenarios
50
+ */
51
+ now(): Date;
52
+ }
53
+ /**
54
+ * Injection token for DateClock
55
+ *
56
+ * Usage:
57
+ * ```typescript
58
+ * // Default (uses SystemClock)
59
+ * bootstrapApplication(AppComponent);
60
+ *
61
+ * // SSR Override
62
+ * bootstrapApplication(AppComponent, {
63
+ * providers: [
64
+ * {
65
+ * provide: DATE_CLOCK,
66
+ * useValue: { now: () => new Date('2026-02-21T00:00:00Z') }
67
+ * }
68
+ * ]
69
+ * });
70
+ *
71
+ * // Testing Override
72
+ * TestBed.configureTestingModule({
73
+ * providers: [
74
+ * {
75
+ * provide: DATE_CLOCK,
76
+ * useValue: { now: () => new Date('2026-01-15T12:00:00Z') }
77
+ * }
78
+ * ]
79
+ * });
80
+ * ```
81
+ */
82
+ export declare const DATE_CLOCK: InjectionToken<DateClock>;
@@ -0,0 +1,113 @@
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 presetEngine;
18
+ private clock;
19
+ private adapter;
20
+ constructor();
21
+ private config;
22
+ private _startDate;
23
+ private _endDate;
24
+ private _leftMonth;
25
+ private _rightMonth;
26
+ private _selectingStart;
27
+ private _startTime;
28
+ private _endTime;
29
+ private _pendingStart;
30
+ private _pendingEnd;
31
+ private _hasPendingChanges;
32
+ readonly startDate: import("@angular/core").Signal<Date>;
33
+ readonly endDate: import("@angular/core").Signal<Date>;
34
+ readonly leftMonth: import("@angular/core").Signal<Date>;
35
+ readonly rightMonth: import("@angular/core").Signal<Date>;
36
+ readonly selectingStart: import("@angular/core").Signal<boolean>;
37
+ readonly startTime: import("@angular/core").Signal<string>;
38
+ readonly endTime: import("@angular/core").Signal<string>;
39
+ readonly hasPendingChanges: import("@angular/core").Signal<boolean>;
40
+ readonly range: import("@angular/core").Signal<DateRangeState>;
41
+ readonly isValid: import("@angular/core").Signal<boolean>;
42
+ readonly rangeText: import("@angular/core").Signal<string>;
43
+ /**
44
+ * Configure the store
45
+ */
46
+ configure(config: Partial<DateRangeConfig>): void;
47
+ /**
48
+ * Set start date (with validation)
49
+ */
50
+ setStart(date: Date | string | null): void;
51
+ /**
52
+ * Set end date (with validation)
53
+ */
54
+ setEnd(date: Date | string | null): void;
55
+ /**
56
+ * Set complete range at once
57
+ */
58
+ setRange(start: Date | string | null, end: Date | string | null): void;
59
+ /**
60
+ * Set pending selection (for requireApply mode)
61
+ */
62
+ setPendingStart(date: Date | string | null): void;
63
+ setPendingEnd(date: Date | string | null): void;
64
+ /**
65
+ * Apply pending changes
66
+ */
67
+ applyPending(): void;
68
+ /**
69
+ * Cancel pending changes
70
+ */
71
+ cancelPending(): void;
72
+ private clearPending;
73
+ /**
74
+ * Reset to empty state
75
+ */
76
+ reset(): void;
77
+ /**
78
+ * Apply a preset by key
79
+ *
80
+ * SSR-Safe: Uses injected DateClock for deterministic resolution
81
+ *
82
+ * @param presetKey - Preset identifier (e.g., 'TODAY', 'LAST_7_DAYS')
83
+ * @param now - Optional date override (defaults to clock.now())
84
+ */
85
+ applyPreset(presetKey: string, now?: Date): void;
86
+ /**
87
+ * Navigate left calendar month
88
+ */
89
+ setLeftMonth(date: Date): void;
90
+ /**
91
+ * Navigate right calendar month
92
+ */
93
+ setRightMonth(date: Date): void;
94
+ /**
95
+ * Set time values
96
+ */
97
+ setStartTime(time: string): void;
98
+ setEndTime(time: string): void;
99
+ /**
100
+ * Get current state as snapshot
101
+ */
102
+ getSnapshot(): DateRangeState;
103
+ /**
104
+ * Load state from snapshot
105
+ */
106
+ loadSnapshot(snapshot: DateRangeState): void;
107
+ private parseDate;
108
+ private getNextMonth;
109
+ private getPreviousMonth;
110
+ private formatDateShort;
111
+ static ɵfac: i0.ɵɵFactoryDeclaration<DualDateRangeStore, never>;
112
+ static ɵprov: i0.ɵɵInjectableDeclaration<DualDateRangeStore>;
113
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Core headless date range logic
3
+ * Import from here for clean barrel exports
4
+ */
5
+ export * from './dual-date-range.store';
6
+ export * from './preset.engine';
7
+ export * from './range.validator';
8
+ export * from './date-clock';
9
+ export * from './system-clock';
10
+ export * from './date-adapter';
11
+ export * from './native-date-adapter';