@oneluiz/dual-datepicker 3.9.2 → 4.0.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.
Files changed (47) hide show
  1. package/dual-datepicker.component.d.ts +4 -4
  2. package/esm2022/core/internal.mjs +92 -0
  3. package/esm2022/core/public.mjs +94 -0
  4. package/esm2022/core/testing/date.helpers.mjs +49 -0
  5. package/esm2022/core/testing/fixed-clock.mjs +30 -0
  6. package/esm2022/core/testing/index.mjs +8 -0
  7. package/esm2022/dual-datepicker.component.mjs +34 -32
  8. package/esm2022/public-api.mjs +39 -9
  9. package/fesm2022/oneluiz-dual-datepicker.mjs +629 -975
  10. package/fesm2022/oneluiz-dual-datepicker.mjs.map +1 -1
  11. package/package.json +11 -1
  12. package/public-api.d.ts +39 -4
  13. package/core/built-in-presets.d.ts +0 -98
  14. package/core/calendar-grid/cache.config.d.ts +0 -34
  15. package/core/calendar-grid/calendar-grid.cache.d.ts +0 -39
  16. package/core/calendar-grid/calendar-grid.factory.d.ts +0 -26
  17. package/core/calendar-grid/calendar-grid.types.d.ts +0 -57
  18. package/core/calendar-grid/index.d.ts +0 -56
  19. package/core/calendar-grid/range-highlighter.cache.d.ts +0 -106
  20. package/core/calendar-grid/range-highlighter.d.ts +0 -85
  21. package/core/calendar-grid/range-highlighter.types.d.ts +0 -182
  22. package/core/calendar-grid/virtual-weeks.logic.d.ts +0 -116
  23. package/core/calendar-grid/virtual-weeks.types.d.ts +0 -71
  24. package/core/date-adapter.d.ts +0 -298
  25. package/core/date-clock.d.ts +0 -82
  26. package/core/dual-date-range.store.d.ts +0 -113
  27. package/core/index.d.ts +0 -33
  28. package/core/native-date-adapter.d.ts +0 -152
  29. package/core/preset-providers.d.ts +0 -176
  30. package/core/preset-registry.d.ts +0 -181
  31. package/core/preset.engine.d.ts +0 -124
  32. package/core/range-preset.plugin.d.ts +0 -188
  33. package/core/range.validator.d.ts +0 -37
  34. package/core/system-clock.d.ts +0 -13
  35. package/date-adapter.d.ts +0 -116
  36. package/esm2022/core/calendar-grid/index.mjs +0 -57
  37. package/esm2022/core/index.mjs +0 -37
  38. package/esm2022/date-adapter.mjs +0 -12
  39. package/esm2022/native-date-adapter.mjs +0 -117
  40. package/esm2022/preset-utils.mjs +0 -276
  41. package/native-date-adapter.d.ts +0 -26
  42. package/preset-utils.d.ts +0 -91
  43. package/src/themes/bootstrap.scss +0 -202
  44. package/src/themes/bulma.scss +0 -209
  45. package/src/themes/custom.scss +0 -236
  46. package/src/themes/foundation.scss +0 -201
  47. package/src/themes/tailwind.scss +0 -109
@@ -1,134 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injectable, inject, signal, computed, Inject, EventEmitter, effect, APP_INITIALIZER, forwardRef, HostListener, Output, Input, Component, makeEnvironmentProviders } from '@angular/core';
2
+ import { InjectionToken, Injectable, inject, signal, computed, makeEnvironmentProviders, APP_INITIALIZER, Inject, EventEmitter, effect, forwardRef, HostListener, Output, Input, Component } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
6
6
 
7
- /**
8
- * Abstract class for date adapters.
9
- * Allows the component to work with different date libraries (Date, DayJS, date-fns, Luxon, etc.)
10
- */
11
- class DateAdapter {
12
- }
13
- /**
14
- * Injection token for DateAdapter
15
- */
16
- const DATE_ADAPTER$1 = new InjectionToken('DATE_ADAPTER');
17
-
18
- /**
19
- * Date adapter implementation for native JavaScript Date objects
20
- * This is the default adapter used by the component
21
- */
22
- let NativeDateAdapter$1 = class NativeDateAdapter extends DateAdapter {
23
- parse(value) {
24
- if (!value)
25
- return null;
26
- if (value instanceof Date) {
27
- return new Date(value);
28
- }
29
- if (typeof value === 'string' || typeof value === 'number') {
30
- // Fix timezone issues: parse YYYY-MM-DD as local date, not UTC
31
- if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
32
- const [year, month, day] = value.split('-').map(Number);
33
- return new Date(year, month - 1, day);
34
- }
35
- const date = new Date(value);
36
- return this.isValid(date) ? date : null;
37
- }
38
- return null;
39
- }
40
- format(date, format = 'YYYY-MM-DD') {
41
- if (!this.isValid(date))
42
- return '';
43
- const year = date.getFullYear();
44
- const month = String(date.getMonth() + 1).padStart(2, '0');
45
- const day = String(date.getDate()).padStart(2, '0');
46
- // Simple format implementation
47
- switch (format) {
48
- case 'YYYY-MM-DD':
49
- return `${year}-${month}-${day}`;
50
- case 'MM/DD/YYYY':
51
- return `${month}/${day}/${year}`;
52
- case 'DD/MM/YYYY':
53
- return `${day}/${month}/${year}`;
54
- default:
55
- return `${year}-${month}-${day}`;
56
- }
57
- }
58
- addDays(date, days) {
59
- const result = this.clone(date);
60
- result.setDate(result.getDate() + days);
61
- return result;
62
- }
63
- addMonths(date, months) {
64
- const result = this.clone(date);
65
- result.setMonth(result.getMonth() + months);
66
- return result;
67
- }
68
- getYear(date) {
69
- return date.getFullYear();
70
- }
71
- getMonth(date) {
72
- return date.getMonth();
73
- }
74
- getDate(date) {
75
- return date.getDate();
76
- }
77
- getDay(date) {
78
- return date.getDay();
79
- }
80
- createDate(year, month, date) {
81
- return new Date(year, month, date);
82
- }
83
- today() {
84
- return new Date();
85
- }
86
- isSameDay(a, b) {
87
- if (!a || !b)
88
- return false;
89
- if (!this.isValid(a) || !this.isValid(b))
90
- return false;
91
- return (a.getFullYear() === b.getFullYear() &&
92
- a.getMonth() === b.getMonth() &&
93
- a.getDate() === b.getDate());
94
- }
95
- isBefore(a, b) {
96
- if (!a || !b)
97
- return false;
98
- if (!this.isValid(a) || !this.isValid(b))
99
- return false;
100
- return a.getTime() < b.getTime();
101
- }
102
- isAfter(a, b) {
103
- if (!a || !b)
104
- return false;
105
- if (!this.isValid(a) || !this.isValid(b))
106
- return false;
107
- return a.getTime() > b.getTime();
108
- }
109
- isBetween(date, start, end) {
110
- if (!date || !start || !end)
111
- return false;
112
- if (!this.isValid(date) || !this.isValid(start) || !this.isValid(end))
113
- return false;
114
- const dateTime = date.getTime();
115
- const startTime = start.getTime();
116
- const endTime = end.getTime();
117
- return dateTime >= startTime && dateTime <= endTime;
118
- }
119
- clone(date) {
120
- return new Date(date.getTime());
121
- }
122
- isValid(date) {
123
- return date instanceof Date && !isNaN(date.getTime());
124
- }
125
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NativeDateAdapter, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
126
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NativeDateAdapter });
127
- };
128
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NativeDateAdapter$1, decorators: [{
129
- type: Injectable
130
- }] });
131
-
132
7
  /**
133
8
  * Pure validation functions for date ranges
134
9
  * No dependencies, no side effects - just logic
@@ -1817,281 +1692,528 @@ const BUILT_IN_PRESETS = [
1817
1692
  ];
1818
1693
 
1819
1694
  /**
1820
- * Calendar Grid Types
1695
+ * Provider Functions for Built-in Presets
1696
+ *
1697
+ * Version: 3.6.0
1698
+ *
1699
+ * Automatic registration of built-in date range presets.
1700
+ * These providers ensure backward compatibility by auto-registering
1701
+ * all standard presets (TODAY, LAST_7_DAYS, THIS_MONTH, etc.)
1821
1702
  *
1822
- * Defines the structure for memoized calendar month grids.
1823
- * Separates base grid structure (cacheable) from decorations (dynamic).
1703
+ * USAGE IN LIBRARY (Internal):
1704
+ * Built-in presets are registered automatically via Angular providers.
1705
+ * Library consumers don't need to do anything.
1706
+ *
1707
+ * USAGE IN APP (Custom Presets):
1708
+ * ```typescript
1709
+ * // app.config.ts
1710
+ * export const appConfig: ApplicationConfig = {
1711
+ * providers: [
1712
+ * // ... other providers
1713
+ * provideCustomPresets([
1714
+ * {
1715
+ * key: 'THIS_FISCAL_QUARTER',
1716
+ * resolve: (clock, adapter) => {
1717
+ * const now = clock.now();
1718
+ * // ... fiscal logic
1719
+ * return { start, end };
1720
+ * }
1721
+ * }
1722
+ * ])
1723
+ * ]
1724
+ * };
1725
+ * ```
1824
1726
  */
1825
-
1826
1727
  /**
1827
- * Calendar Grid Factory
1728
+ * Initializer function that registers built-in presets
1828
1729
  *
1829
- * Generates calendar month grids using DateAdapter for deterministic,
1830
- * timezone-safe date operations.
1730
+ * Runs at application startup (APP_INITIALIZER)
1831
1731
  *
1832
- * Grid structure:
1833
- * - Always 6 weeks x 7 days (42 cells) for layout stability
1834
- * - Includes padding days from previous/next month
1835
- * - No decorations (selected, disabled, etc.) - those are applied separately
1732
+ * @param registry - PresetRegistry instance
1733
+ * @returns Initialization function
1836
1734
  */
1837
- class CalendarGridFactory {
1838
- adapter;
1839
- constructor(adapter) {
1840
- this.adapter = adapter;
1841
- }
1842
- /**
1843
- * Create a calendar grid for a given month
1844
- *
1845
- * @param monthDate - Any date within the target month (will be normalized to start of month)
1846
- * @param weekStart - First day of week (0 = Sunday, 1 = Monday, etc.)
1847
- * @param locale - Locale identifier (optional, for future use)
1848
- * @returns CalendarGrid - 6 weeks x 7 days grid
1849
- */
1850
- createGrid(monthDate, weekStart = 0, locale) {
1851
- // Normalize to start of month
1852
- const year = this.adapter.getYear(monthDate);
1853
- const month = this.adapter.getMonth(monthDate);
1854
- const firstDayOfMonth = new Date(year, month, 1);
1855
- const normalizedFirst = this.adapter.normalize(firstDayOfMonth);
1856
- // Get days in month (day 0 of next month = last day of current month)
1857
- const lastDayOfMonth = new Date(year, month + 1, 0);
1858
- const daysInMonth = this.adapter.getDate(lastDayOfMonth);
1859
- // Get first day of week offset
1860
- const firstDayOfWeek = this.adapter.getDay(normalizedFirst);
1861
- const offset = this.calculateOffset(firstDayOfWeek, weekStart);
1862
- // Generate 42 cells (6 weeks x 7 days)
1863
- const cells = [];
1864
- let currentDate = this.adapter.addDays(normalizedFirst, -offset);
1865
- for (let i = 0; i < 42; i++) {
1866
- const cellDate = this.adapter.normalize(currentDate);
1867
- const cellYear = this.adapter.getYear(cellDate);
1868
- const cellMonth = this.adapter.getMonth(cellDate);
1869
- const cellDay = this.adapter.getDate(cellDate);
1870
- const cellDayOfWeek = this.adapter.getDay(cellDate);
1871
- cells.push({
1872
- date: cellDate,
1873
- inCurrentMonth: cellYear === year && cellMonth === month,
1874
- iso: this.adapter.toISODate(cellDate),
1875
- day: cellDay,
1876
- month: cellMonth,
1877
- year: cellYear,
1878
- dayOfWeek: cellDayOfWeek
1879
- });
1880
- currentDate = this.adapter.addDays(currentDate, 1);
1881
- }
1882
- // Split into weeks
1883
- const weeks = [];
1884
- for (let i = 0; i < 6; i++) {
1885
- weeks.push(cells.slice(i * 7, (i + 1) * 7));
1886
- }
1887
- return {
1888
- month: { year, month },
1889
- weekStart,
1890
- locale,
1891
- weeks,
1892
- cells
1893
- };
1894
- }
1895
- /**
1896
- * Calculate offset (number of padding days from previous month)
1897
- *
1898
- * @param firstDayOfWeek - Day of week for first day of month (0-6)
1899
- * @param weekStart - Desired week start (0-6)
1900
- * @returns Number of padding days needed
1901
- */
1902
- calculateOffset(firstDayOfWeek, weekStart) {
1903
- let offset = firstDayOfWeek - weekStart;
1904
- if (offset < 0) {
1905
- offset += 7;
1735
+ function initializeBuiltInPresets(registry) {
1736
+ return () => {
1737
+ // Register all built-in presets
1738
+ BUILT_IN_PRESETS.forEach(preset => {
1739
+ registry.register(preset);
1740
+ });
1741
+ // Log registration for debugging (can be removed in production)
1742
+ if (typeof console !== 'undefined' && console.debug) {
1743
+ console.debug(`[ng-dual-datepicker] Registered ${BUILT_IN_PRESETS.length} built-in presets:`, BUILT_IN_PRESETS.map(p => p.key).join(', '));
1906
1744
  }
1907
- return offset;
1908
- }
1909
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridFactory, deps: [{ token: DATE_ADAPTER }], target: i0.ɵɵFactoryTarget.Injectable });
1910
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridFactory, providedIn: 'root' });
1745
+ };
1911
1746
  }
1912
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridFactory, decorators: [{
1913
- type: Injectable,
1914
- args: [{ providedIn: 'root' }]
1915
- }], ctorParameters: () => [{ type: undefined, decorators: [{
1916
- type: Inject,
1917
- args: [DATE_ADAPTER]
1918
- }] }] });
1919
-
1920
1747
  /**
1921
- * Cache Configuration
1748
+ * Provide built-in date range presets
1922
1749
  *
1923
- * Centralized cache limits for memory safety.
1750
+ * This provider is automatically included in the library's root providers.
1751
+ * Library consumers don't need to add this manually.
1924
1752
  *
1925
- * @module core/calendar-grid/cache.config
1926
- * @version 3.9.2
1753
+ * @returns EnvironmentProviders for built-in presets
1754
+ *
1755
+ * @internal
1927
1756
  */
1757
+ function provideBuiltInPresets() {
1758
+ return makeEnvironmentProviders([
1759
+ {
1760
+ provide: APP_INITIALIZER,
1761
+ multi: true,
1762
+ useFactory: initializeBuiltInPresets,
1763
+ deps: [PresetRegistry]
1764
+ }
1765
+ ]);
1766
+ }
1928
1767
  /**
1929
- * Maximum cache entries before FIFO eviction
1768
+ * Provide custom date range presets
1930
1769
  *
1931
- * Applied to both:
1932
- * - CalendarGridCache: Raw month grids
1933
- * - RangeHighlighterCache: Decorated grids
1770
+ * Use this to register your own industry-specific presets:
1771
+ * - Fiscal presets
1772
+ * - Hotel/hospitality presets
1773
+ * - Logistics presets
1774
+ * - Custom business logic
1934
1775
  *
1935
- * Memory footprint:
1936
- * - CalendarGrid (raw): ~10KB each
1937
- * - DecoratedGrid: ~15KB each
1938
- * - Total worst case: (10KB + 15KB) × 48 = ~1.2MB
1776
+ * @param presets - Array of custom RangePresetPlugin implementations
1777
+ * @returns EnvironmentProviders for custom presets
1939
1778
  *
1940
- * Coverage:
1941
- * - 48 months = 4 years of navigation
1942
- * - Sufficient for long-running sessions
1779
+ * @example
1780
+ * ```typescript
1781
+ * // Fiscal presets
1782
+ * const FISCAL_PRESETS: RangePresetPlugin[] = [
1783
+ * {
1784
+ * key: 'THIS_FISCAL_QUARTER',
1785
+ * resolve: (clock, adapter) => {
1786
+ * const now = clock.now();
1787
+ * const month = adapter.getMonth(now);
1943
1788
  *
1944
- * Critical for:
1945
- * - ERP systems (invoice date selection)
1946
- * - BI dashboards (date range filters)
1947
- * - Hotel reservation systems (availability calendars)
1948
- * - Point-of-sale systems (report generation)
1789
+ * // Fiscal year starts April (month 3)
1790
+ * const fiscalMonth = (month + 9) % 12;
1791
+ * const quarterStart = Math.floor(fiscalMonth / 3) * 3;
1792
+ * const calendarMonth = (quarterStart - 9 + 12) % 12;
1949
1793
  *
1950
- * @constant
1951
- * @since v3.9.2
1794
+ * const year = adapter.getYear(now);
1795
+ * const fiscalYear = month < 3 ? year - 1 : year;
1796
+ *
1797
+ * const start = new Date(fiscalYear, calendarMonth, 1);
1798
+ * const end = new Date(fiscalYear, calendarMonth + 3, 0);
1799
+ *
1800
+ * return {
1801
+ * start: adapter.normalize(start),
1802
+ * end: adapter.normalize(end)
1803
+ * };
1804
+ * }
1805
+ * },
1806
+ * {
1807
+ * key: 'FISCAL_YEAR_TO_DATE',
1808
+ * resolve: (clock, adapter) => {
1809
+ * const now = clock.now();
1810
+ * const month = adapter.getMonth(now);
1811
+ * const year = adapter.getYear(now);
1812
+ *
1813
+ * // Fiscal year starts April 1
1814
+ * const fiscalYearStart = month >= 3
1815
+ * ? new Date(year, 3, 1)
1816
+ * : new Date(year - 1, 3, 1);
1817
+ *
1818
+ * return {
1819
+ * start: adapter.normalize(fiscalYearStart),
1820
+ * end: adapter.normalize(now)
1821
+ * };
1822
+ * }
1823
+ * }
1824
+ * ];
1825
+ *
1826
+ * // In app.config.ts
1827
+ * export const appConfig: ApplicationConfig = {
1828
+ * providers: [
1829
+ * provideCustomPresets(FISCAL_PRESETS)
1830
+ * ]
1831
+ * };
1832
+ *
1833
+ * // Use in components
1834
+ * store.applyPreset('THIS_FISCAL_QUARTER');
1835
+ * store.applyPreset('FISCAL_YEAR_TO_DATE');
1836
+ * ```
1837
+ *
1838
+ * @example
1839
+ * ```typescript
1840
+ * // Hotel presets
1841
+ * const HOTEL_PRESETS: RangePresetPlugin[] = [
1842
+ * {
1843
+ * key: 'CHECK_IN_WEEK',
1844
+ * resolve: (clock, adapter) => {
1845
+ * const now = clock.now();
1846
+ * const dayOfWeek = adapter.getDayOfWeek(now);
1847
+ *
1848
+ * // Check-in week: Friday to Friday
1849
+ * const daysToNextFriday = dayOfWeek <= 5
1850
+ * ? 5 - dayOfWeek
1851
+ * : 7 - dayOfWeek + 5;
1852
+ *
1853
+ * const nextFriday = adapter.addDays(now, daysToNextFriday);
1854
+ * const followingFriday = adapter.addDays(nextFriday, 7);
1855
+ *
1856
+ * return { start: nextFriday, end: followingFriday };
1857
+ * }
1858
+ * },
1859
+ * {
1860
+ * key: 'NEXT_30_NIGHTS',
1861
+ * resolve: (clock, adapter) => {
1862
+ * const now = clock.now();
1863
+ * const tomorrow = adapter.addDays(now, 1);
1864
+ * const end = adapter.addDays(tomorrow, 30);
1865
+ * return { start: tomorrow, end };
1866
+ * }
1867
+ * }
1868
+ * ];
1869
+ *
1870
+ * providers: [provideCustomPresets(HOTEL_PRESETS)]
1871
+ * ```
1952
1872
  */
1953
- const MAX_CACHE_ENTRIES = 48;
1954
-
1873
+ function provideCustomPresets(presets) {
1874
+ return makeEnvironmentProviders([
1875
+ {
1876
+ provide: APP_INITIALIZER,
1877
+ multi: true,
1878
+ useFactory: (registry) => {
1879
+ return () => {
1880
+ presets.forEach(preset => {
1881
+ registry.register(preset);
1882
+ });
1883
+ if (typeof console !== 'undefined' && console.debug) {
1884
+ console.debug(`[ng-dual-datepicker] Registered ${presets.length} custom presets:`, presets.map(p => p.key).join(', '));
1885
+ }
1886
+ };
1887
+ },
1888
+ deps: [PresetRegistry]
1889
+ }
1890
+ ]);
1891
+ }
1955
1892
  /**
1956
- * Calendar Grid Cache
1893
+ * Provide preset package
1957
1894
  *
1958
- * LRU cache for calendar month grids to avoid recomputing the same month grid
1959
- * multiple times when only decorations (selected dates, hover, etc.) change.
1895
+ * Convenience function for external preset packages.
1960
1896
  *
1961
- * Strategy:
1962
- * - Key: year-month-weekStart-locale
1963
- * - LRU eviction (least recently used) when limit is reached
1964
- * - Default limit: 48 months (covers 2 years of navigation)
1897
+ * @param packageName - Name of the preset package (for logging)
1898
+ * @param presets - Array of presets from the package
1899
+ * @returns EnvironmentProviders
1965
1900
  *
1966
- * Performance impact:
1967
- * - Eliminates ~90% of grid recalculations in typical usage
1968
- * - Memory footprint: ~10KB per cached month (negligible)
1901
+ * @example
1902
+ * ```typescript
1903
+ * // @acme/fiscal-presets package
1904
+ * export function provideFiscalPresets(): EnvironmentProviders {
1905
+ * return providePresetPackage('@acme/fiscal-presets', FISCAL_PRESETS);
1906
+ * }
1969
1907
  *
1970
- * Memory safety (v3.9.2):
1971
- * - MAX_CACHE_ENTRIES prevents unbounded growth in long-running sessions
1972
- * - Critical for: ERP, BI dashboards, hotel reservation systems
1973
- * - FIFO eviction when limit exceeded
1908
+ * // In app
1909
+ * import { provideFiscalPresets } from '@acme/fiscal-presets';
1910
+ *
1911
+ * providers: [provideFiscalPresets()]
1912
+ * ```
1974
1913
  */
1975
- class CalendarGridCache {
1976
- factory;
1977
- cache = new Map();
1978
- maxSize = MAX_CACHE_ENTRIES;
1979
- constructor(factory) {
1980
- this.factory = factory;
1981
- }
1982
- /**
1983
- * Get or create a calendar grid
1984
- *
1985
- * @param monthDate - Any date within the target month
1986
- * @param weekStart - First day of week (0 = Sunday, 1 = Monday, etc.)
1987
- * @param locale - Locale identifier (optional)
1988
- * @returns CalendarGrid - cached or newly created
1989
- */
1990
- get(monthDate, weekStart = 0, locale) {
1991
- const key = this.buildKey(monthDate, weekStart, locale);
1992
- // Check cache
1993
- const cached = this.cache.get(key);
1994
- if (cached) {
1995
- // Move to end (LRU)
1996
- this.cache.delete(key);
1997
- this.cache.set(key, cached);
1998
- return cached;
1999
- }
2000
- // Generate new grid
2001
- const grid = this.factory.createGrid(monthDate, weekStart, locale);
2002
- // Store in cache
2003
- this.cache.set(key, grid);
2004
- // Evict oldest if over limit (LRU)
2005
- if (this.cache.size > this.maxSize) {
2006
- const firstKey = this.cache.keys().next().value;
2007
- this.cache.delete(firstKey);
1914
+ function providePresetPackage(packageName, presets) {
1915
+ return makeEnvironmentProviders([
1916
+ {
1917
+ provide: APP_INITIALIZER,
1918
+ multi: true,
1919
+ useFactory: (registry) => {
1920
+ return () => {
1921
+ presets.forEach(preset => {
1922
+ registry.register(preset);
1923
+ });
1924
+ if (typeof console !== 'undefined' && console.debug) {
1925
+ console.debug(`[${packageName}] Registered ${presets.length} presets:`, presets.map(p => p.key).join(', '));
1926
+ }
1927
+ };
1928
+ },
1929
+ deps: [PresetRegistry]
2008
1930
  }
2009
- return grid;
2010
- }
2011
- /**
2012
- * Build cache key from month parameters
2013
- *
2014
- * Format: "year-month-weekStart-locale"
2015
- * Example: "2026-1-0-en" (Feb 2026, Sunday start, English)
2016
- */
2017
- buildKey(monthDate, weekStart, locale) {
2018
- const year = monthDate.getFullYear();
2019
- const month = monthDate.getMonth();
2020
- return `${year}-${month}-${weekStart}${locale ? '-' + locale : ''}`;
2021
- }
2022
- /**
2023
- * Clear entire cache (for testing or manual reset)
2024
- */
2025
- clear() {
2026
- this.cache.clear();
2027
- }
2028
- /**
2029
- * Get cache size (for debugging/testing)
2030
- */
2031
- size() {
2032
- return this.cache.size;
2033
- }
2034
- /**
2035
- * Check if a specific month is cached
2036
- */
2037
- has(monthDate, weekStart = 0, locale) {
2038
- const key = this.buildKey(monthDate, weekStart, locale);
2039
- return this.cache.has(key);
2040
- }
2041
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridCache, deps: [{ token: CalendarGridFactory }], target: i0.ɵɵFactoryTarget.Injectable });
2042
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridCache, providedIn: 'root' });
1931
+ ]);
2043
1932
  }
2044
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridCache, decorators: [{
2045
- type: Injectable,
2046
- args: [{ providedIn: 'root' }]
2047
- }], ctorParameters: () => [{ type: CalendarGridFactory }] });
2048
1933
 
2049
1934
  /**
2050
- * Range Highlighter Types
1935
+ * PUBLIC API for @oneluiz/dual-datepicker
2051
1936
  *
2052
- * Type definitions for calendar cell decorations with range highlighting.
2053
- * Used by RangeHighlighter to cache decorated grids and avoid recomputations.
1937
+ * This file defines the stable public API contract.
1938
+ * Exports from this barrel are guaranteed to follow semantic versioning.
2054
1939
  *
2055
- * @module core/calendar-grid/range-highlighter.types
2056
- * @version 3.8.0
1940
+ * @public
1941
+ * @packageDocumentation
1942
+ * @module @oneluiz/dual-datepicker/core
1943
+ * @since v4.0.0
2057
1944
  */
2058
-
1945
+ // ============================================================================
1946
+ // HEADLESS STORE
1947
+ // ============================================================================
2059
1948
  /**
2060
- * Range Highlighter Service
1949
+ * DualDateRangeStore - Signal-based headless date range store
2061
1950
  *
2062
- * Decorates calendar grids with range highlights, hover previews, and disabled states.
2063
- * Pure computation layer - no caching (see RangeHighlighterCache for memoization).
1951
+ * Core reactive store for managing date range state without UI dependencies.
1952
+ * Perfect for SSR, global state, and custom UI implementations.
2064
1953
  *
2065
- * @module core/calendar-grid/range-highlighter
2066
- * @version 3.8.0
1954
+ * @public
1955
+ * @since v3.5.0
2067
1956
  */
1957
+
2068
1958
  /**
2069
- * Range Highlighter
2070
- *
2071
- * Applies decorations to calendar grids:
2072
- * - isSelectedStart, isSelectedEnd (range boundaries)
2073
- * - isInRange (cells within selected range)
2074
- * - isInHoverRange (hover preview)
2075
- * - isDisabled (constraints, custom predicates)
1959
+ * Calendar Grid Factory
2076
1960
  *
2077
- * Design:
2078
- * - Pure functions (same input = same output)
2079
- * - Uses DateAdapter for all date operations (SSR-safe)
2080
- * - No side effects, no state
2081
- * - Fast (~1ms for 42 cells on mobile)
1961
+ * Generates calendar month grids using DateAdapter for deterministic,
1962
+ * timezone-safe date operations.
2082
1963
  *
2083
- * Usage:
2084
- * ```typescript
2085
- * const grid = calendarGridCache.get(monthDate, 0);
2086
- * const decorated = rangeHighlighter.decorate(grid, {
2087
- * start: startDate,
2088
- * end: endDate,
2089
- * hoverDate: '2026-01-20',
2090
- * disabledDates: [...]
2091
- * });
2092
- * ```
1964
+ * Grid structure:
1965
+ * - Always 6 weeks x 7 days (42 cells) for layout stability
1966
+ * - Includes padding days from previous/next month
1967
+ * - No decorations (selected, disabled, etc.) - those are applied separately
2093
1968
  */
2094
- class RangeHighlighter {
1969
+ class CalendarGridFactory {
1970
+ adapter;
1971
+ constructor(adapter) {
1972
+ this.adapter = adapter;
1973
+ }
1974
+ /**
1975
+ * Create a calendar grid for a given month
1976
+ *
1977
+ * @param monthDate - Any date within the target month (will be normalized to start of month)
1978
+ * @param weekStart - First day of week (0 = Sunday, 1 = Monday, etc.)
1979
+ * @param locale - Locale identifier (optional, for future use)
1980
+ * @returns CalendarGrid - 6 weeks x 7 days grid
1981
+ */
1982
+ createGrid(monthDate, weekStart = 0, locale) {
1983
+ // Normalize to start of month
1984
+ const year = this.adapter.getYear(monthDate);
1985
+ const month = this.adapter.getMonth(monthDate);
1986
+ const firstDayOfMonth = new Date(year, month, 1);
1987
+ const normalizedFirst = this.adapter.normalize(firstDayOfMonth);
1988
+ // Get days in month (day 0 of next month = last day of current month)
1989
+ const lastDayOfMonth = new Date(year, month + 1, 0);
1990
+ const daysInMonth = this.adapter.getDate(lastDayOfMonth);
1991
+ // Get first day of week offset
1992
+ const firstDayOfWeek = this.adapter.getDay(normalizedFirst);
1993
+ const offset = this.calculateOffset(firstDayOfWeek, weekStart);
1994
+ // Generate 42 cells (6 weeks x 7 days)
1995
+ const cells = [];
1996
+ let currentDate = this.adapter.addDays(normalizedFirst, -offset);
1997
+ for (let i = 0; i < 42; i++) {
1998
+ const cellDate = this.adapter.normalize(currentDate);
1999
+ const cellYear = this.adapter.getYear(cellDate);
2000
+ const cellMonth = this.adapter.getMonth(cellDate);
2001
+ const cellDay = this.adapter.getDate(cellDate);
2002
+ const cellDayOfWeek = this.adapter.getDay(cellDate);
2003
+ cells.push({
2004
+ date: cellDate,
2005
+ inCurrentMonth: cellYear === year && cellMonth === month,
2006
+ iso: this.adapter.toISODate(cellDate),
2007
+ day: cellDay,
2008
+ month: cellMonth,
2009
+ year: cellYear,
2010
+ dayOfWeek: cellDayOfWeek
2011
+ });
2012
+ currentDate = this.adapter.addDays(currentDate, 1);
2013
+ }
2014
+ // Split into weeks
2015
+ const weeks = [];
2016
+ for (let i = 0; i < 6; i++) {
2017
+ weeks.push(cells.slice(i * 7, (i + 1) * 7));
2018
+ }
2019
+ return {
2020
+ month: { year, month },
2021
+ weekStart,
2022
+ locale,
2023
+ weeks,
2024
+ cells
2025
+ };
2026
+ }
2027
+ /**
2028
+ * Calculate offset (number of padding days from previous month)
2029
+ *
2030
+ * @param firstDayOfWeek - Day of week for first day of month (0-6)
2031
+ * @param weekStart - Desired week start (0-6)
2032
+ * @returns Number of padding days needed
2033
+ */
2034
+ calculateOffset(firstDayOfWeek, weekStart) {
2035
+ let offset = firstDayOfWeek - weekStart;
2036
+ if (offset < 0) {
2037
+ offset += 7;
2038
+ }
2039
+ return offset;
2040
+ }
2041
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridFactory, deps: [{ token: DATE_ADAPTER }], target: i0.ɵɵFactoryTarget.Injectable });
2042
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridFactory, providedIn: 'root' });
2043
+ }
2044
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridFactory, decorators: [{
2045
+ type: Injectable,
2046
+ args: [{ providedIn: 'root' }]
2047
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2048
+ type: Inject,
2049
+ args: [DATE_ADAPTER]
2050
+ }] }] });
2051
+
2052
+ /**
2053
+ * Cache Configuration
2054
+ *
2055
+ * Centralized cache limits for memory safety.
2056
+ *
2057
+ * @module core/calendar-grid/cache.config
2058
+ * @version 3.9.2
2059
+ */
2060
+ /**
2061
+ * Maximum cache entries before FIFO eviction
2062
+ *
2063
+ * Applied to both:
2064
+ * - CalendarGridCache: Raw month grids
2065
+ * - RangeHighlighterCache: Decorated grids
2066
+ *
2067
+ * Memory footprint:
2068
+ * - CalendarGrid (raw): ~10KB each
2069
+ * - DecoratedGrid: ~15KB each
2070
+ * - Total worst case: (10KB + 15KB) × 48 = ~1.2MB
2071
+ *
2072
+ * Coverage:
2073
+ * - 48 months = 4 years of navigation
2074
+ * - Sufficient for long-running sessions
2075
+ *
2076
+ * Critical for:
2077
+ * - ERP systems (invoice date selection)
2078
+ * - BI dashboards (date range filters)
2079
+ * - Hotel reservation systems (availability calendars)
2080
+ * - Point-of-sale systems (report generation)
2081
+ *
2082
+ * @constant
2083
+ * @since v3.9.2
2084
+ */
2085
+ const MAX_CACHE_ENTRIES = 48;
2086
+
2087
+ /**
2088
+ * Calendar Grid Cache
2089
+ *
2090
+ * LRU cache for calendar month grids to avoid recomputing the same month grid
2091
+ * multiple times when only decorations (selected dates, hover, etc.) change.
2092
+ *
2093
+ * Strategy:
2094
+ * - Key: year-month-weekStart-locale
2095
+ * - LRU eviction (least recently used) when limit is reached
2096
+ * - Default limit: 48 months (covers 2 years of navigation)
2097
+ *
2098
+ * Performance impact:
2099
+ * - Eliminates ~90% of grid recalculations in typical usage
2100
+ * - Memory footprint: ~10KB per cached month (negligible)
2101
+ *
2102
+ * Memory safety (v3.9.2):
2103
+ * - MAX_CACHE_ENTRIES prevents unbounded growth in long-running sessions
2104
+ * - Critical for: ERP, BI dashboards, hotel reservation systems
2105
+ * - FIFO eviction when limit exceeded
2106
+ */
2107
+ class CalendarGridCache {
2108
+ factory;
2109
+ cache = new Map();
2110
+ maxSize = MAX_CACHE_ENTRIES;
2111
+ constructor(factory) {
2112
+ this.factory = factory;
2113
+ }
2114
+ /**
2115
+ * Get or create a calendar grid
2116
+ *
2117
+ * @param monthDate - Any date within the target month
2118
+ * @param weekStart - First day of week (0 = Sunday, 1 = Monday, etc.)
2119
+ * @param locale - Locale identifier (optional)
2120
+ * @returns CalendarGrid - cached or newly created
2121
+ */
2122
+ get(monthDate, weekStart = 0, locale) {
2123
+ const key = this.buildKey(monthDate, weekStart, locale);
2124
+ // Check cache
2125
+ const cached = this.cache.get(key);
2126
+ if (cached) {
2127
+ // Move to end (LRU)
2128
+ this.cache.delete(key);
2129
+ this.cache.set(key, cached);
2130
+ return cached;
2131
+ }
2132
+ // Generate new grid
2133
+ const grid = this.factory.createGrid(monthDate, weekStart, locale);
2134
+ // Store in cache
2135
+ this.cache.set(key, grid);
2136
+ // Evict oldest if over limit (LRU)
2137
+ if (this.cache.size > this.maxSize) {
2138
+ const firstKey = this.cache.keys().next().value;
2139
+ this.cache.delete(firstKey);
2140
+ }
2141
+ return grid;
2142
+ }
2143
+ /**
2144
+ * Build cache key from month parameters
2145
+ *
2146
+ * Format: "year-month-weekStart-locale"
2147
+ * Example: "2026-1-0-en" (Feb 2026, Sunday start, English)
2148
+ */
2149
+ buildKey(monthDate, weekStart, locale) {
2150
+ const year = monthDate.getFullYear();
2151
+ const month = monthDate.getMonth();
2152
+ return `${year}-${month}-${weekStart}${locale ? '-' + locale : ''}`;
2153
+ }
2154
+ /**
2155
+ * Clear entire cache (for testing or manual reset)
2156
+ */
2157
+ clear() {
2158
+ this.cache.clear();
2159
+ }
2160
+ /**
2161
+ * Get cache size (for debugging/testing)
2162
+ */
2163
+ size() {
2164
+ return this.cache.size;
2165
+ }
2166
+ /**
2167
+ * Check if a specific month is cached
2168
+ */
2169
+ has(monthDate, weekStart = 0, locale) {
2170
+ const key = this.buildKey(monthDate, weekStart, locale);
2171
+ return this.cache.has(key);
2172
+ }
2173
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridCache, deps: [{ token: CalendarGridFactory }], target: i0.ɵɵFactoryTarget.Injectable });
2174
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridCache, providedIn: 'root' });
2175
+ }
2176
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarGridCache, decorators: [{
2177
+ type: Injectable,
2178
+ args: [{ providedIn: 'root' }]
2179
+ }], ctorParameters: () => [{ type: CalendarGridFactory }] });
2180
+
2181
+ /**
2182
+ * Range Highlighter Service
2183
+ *
2184
+ * Decorates calendar grids with range highlights, hover previews, and disabled states.
2185
+ * Pure computation layer - no caching (see RangeHighlighterCache for memoization).
2186
+ *
2187
+ * @module core/calendar-grid/range-highlighter
2188
+ * @version 3.8.0
2189
+ */
2190
+ /**
2191
+ * Range Highlighter
2192
+ *
2193
+ * Applies decorations to calendar grids:
2194
+ * - isSelectedStart, isSelectedEnd (range boundaries)
2195
+ * - isInRange (cells within selected range)
2196
+ * - isInHoverRange (hover preview)
2197
+ * - isDisabled (constraints, custom predicates)
2198
+ *
2199
+ * Design:
2200
+ * - Pure functions (same input = same output)
2201
+ * - Uses DateAdapter for all date operations (SSR-safe)
2202
+ * - No side effects, no state
2203
+ * - Fast (~1ms for 42 cells on mobile)
2204
+ *
2205
+ * Usage:
2206
+ * ```typescript
2207
+ * const grid = calendarGridCache.get(monthDate, 0);
2208
+ * const decorated = rangeHighlighter.decorate(grid, {
2209
+ * start: startDate,
2210
+ * end: endDate,
2211
+ * hoverDate: '2026-01-20',
2212
+ * disabledDates: [...]
2213
+ * });
2214
+ * ```
2215
+ */
2216
+ class RangeHighlighter {
2095
2217
  adapter;
2096
2218
  constructor(adapter) {
2097
2219
  this.adapter = adapter;
@@ -2433,16 +2555,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2433
2555
  args: [DATE_ADAPTER]
2434
2556
  }] }] });
2435
2557
 
2436
- /**
2437
- * Virtual Weeks Types
2438
- *
2439
- * Type definitions for windowed week rendering (virtual scrolling).
2440
- * Reduces DOM nodes by rendering only visible weeks instead of full month.
2441
- *
2442
- * @module core/calendar-grid/virtual-weeks.types
2443
- * @version 3.9.0
2444
- */
2445
-
2446
2558
  /**
2447
2559
  * Virtual Weeks Logic (Pure Functions)
2448
2560
  *
@@ -2593,51 +2705,110 @@ function isVirtualWeeksEnabled(windowSize, totalWeeks) {
2593
2705
  }
2594
2706
 
2595
2707
  /**
2596
- * Calendar Grid Module
2597
- *
2598
- * Performance-optimized calendar month grid generation with memoization.
2599
- *
2600
- * v3.7.0: Grid Structure Cache
2601
- * - CalendarGridFactory for deterministic 42-cell grids
2602
- * - CalendarGridCache with LRU (24 months)
2708
+ * Fixed Clock for Deterministic Testing
2603
2709
  *
2604
- * v3.8.0: Range Highlight Cache
2605
- * - RangeHighlighter for decoration logic
2606
- * - RangeHighlighterCache with LRU (48 grids)
2607
- * - Separates grid structure from decorations
2608
- *
2609
- * v3.9.0: Virtual Weeks (Windowed Rendering)
2610
- * - Virtual-weeks logic for reduced DOM complexity
2611
- * - Render only visible weeks (configurable window)
2612
- * - ~50% reduction in DOM nodes with windowSize=3
2613
- *
2614
- * v3.9.2: Cache Bounds (Memory Safety)
2615
- * - MAX_CACHE_ENTRIES = 48 prevents unbounded growth
2616
- * - FIFO eviction for long-running sessions
2617
- * - Critical for ERP, BI, hotel systems
2710
+ * Implements DateClock interface with a fixed date.
2618
2711
  *
2619
2712
  * Usage:
2620
2713
  * ```typescript
2621
- * constructor(
2622
- * private gridCache: CalendarGridCache,
2623
- * private highlighterCache: RangeHighlighterCache
2624
- * ) {}
2625
- *
2626
- * const grid = this.gridCache.get(monthDate, weekStart);
2627
- * const decorated = this.highlighterCache.get(grid, {
2628
- * start, end, hoverDate, disabledDates
2629
- * });
2630
- *
2631
- * // Optional: Windowed rendering (v3.9.0+)
2632
- * const windowSize = 3; // Render only 3 weeks
2633
- * const visibleWeeks = getVisibleWeeks(
2634
- * decorated.weeks,
2635
- * weekStartIndex,
2636
- * windowSize
2637
- * );
2638
- * // decorated.cells[0].iso => '2026-02-01'
2639
- * // decorated.cells[0].isInRange => true
2714
+ * const clock = new FixedClock(new Date(2026, 1, 21)); // Feb 21, 2026
2715
+ * const now = clock.now(); // Always returns Feb 21, 2026 (cloned)
2640
2716
  * ```
2717
+ *
2718
+ * Why clone?
2719
+ * - Prevents test pollution if consumer mutates the returned date
2720
+ * - Each call to now() returns a fresh Date instance
2721
+ */
2722
+ class FixedClock {
2723
+ fixedDate;
2724
+ constructor(fixedDate) {
2725
+ this.fixedDate = fixedDate;
2726
+ }
2727
+ /**
2728
+ * Returns a cloned copy of the fixed date
2729
+ *
2730
+ * Cloning prevents test pollution from mutations
2731
+ */
2732
+ now() {
2733
+ return new Date(this.fixedDate);
2734
+ }
2735
+ }
2736
+
2737
+ /**
2738
+ * Date Test Helpers
2739
+ *
2740
+ * Utilities for creating deterministic dates in tests.
2741
+ */
2742
+ /**
2743
+ * Create a Date in local timezone
2744
+ *
2745
+ * @param year - Full year (e.g., 2026)
2746
+ * @param month - Month 1-12 (natural notation)
2747
+ * @param day - Day of month 1-31
2748
+ * @returns Date at 00:00:00.000 local time
2749
+ *
2750
+ * @example
2751
+ * makeDate(2026, 2, 21) // Feb 21, 2026 00:00:00.000
2752
+ */
2753
+ function makeDate(year, month, day) {
2754
+ return new Date(year, month - 1, day); // month-1 because Date uses 0-indexed months
2755
+ }
2756
+ /**
2757
+ * Create ISO date string (YYYY-MM-DD)
2758
+ *
2759
+ * @param year - Full year (e.g., 2026)
2760
+ * @param month - Month 1-12 (natural notation)
2761
+ * @param day - Day of month 1-31
2762
+ * @returns ISO date string
2763
+ *
2764
+ * @example
2765
+ * iso(2026, 2, 21) // '2026-02-21'
2766
+ */
2767
+ function iso(year, month, day) {
2768
+ const y = String(year).padStart(4, '0');
2769
+ const m = String(month).padStart(2, '0');
2770
+ const d = String(day).padStart(2, '0');
2771
+ return `${y}-${m}-${d}`;
2772
+ }
2773
+ /**
2774
+ * Compare two dates for equality (ignoring time)
2775
+ *
2776
+ * @param a - First date
2777
+ * @param b - Second date
2778
+ * @returns true if same calendar day
2779
+ */
2780
+ function isSameCalendarDay(a, b) {
2781
+ return (a.getFullYear() === b.getFullYear() &&
2782
+ a.getMonth() === b.getMonth() &&
2783
+ a.getDate() === b.getDate());
2784
+ }
2785
+
2786
+ /**
2787
+ * Testing Utilities
2788
+ *
2789
+ * Helpers for deterministic date range testing.
2790
+ */
2791
+
2792
+ /**
2793
+ * INTERNAL API for @oneluiz/dual-datepicker
2794
+ *
2795
+ * This file exports implementation details required by the component itself.
2796
+ * These exports are NOT part of the public API and may change without notice.
2797
+ *
2798
+ * ⚠️ DO NOT import from this file in your application code.
2799
+ * ⚠️ Use the public API from '@oneluiz/dual-datepicker/core' instead.
2800
+ *
2801
+ * @internal
2802
+ * @packageDocumentation
2803
+ * @since v4.0.0
2804
+ */
2805
+ /**
2806
+ * CalendarGridFactory - Deterministic grid generation
2807
+ *
2808
+ * Creates normalized 42-cell grids (6 weeks × 7 days).
2809
+ * Pure function approach with no side effects.
2810
+ *
2811
+ * @internal
2641
2812
  */
2642
2813
 
2643
2814
  class DualDatepickerComponent {
@@ -2645,7 +2816,7 @@ class DualDatepickerComponent {
2645
2816
  placeholder = 'Select date range';
2646
2817
  set startDate(value) {
2647
2818
  if (value) {
2648
- const date = this.dateAdapter.parse(value);
2819
+ const date = this.dateAdapter.parseISODate(value);
2649
2820
  if (date)
2650
2821
  this.rangeStore.setStart(date);
2651
2822
  }
@@ -2656,7 +2827,7 @@ class DualDatepickerComponent {
2656
2827
  }
2657
2828
  set endDate(value) {
2658
2829
  if (value) {
2659
- const date = this.dateAdapter.parse(value);
2830
+ const date = this.dateAdapter.parseISODate(value);
2660
2831
  if (date)
2661
2832
  this.rangeStore.setEnd(date);
2662
2833
  }
@@ -2713,7 +2884,7 @@ class DualDatepickerComponent {
2713
2884
  multiDateRangeChange = new EventEmitter();
2714
2885
  multiDateRangeSelected = new EventEmitter();
2715
2886
  // Date adapter injection
2716
- dateAdapter = inject(DATE_ADAPTER$1);
2887
+ dateAdapter = inject(DATE_ADAPTER);
2717
2888
  // Calendar grid cache (v3.7.0+) - memoizes month grid generation
2718
2889
  gridCache = inject(CalendarGridCache);
2719
2890
  // Range highlighter cache (v3.8.0+) - memoizes decorations
@@ -2722,8 +2893,8 @@ class DualDatepickerComponent {
2722
2893
  rangeStore = inject(DualDateRangeStore);
2723
2894
  // UI-only signals
2724
2895
  showDatePicker = signal(false);
2725
- currentMonth = signal(this.dateAdapter.today());
2726
- previousMonth = signal(this.dateAdapter.today());
2896
+ currentMonth = signal(this.dateAdapter.normalize(new Date()));
2897
+ previousMonth = signal(this.dateAdapter.normalize(new Date()));
2727
2898
  currentMonthDays = signal([]);
2728
2899
  previousMonthDays = signal([]);
2729
2900
  isDisabled = signal(false);
@@ -2936,7 +3107,7 @@ class DualDatepickerComponent {
2936
3107
  }
2937
3108
  else {
2938
3109
  // startDate is not visible, focus on today if visible
2939
- const today = this.dateAdapter.format(this.dateAdapter.today(), 'yyyy-MM-dd');
3110
+ const today = this.dateAdapter.toISODate(this.dateAdapter.normalize(new Date()));
2940
3111
  const inCurrMonth = this.isDateInMonth(today, this.currentMonth());
2941
3112
  if (inCurrMonth) {
2942
3113
  this.focusedDay.set({ date: today, monthIndex: 1 });
@@ -2958,7 +3129,7 @@ class DualDatepickerComponent {
2958
3129
  }
2959
3130
  else {
2960
3131
  // endDate is not visible, focus on today if visible
2961
- const today = this.dateAdapter.format(this.dateAdapter.today(), 'yyyy-MM-dd');
3132
+ const today = this.dateAdapter.toISODate(this.dateAdapter.normalize(new Date()));
2962
3133
  const inCurrMonth = this.isDateInMonth(today, this.currentMonth());
2963
3134
  if (inCurrMonth) {
2964
3135
  this.focusedDay.set({ date: today, monthIndex: 1 });
@@ -2974,7 +3145,7 @@ class DualDatepickerComponent {
2974
3145
  }
2975
3146
  else {
2976
3147
  // Focus on today if visible, otherwise first day of current month
2977
- const today = this.dateAdapter.format(this.dateAdapter.today(), 'yyyy-MM-dd');
3148
+ const today = this.dateAdapter.toISODate(this.dateAdapter.normalize(new Date()));
2978
3149
  const inCurrMonth = this.isDateInMonth(today, this.currentMonth());
2979
3150
  if (inCurrMonth) {
2980
3151
  this.focusedDay.set({ date: today, monthIndex: 1 });
@@ -2990,7 +3161,7 @@ class DualDatepickerComponent {
2990
3161
  }
2991
3162
  }
2992
3163
  isDateInMonth(dateStr, monthDate) {
2993
- const date = this.dateAdapter.parse(dateStr);
3164
+ const date = this.dateAdapter.parseISODate(dateStr);
2994
3165
  if (!date)
2995
3166
  return false;
2996
3167
  const year = this.dateAdapter.getYear(date);
@@ -3005,7 +3176,7 @@ class DualDatepickerComponent {
3005
3176
  this.initializeFocus();
3006
3177
  return;
3007
3178
  }
3008
- const currentDate = this.dateAdapter.parse(focused.date);
3179
+ const currentDate = this.dateAdapter.parseISODate(focused.date);
3009
3180
  if (!currentDate)
3010
3181
  return;
3011
3182
  const newDate = this.dateAdapter.addDays(currentDate, direction);
@@ -3042,7 +3213,7 @@ class DualDatepickerComponent {
3042
3213
  this.initializeFocus();
3043
3214
  return;
3044
3215
  }
3045
- const currentDate = this.dateAdapter.parse(focused.date);
3216
+ const currentDate = this.dateAdapter.parseISODate(focused.date);
3046
3217
  if (!currentDate)
3047
3218
  return;
3048
3219
  const newDate = this.dateAdapter.addDays(currentDate, direction * 7); // Move by week
@@ -3093,17 +3264,17 @@ class DualDatepickerComponent {
3093
3264
  this.initializeFocus();
3094
3265
  return;
3095
3266
  }
3096
- const currentDate = this.dateAdapter.parse(focused.date);
3267
+ const currentDate = this.dateAdapter.parseISODate(focused.date);
3097
3268
  if (!currentDate)
3098
3269
  return;
3099
3270
  const currentYear = this.dateAdapter.getYear(currentDate);
3100
3271
  const currentMonth = this.dateAdapter.getMonth(currentDate);
3101
3272
  const currentDay = this.dateAdapter.getDate(currentDate);
3102
- const newDate = this.dateAdapter.createDate(currentYear + direction, currentMonth, currentDay);
3273
+ const newDate = this.dateAdapter.normalize(new Date(currentYear + direction, currentMonth, currentDay));
3103
3274
  const newDateStr = this.formatDate(newDate);
3104
3275
  // Update months to show the new year
3105
- this.currentMonth.set(this.dateAdapter.createDate(currentYear + direction, currentMonth, 1));
3106
- this.previousMonth.set(this.dateAdapter.createDate(currentYear + direction, currentMonth - 1, 1));
3276
+ this.currentMonth.set(this.dateAdapter.normalize(new Date(currentYear + direction, currentMonth, 1)));
3277
+ this.previousMonth.set(this.dateAdapter.normalize(new Date(currentYear + direction, currentMonth - 1, 1)));
3107
3278
  this.generateCalendars();
3108
3279
  const inPrevMonth = this.isDateInMonth(newDateStr, this.previousMonth());
3109
3280
  this.focusedDay.set({
@@ -3153,12 +3324,12 @@ class DualDatepickerComponent {
3153
3324
  });
3154
3325
  // Initialize dates in store if provided
3155
3326
  if (this.startDate) {
3156
- const date = this.dateAdapter.parse(this.startDate);
3327
+ const date = this.dateAdapter.parseISODate(this.startDate);
3157
3328
  if (date)
3158
3329
  this.rangeStore.setStart(date);
3159
3330
  }
3160
3331
  if (this.endDate) {
3161
- const date = this.dateAdapter.parse(this.endDate);
3332
+ const date = this.dateAdapter.parseISODate(this.endDate);
3162
3333
  if (date)
3163
3334
  this.rangeStore.setEnd(date);
3164
3335
  }
@@ -3170,12 +3341,12 @@ class DualDatepickerComponent {
3170
3341
  if (changes['startDate'] || changes['endDate']) {
3171
3342
  // Sync with store
3172
3343
  if (changes['startDate'] && this.startDate) {
3173
- const date = this.dateAdapter.parse(this.startDate);
3344
+ const date = this.dateAdapter.parseISODate(this.startDate);
3174
3345
  if (date)
3175
3346
  this.rangeStore.setStart(date);
3176
3347
  }
3177
3348
  if (changes['endDate'] && this.endDate) {
3178
- const date = this.dateAdapter.parse(this.endDate);
3349
+ const date = this.dateAdapter.parseISODate(this.endDate);
3179
3350
  if (date)
3180
3351
  this.rangeStore.setEnd(date);
3181
3352
  }
@@ -3203,7 +3374,7 @@ class DualDatepickerComponent {
3203
3374
  formatDateDisplay(dateStr) {
3204
3375
  if (!dateStr)
3205
3376
  return '';
3206
- const date = this.dateAdapter.parse(dateStr);
3377
+ const date = this.dateAdapter.parseISODate(dateStr);
3207
3378
  if (!date)
3208
3379
  return '';
3209
3380
  const year = this.dateAdapter.getYear(date);
@@ -3238,7 +3409,7 @@ class DualDatepickerComponent {
3238
3409
  const currentMonthValue = this.currentMonth();
3239
3410
  const year = this.dateAdapter.getYear(currentMonthValue);
3240
3411
  const month = this.dateAdapter.getMonth(currentMonthValue);
3241
- const previousMonthDate = this.dateAdapter.createDate(year, month - 1, 1);
3412
+ const previousMonthDate = this.dateAdapter.normalize(new Date(year, month - 1, 1));
3242
3413
  this.previousMonth.set(previousMonthDate);
3243
3414
  this.generateCalendars();
3244
3415
  // Initialize keyboard focus only if keyboard navigation is enabled
@@ -3456,7 +3627,7 @@ class DualDatepickerComponent {
3456
3627
  selectDay(dayObj) {
3457
3628
  if (!dayObj.isCurrentMonth || this.isDisabled() || dayObj.isDisabled)
3458
3629
  return;
3459
- const selectedDate = this.dateAdapter.parse(dayObj.date);
3630
+ const selectedDate = this.dateAdapter.parseISODate(dayObj.date);
3460
3631
  if (!selectedDate)
3461
3632
  return;
3462
3633
  if (this.multiRange) {
@@ -3568,11 +3739,11 @@ class DualDatepickerComponent {
3568
3739
  const currentMonthValue = this.currentMonth();
3569
3740
  const year = this.dateAdapter.getYear(currentMonthValue);
3570
3741
  const month = this.dateAdapter.getMonth(currentMonthValue);
3571
- const newCurrentMonth = this.dateAdapter.createDate(year, month + direction, 1);
3742
+ const newCurrentMonth = this.dateAdapter.normalize(new Date(year, month + direction, 1));
3572
3743
  this.currentMonth.set(newCurrentMonth);
3573
3744
  const newYear = this.dateAdapter.getYear(newCurrentMonth);
3574
3745
  const newMonth = this.dateAdapter.getMonth(newCurrentMonth);
3575
- const newPreviousMonth = this.dateAdapter.createDate(newYear, newMonth - 1, 1);
3746
+ const newPreviousMonth = this.dateAdapter.normalize(new Date(newYear, newMonth - 1, 1));
3576
3747
  this.previousMonth.set(newPreviousMonth);
3577
3748
  this.generateCalendars();
3578
3749
  }
@@ -3589,8 +3760,8 @@ class DualDatepickerComponent {
3589
3760
  return;
3590
3761
  }
3591
3762
  const range = preset.getValue();
3592
- const startDate = this.dateAdapter.parse(range.start);
3593
- const endDate = this.dateAdapter.parse(range.end);
3763
+ const startDate = this.dateAdapter.parseISODate(range.start);
3764
+ const endDate = this.dateAdapter.parseISODate(range.end);
3594
3765
  if (startDate && endDate) {
3595
3766
  this.rangeStore.setRange(startDate, endDate);
3596
3767
  }
@@ -3829,8 +4000,8 @@ class DualDatepickerComponent {
3829
4000
  multi: true
3830
4001
  },
3831
4002
  {
3832
- provide: DATE_ADAPTER$1,
3833
- useClass: NativeDateAdapter$1
4003
+ provide: DATE_ADAPTER,
4004
+ useClass: NativeDateAdapter
3834
4005
  },
3835
4006
  {
3836
4007
  provide: APP_INITIALIZER,
@@ -3854,8 +4025,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3854
4025
  multi: true
3855
4026
  },
3856
4027
  {
3857
- provide: DATE_ADAPTER$1,
3858
- useClass: NativeDateAdapter$1
4028
+ provide: DATE_ADAPTER,
4029
+ useClass: NativeDateAdapter
3859
4030
  },
3860
4031
  {
3861
4032
  provide: APP_INITIALIZER,
@@ -3942,550 +4113,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3942
4113
  }] } });
3943
4114
 
3944
4115
  /**
3945
- * Utility functions for creating common date range presets
3946
- * Perfect for dashboards, reporting, POS, BI apps, and ERP systems
3947
- */
3948
- /**
3949
- * Format a date as YYYY-MM-DD string
3950
- */
3951
- function formatDate(date) {
3952
- const year = date.getFullYear();
3953
- const month = String(date.getMonth() + 1).padStart(2, '0');
3954
- const day = String(date.getDate()).padStart(2, '0');
3955
- return `${year}-${month}-${day}`;
3956
- }
3957
- /**
3958
- * Get the start of today
3959
- */
3960
- function getToday() {
3961
- const today = new Date();
3962
- return {
3963
- start: formatDate(today),
3964
- end: formatDate(today)
3965
- };
3966
- }
3967
- /**
3968
- * Get yesterday's date range
3969
- */
3970
- function getYesterday() {
3971
- const yesterday = new Date();
3972
- yesterday.setDate(yesterday.getDate() - 1);
3973
- return {
3974
- start: formatDate(yesterday),
3975
- end: formatDate(yesterday)
3976
- };
3977
- }
3978
- /**
3979
- * Get last N days (including today)
3980
- */
3981
- function getLastNDays(days) {
3982
- const end = new Date();
3983
- const start = new Date();
3984
- start.setDate(start.getDate() - days + 1);
3985
- return {
3986
- start: formatDate(start),
3987
- end: formatDate(end)
3988
- };
3989
- }
3990
- /**
3991
- * Get this week (Monday to Sunday)
3992
- */
3993
- function getThisWeek() {
3994
- const today = new Date();
3995
- const dayOfWeek = today.getDay();
3996
- const start = new Date(today);
3997
- // Adjust to Monday (1) as first day of week
3998
- const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
3999
- start.setDate(start.getDate() - daysToMonday);
4000
- const end = new Date(start);
4001
- end.setDate(end.getDate() + 6);
4002
- return {
4003
- start: formatDate(start),
4004
- end: formatDate(end)
4005
- };
4006
- }
4007
- /**
4008
- * Get last week (Monday to Sunday)
4009
- */
4010
- function getLastWeek() {
4011
- const today = new Date();
4012
- const dayOfWeek = today.getDay();
4013
- const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
4014
- const lastMonday = new Date(today);
4015
- lastMonday.setDate(lastMonday.getDate() - daysToMonday - 7);
4016
- const lastSunday = new Date(lastMonday);
4017
- lastSunday.setDate(lastSunday.getDate() + 6);
4018
- return {
4019
- start: formatDate(lastMonday),
4020
- end: formatDate(lastSunday)
4021
- };
4022
- }
4023
- /**
4024
- * Get this month (1st to last day)
4025
- */
4026
- function getThisMonth() {
4027
- const today = new Date();
4028
- const start = new Date(today.getFullYear(), today.getMonth(), 1);
4029
- const end = new Date(today.getFullYear(), today.getMonth() + 1, 0);
4030
- return {
4031
- start: formatDate(start),
4032
- end: formatDate(end)
4033
- };
4034
- }
4035
- /**
4036
- * Get last month (1st to last day)
4037
- */
4038
- function getLastMonth() {
4039
- const today = new Date();
4040
- const start = new Date(today.getFullYear(), today.getMonth() - 1, 1);
4041
- const end = new Date(today.getFullYear(), today.getMonth(), 0);
4042
- return {
4043
- start: formatDate(start),
4044
- end: formatDate(end)
4045
- };
4046
- }
4047
- /**
4048
- * Get month to date (1st of current month to today)
4049
- */
4050
- function getMonthToDate() {
4051
- const today = new Date();
4052
- const start = new Date(today.getFullYear(), today.getMonth(), 1);
4053
- return {
4054
- start: formatDate(start),
4055
- end: formatDate(today)
4056
- };
4057
- }
4058
- /**
4059
- * Get this quarter (Q1, Q2, Q3, or Q4)
4060
- */
4061
- function getThisQuarter() {
4062
- const today = new Date();
4063
- const currentMonth = today.getMonth();
4064
- const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
4065
- const start = new Date(today.getFullYear(), quarterStartMonth, 1);
4066
- const end = new Date(today.getFullYear(), quarterStartMonth + 3, 0);
4067
- return {
4068
- start: formatDate(start),
4069
- end: formatDate(end)
4070
- };
4071
- }
4072
- /**
4073
- * Get last quarter
4074
- */
4075
- function getLastQuarter() {
4076
- const today = new Date();
4077
- const currentMonth = today.getMonth();
4078
- const lastQuarterStartMonth = Math.floor(currentMonth / 3) * 3 - 3;
4079
- const start = new Date(today.getFullYear(), lastQuarterStartMonth, 1);
4080
- const end = new Date(today.getFullYear(), lastQuarterStartMonth + 3, 0);
4081
- return {
4082
- start: formatDate(start),
4083
- end: formatDate(end)
4084
- };
4085
- }
4086
- /**
4087
- * Get quarter to date (start of current quarter to today)
4088
- */
4089
- function getQuarterToDate() {
4090
- const today = new Date();
4091
- const currentMonth = today.getMonth();
4092
- const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
4093
- const start = new Date(today.getFullYear(), quarterStartMonth, 1);
4094
- return {
4095
- start: formatDate(start),
4096
- end: formatDate(today)
4097
- };
4098
- }
4099
- /**
4100
- * Get this year (January 1 to December 31)
4101
- */
4102
- function getThisYear() {
4103
- const today = new Date();
4104
- const start = new Date(today.getFullYear(), 0, 1);
4105
- const end = new Date(today.getFullYear(), 11, 31);
4106
- return {
4107
- start: formatDate(start),
4108
- end: formatDate(end)
4109
- };
4110
- }
4111
- /**
4112
- * Get last year
4113
- */
4114
- function getLastYear() {
4115
- const today = new Date();
4116
- const start = new Date(today.getFullYear() - 1, 0, 1);
4117
- const end = new Date(today.getFullYear() - 1, 11, 31);
4118
- return {
4119
- start: formatDate(start),
4120
- end: formatDate(end)
4121
- };
4122
- }
4123
- /**
4124
- * Get year to date (January 1 to today)
4125
- */
4126
- function getYearToDate() {
4127
- const today = new Date();
4128
- const start = new Date(today.getFullYear(), 0, 1);
4129
- return {
4130
- start: formatDate(start),
4131
- end: formatDate(today)
4132
- };
4133
- }
4134
- /**
4135
- * Get last N months (including current partial month)
4136
- */
4137
- function getLastNMonths(months) {
4138
- const today = new Date();
4139
- const start = new Date(today);
4140
- start.setMonth(start.getMonth() - months);
4141
- return {
4142
- start: formatDate(start),
4143
- end: formatDate(today)
4144
- };
4145
- }
4146
- /**
4147
- * Get last N years
4148
- */
4149
- function getLastNYears(years) {
4150
- const today = new Date();
4151
- const start = new Date(today);
4152
- start.setFullYear(start.getFullYear() - years);
4153
- return {
4154
- start: formatDate(start),
4155
- end: formatDate(today)
4156
- };
4157
- }
4158
- /**
4159
- * Pre-built preset configurations for common use cases
4160
- * Import and use these directly in your component
4161
- */
4162
- const CommonPresets = {
4163
- /**
4164
- * Dashboard presets - Perfect for analytics dashboards
4165
- */
4166
- dashboard: [
4167
- { label: 'Today', getValue: getToday },
4168
- { label: 'Yesterday', getValue: getYesterday },
4169
- { label: 'Last 7 days', getValue: () => getLastNDays(7) },
4170
- { label: 'Last 30 days', getValue: () => getLastNDays(30) },
4171
- { label: 'This month', getValue: getThisMonth },
4172
- { label: 'Last month', getValue: getLastMonth }
4173
- ],
4174
- /**
4175
- * Reporting presets - Perfect for business reporting
4176
- */
4177
- reporting: [
4178
- { label: 'Today', getValue: getToday },
4179
- { label: 'This week', getValue: getThisWeek },
4180
- { label: 'Last week', getValue: getLastWeek },
4181
- { label: 'This month', getValue: getThisMonth },
4182
- { label: 'Last month', getValue: getLastMonth },
4183
- { label: 'This quarter', getValue: getThisQuarter },
4184
- { label: 'Last quarter', getValue: getLastQuarter }
4185
- ],
4186
- /**
4187
- * Financial presets - Perfect for ERP and accounting systems
4188
- */
4189
- financial: [
4190
- { label: 'Month to date', getValue: getMonthToDate },
4191
- { label: 'Quarter to date', getValue: getQuarterToDate },
4192
- { label: 'Year to date', getValue: getYearToDate },
4193
- { label: 'Last month', getValue: getLastMonth },
4194
- { label: 'Last quarter', getValue: getLastQuarter },
4195
- { label: 'Last year', getValue: getLastYear }
4196
- ],
4197
- /**
4198
- * Analytics presets - Perfect for BI and data analysis
4199
- */
4200
- analytics: [
4201
- { label: 'Last 7 days', getValue: () => getLastNDays(7) },
4202
- { label: 'Last 14 days', getValue: () => getLastNDays(14) },
4203
- { label: 'Last 30 days', getValue: () => getLastNDays(30) },
4204
- { label: 'Last 60 days', getValue: () => getLastNDays(60) },
4205
- { label: 'Last 90 days', getValue: () => getLastNDays(90) },
4206
- { label: 'Last 180 days', getValue: () => getLastNDays(180) },
4207
- { label: 'Last 365 days', getValue: () => getLastNDays(365) }
4208
- ],
4209
- /**
4210
- * Simple presets - Basic common ranges
4211
- */
4212
- simple: [
4213
- { label: 'Today', getValue: getToday },
4214
- { label: 'Last 7 days', getValue: () => getLastNDays(7) },
4215
- { label: 'Last 30 days', getValue: () => getLastNDays(30) },
4216
- { label: 'This year', getValue: getThisYear }
4217
- ]
4218
- };
4219
-
4220
- /**
4221
- * Provider Functions for Built-in Presets
4222
- *
4223
- * Version: 3.6.0
4224
- *
4225
- * Automatic registration of built-in date range presets.
4226
- * These providers ensure backward compatibility by auto-registering
4227
- * all standard presets (TODAY, LAST_7_DAYS, THIS_MONTH, etc.)
4228
- *
4229
- * USAGE IN LIBRARY (Internal):
4230
- * Built-in presets are registered automatically via Angular providers.
4231
- * Library consumers don't need to do anything.
4232
- *
4233
- * USAGE IN APP (Custom Presets):
4234
- * ```typescript
4235
- * // app.config.ts
4236
- * export const appConfig: ApplicationConfig = {
4237
- * providers: [
4238
- * // ... other providers
4239
- * provideCustomPresets([
4240
- * {
4241
- * key: 'THIS_FISCAL_QUARTER',
4242
- * resolve: (clock, adapter) => {
4243
- * const now = clock.now();
4244
- * // ... fiscal logic
4245
- * return { start, end };
4246
- * }
4247
- * }
4248
- * ])
4249
- * ]
4250
- * };
4251
- * ```
4252
- */
4253
- /**
4254
- * Initializer function that registers built-in presets
4255
- *
4256
- * Runs at application startup (APP_INITIALIZER)
4257
- *
4258
- * @param registry - PresetRegistry instance
4259
- * @returns Initialization function
4260
- */
4261
- function initializeBuiltInPresets(registry) {
4262
- return () => {
4263
- // Register all built-in presets
4264
- BUILT_IN_PRESETS.forEach(preset => {
4265
- registry.register(preset);
4266
- });
4267
- // Log registration for debugging (can be removed in production)
4268
- if (typeof console !== 'undefined' && console.debug) {
4269
- console.debug(`[ng-dual-datepicker] Registered ${BUILT_IN_PRESETS.length} built-in presets:`, BUILT_IN_PRESETS.map(p => p.key).join(', '));
4270
- }
4271
- };
4272
- }
4273
- /**
4274
- * Provide built-in date range presets
4275
- *
4276
- * This provider is automatically included in the library's root providers.
4277
- * Library consumers don't need to add this manually.
4278
- *
4279
- * @returns EnvironmentProviders for built-in presets
4280
- *
4281
- * @internal
4282
- */
4283
- function provideBuiltInPresets() {
4284
- return makeEnvironmentProviders([
4285
- {
4286
- provide: APP_INITIALIZER,
4287
- multi: true,
4288
- useFactory: initializeBuiltInPresets,
4289
- deps: [PresetRegistry]
4290
- }
4291
- ]);
4292
- }
4293
- /**
4294
- * Provide custom date range presets
4295
- *
4296
- * Use this to register your own industry-specific presets:
4297
- * - Fiscal presets
4298
- * - Hotel/hospitality presets
4299
- * - Logistics presets
4300
- * - Custom business logic
4301
- *
4302
- * @param presets - Array of custom RangePresetPlugin implementations
4303
- * @returns EnvironmentProviders for custom presets
4304
- *
4305
- * @example
4306
- * ```typescript
4307
- * // Fiscal presets
4308
- * const FISCAL_PRESETS: RangePresetPlugin[] = [
4309
- * {
4310
- * key: 'THIS_FISCAL_QUARTER',
4311
- * resolve: (clock, adapter) => {
4312
- * const now = clock.now();
4313
- * const month = adapter.getMonth(now);
4314
- *
4315
- * // Fiscal year starts April (month 3)
4316
- * const fiscalMonth = (month + 9) % 12;
4317
- * const quarterStart = Math.floor(fiscalMonth / 3) * 3;
4318
- * const calendarMonth = (quarterStart - 9 + 12) % 12;
4319
- *
4320
- * const year = adapter.getYear(now);
4321
- * const fiscalYear = month < 3 ? year - 1 : year;
4322
- *
4323
- * const start = new Date(fiscalYear, calendarMonth, 1);
4324
- * const end = new Date(fiscalYear, calendarMonth + 3, 0);
4325
- *
4326
- * return {
4327
- * start: adapter.normalize(start),
4328
- * end: adapter.normalize(end)
4329
- * };
4330
- * }
4331
- * },
4332
- * {
4333
- * key: 'FISCAL_YEAR_TO_DATE',
4334
- * resolve: (clock, adapter) => {
4335
- * const now = clock.now();
4336
- * const month = adapter.getMonth(now);
4337
- * const year = adapter.getYear(now);
4338
- *
4339
- * // Fiscal year starts April 1
4340
- * const fiscalYearStart = month >= 3
4341
- * ? new Date(year, 3, 1)
4342
- * : new Date(year - 1, 3, 1);
4343
- *
4344
- * return {
4345
- * start: adapter.normalize(fiscalYearStart),
4346
- * end: adapter.normalize(now)
4347
- * };
4348
- * }
4349
- * }
4350
- * ];
4351
- *
4352
- * // In app.config.ts
4353
- * export const appConfig: ApplicationConfig = {
4354
- * providers: [
4355
- * provideCustomPresets(FISCAL_PRESETS)
4356
- * ]
4357
- * };
4358
- *
4359
- * // Use in components
4360
- * store.applyPreset('THIS_FISCAL_QUARTER');
4361
- * store.applyPreset('FISCAL_YEAR_TO_DATE');
4362
- * ```
4363
- *
4364
- * @example
4365
- * ```typescript
4366
- * // Hotel presets
4367
- * const HOTEL_PRESETS: RangePresetPlugin[] = [
4368
- * {
4369
- * key: 'CHECK_IN_WEEK',
4370
- * resolve: (clock, adapter) => {
4371
- * const now = clock.now();
4372
- * const dayOfWeek = adapter.getDayOfWeek(now);
4373
- *
4374
- * // Check-in week: Friday to Friday
4375
- * const daysToNextFriday = dayOfWeek <= 5
4376
- * ? 5 - dayOfWeek
4377
- * : 7 - dayOfWeek + 5;
4378
- *
4379
- * const nextFriday = adapter.addDays(now, daysToNextFriday);
4380
- * const followingFriday = adapter.addDays(nextFriday, 7);
4381
- *
4382
- * return { start: nextFriday, end: followingFriday };
4383
- * }
4384
- * },
4385
- * {
4386
- * key: 'NEXT_30_NIGHTS',
4387
- * resolve: (clock, adapter) => {
4388
- * const now = clock.now();
4389
- * const tomorrow = adapter.addDays(now, 1);
4390
- * const end = adapter.addDays(tomorrow, 30);
4391
- * return { start: tomorrow, end };
4392
- * }
4393
- * }
4394
- * ];
4395
- *
4396
- * providers: [provideCustomPresets(HOTEL_PRESETS)]
4397
- * ```
4398
- */
4399
- function provideCustomPresets(presets) {
4400
- return makeEnvironmentProviders([
4401
- {
4402
- provide: APP_INITIALIZER,
4403
- multi: true,
4404
- useFactory: (registry) => {
4405
- return () => {
4406
- presets.forEach(preset => {
4407
- registry.register(preset);
4408
- });
4409
- if (typeof console !== 'undefined' && console.debug) {
4410
- console.debug(`[ng-dual-datepicker] Registered ${presets.length} custom presets:`, presets.map(p => p.key).join(', '));
4411
- }
4412
- };
4413
- },
4414
- deps: [PresetRegistry]
4415
- }
4416
- ]);
4417
- }
4418
- /**
4419
- * Provide preset package
4420
- *
4421
- * Convenience function for external preset packages.
4422
- *
4423
- * @param packageName - Name of the preset package (for logging)
4424
- * @param presets - Array of presets from the package
4425
- * @returns EnvironmentProviders
4426
- *
4427
- * @example
4428
- * ```typescript
4429
- * // @acme/fiscal-presets package
4430
- * export function provideFiscalPresets(): EnvironmentProviders {
4431
- * return providePresetPackage('@acme/fiscal-presets', FISCAL_PRESETS);
4432
- * }
4116
+ * Public API Surface of @oneluiz/dual-datepicker
4433
4117
  *
4434
- * // In app
4435
- * import { provideFiscalPresets } from '@acme/fiscal-presets';
4118
+ * v4.0.0: Formalized Public API
4119
+ * - Only stable, documented APIs are exported
4120
+ * - Internal implementation details removed
4121
+ * - Backward compatibility maintained where possible
4436
4122
  *
4437
- * providers: [provideFiscalPresets()]
4438
- * ```
4123
+ * @public
4124
+ * @packageDocumentation
4125
+ * @module @oneluiz/dual-datepicker
4439
4126
  */
4440
- function providePresetPackage(packageName, presets) {
4441
- return makeEnvironmentProviders([
4442
- {
4443
- provide: APP_INITIALIZER,
4444
- multi: true,
4445
- useFactory: (registry) => {
4446
- return () => {
4447
- presets.forEach(preset => {
4448
- registry.register(preset);
4449
- });
4450
- if (typeof console !== 'undefined' && console.debug) {
4451
- console.debug(`[${packageName}] Registered ${presets.length} presets:`, presets.map(p => p.key).join(', '));
4452
- }
4453
- };
4454
- },
4455
- deps: [PresetRegistry]
4456
- }
4457
- ]);
4458
- }
4459
-
4127
+ // ============================================================================
4128
+ // UI COMPONENT
4129
+ // ============================================================================
4460
4130
  /**
4461
- * Core headless date range logic
4131
+ * DualDatepickerComponent - Main Angular component
4462
4132
  *
4463
- * v3.6.0: Plugin Architecture
4464
- * - RangePresetPlugin interface for extensible presets
4465
- * - PresetRegistry for plugin management
4466
- * - Built-in presets as plugins
4467
- * - Provider functions for custom presets
4133
+ * Standalone component for dual-calendar date range selection.
4134
+ * Supports reactive forms, signals, themes, and extensive customization.
4468
4135
  *
4469
- * v3.7.0: Calendar Grid Cache
4470
- * - CalendarGridFactory for deterministic grid generation
4471
- * - CalendarGridCache with LRU memoization
4472
- * - Separates grid structure from decorations for performance
4473
- *
4474
- * v3.8.0: Range Highlight Cache
4475
- * - RangeHighlighter for decoration logic (pure computation)
4476
- * - RangeHighlighterCache with LRU (48 grids)
4477
- * - Eliminates redundant decoration recomputations
4478
- *
4479
- * Import from here for clean barrel exports
4480
- */
4481
-
4482
- /**
4483
- * Public API Surface of @oneluiz/dual-datepicker
4136
+ * @public
4137
+ * @since v1.0.0
4484
4138
  */
4485
4139
 
4486
4140
  /**
4487
4141
  * Generated bundle index. Do not edit.
4488
4142
  */
4489
4143
 
4490
- export { BUILT_IN_PRESETS, CalendarGridCache, CalendarGridFactory, CommonPresets, DATE_ADAPTER$1 as DATE_ADAPTER, DATE_CLOCK, DateAdapter, DualDateRangeStore, DualDatepickerComponent, LAST_14_DAYS_PRESET, LAST_30_DAYS_PRESET, LAST_60_DAYS_PRESET, LAST_7_DAYS_PRESET, LAST_90_DAYS_PRESET, LAST_MONTH_PRESET, LAST_QUARTER_PRESET, LAST_WEEK_PRESET, LAST_YEAR_PRESET, MAX_CACHE_ENTRIES, MONTH_TO_DATE_PRESET, NativeDateAdapter$1 as NativeDateAdapter, PresetEngine, PresetRegistry, QUARTER_TO_DATE_PRESET, RangeHighlighter, RangeHighlighterCache, SystemClock, THIS_MONTH_PRESET, THIS_QUARTER_PRESET, THIS_WEEK_PRESET, THIS_YEAR_PRESET, TODAY_PRESET, YEAR_TO_DATE_PRESET, YESTERDAY_PRESET, applyBounds, clampWeekStart, createPreset, formatISODate, getLastMonth, getLastNDays, getLastNMonths, getLastNYears, getLastQuarter, getLastWeek, getLastYear, getMonthToDate, getQuarterToDate, getThisMonth, getThisQuarter, getThisWeek, getThisYear, getToday, getVirtualWeekWindow, getVisibleWeeks, getYearToDate, getYesterday, isDateDisabled, isRangePresetPlugin, isVirtualWeeksEnabled, navigateWeekWindow, parseISODate, presetEngine, provideBuiltInPresets, provideCustomPresets, providePresetPackage, validateDateBounds, validateRangeBounds, validateRangeOrder };
4144
+ export { BUILT_IN_PRESETS, DATE_ADAPTER, DualDateRangeStore, DualDatepickerComponent, LAST_30_DAYS_PRESET, LAST_7_DAYS_PRESET, LAST_MONTH_PRESET, LAST_WEEK_PRESET, LAST_YEAR_PRESET, NativeDateAdapter, PresetEngine, PresetRegistry, THIS_MONTH_PRESET, THIS_WEEK_PRESET, THIS_YEAR_PRESET, TODAY_PRESET, YESTERDAY_PRESET, applyBounds, isDateDisabled, provideCustomPresets, providePresetPackage, validateRangeOrder };
4491
4145
  //# sourceMappingURL=oneluiz-dual-datepicker.mjs.map