@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.
- package/dual-datepicker.component.d.ts +4 -4
- package/esm2022/core/internal.mjs +92 -0
- package/esm2022/core/public.mjs +94 -0
- package/esm2022/core/testing/date.helpers.mjs +49 -0
- package/esm2022/core/testing/fixed-clock.mjs +30 -0
- package/esm2022/core/testing/index.mjs +8 -0
- package/esm2022/dual-datepicker.component.mjs +34 -32
- package/esm2022/public-api.mjs +39 -9
- package/fesm2022/oneluiz-dual-datepicker.mjs +629 -975
- package/fesm2022/oneluiz-dual-datepicker.mjs.map +1 -1
- package/package.json +11 -1
- package/public-api.d.ts +39 -4
- package/core/built-in-presets.d.ts +0 -98
- package/core/calendar-grid/cache.config.d.ts +0 -34
- package/core/calendar-grid/calendar-grid.cache.d.ts +0 -39
- package/core/calendar-grid/calendar-grid.factory.d.ts +0 -26
- package/core/calendar-grid/calendar-grid.types.d.ts +0 -57
- package/core/calendar-grid/index.d.ts +0 -56
- package/core/calendar-grid/range-highlighter.cache.d.ts +0 -106
- package/core/calendar-grid/range-highlighter.d.ts +0 -85
- package/core/calendar-grid/range-highlighter.types.d.ts +0 -182
- package/core/calendar-grid/virtual-weeks.logic.d.ts +0 -116
- package/core/calendar-grid/virtual-weeks.types.d.ts +0 -71
- package/core/date-adapter.d.ts +0 -298
- package/core/date-clock.d.ts +0 -82
- package/core/dual-date-range.store.d.ts +0 -113
- package/core/index.d.ts +0 -33
- package/core/native-date-adapter.d.ts +0 -152
- package/core/preset-providers.d.ts +0 -176
- package/core/preset-registry.d.ts +0 -181
- package/core/preset.engine.d.ts +0 -124
- package/core/range-preset.plugin.d.ts +0 -188
- package/core/range.validator.d.ts +0 -37
- package/core/system-clock.d.ts +0 -13
- package/date-adapter.d.ts +0 -116
- package/esm2022/core/calendar-grid/index.mjs +0 -57
- package/esm2022/core/index.mjs +0 -37
- package/esm2022/date-adapter.mjs +0 -12
- package/esm2022/native-date-adapter.mjs +0 -117
- package/esm2022/preset-utils.mjs +0 -276
- package/native-date-adapter.d.ts +0 -26
- package/preset-utils.d.ts +0 -91
- package/src/themes/bootstrap.scss +0 -202
- package/src/themes/bulma.scss +0 -209
- package/src/themes/custom.scss +0 -236
- package/src/themes/foundation.scss +0 -201
- 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,
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1823
|
-
*
|
|
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
|
-
*
|
|
1728
|
+
* Initializer function that registers built-in presets
|
|
1828
1729
|
*
|
|
1829
|
-
*
|
|
1830
|
-
* timezone-safe date operations.
|
|
1730
|
+
* Runs at application startup (APP_INITIALIZER)
|
|
1831
1731
|
*
|
|
1832
|
-
*
|
|
1833
|
-
*
|
|
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
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1748
|
+
* Provide built-in date range presets
|
|
1922
1749
|
*
|
|
1923
|
-
*
|
|
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
|
-
* @
|
|
1926
|
-
*
|
|
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
|
-
*
|
|
1768
|
+
* Provide custom date range presets
|
|
1930
1769
|
*
|
|
1931
|
-
*
|
|
1932
|
-
* -
|
|
1933
|
-
* -
|
|
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
|
-
*
|
|
1936
|
-
*
|
|
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
|
-
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
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
|
-
*
|
|
1945
|
-
*
|
|
1946
|
-
*
|
|
1947
|
-
* -
|
|
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
|
-
*
|
|
1951
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
1893
|
+
* Provide preset package
|
|
1957
1894
|
*
|
|
1958
|
-
*
|
|
1959
|
-
* multiple times when only decorations (selected dates, hover, etc.) change.
|
|
1895
|
+
* Convenience function for external preset packages.
|
|
1960
1896
|
*
|
|
1961
|
-
*
|
|
1962
|
-
* -
|
|
1963
|
-
*
|
|
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
|
-
*
|
|
1967
|
-
*
|
|
1968
|
-
* -
|
|
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
|
-
*
|
|
1971
|
-
*
|
|
1972
|
-
*
|
|
1973
|
-
*
|
|
1908
|
+
* // In app
|
|
1909
|
+
* import { provideFiscalPresets } from '@acme/fiscal-presets';
|
|
1910
|
+
*
|
|
1911
|
+
* providers: [provideFiscalPresets()]
|
|
1912
|
+
* ```
|
|
1974
1913
|
*/
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1935
|
+
* PUBLIC API for @oneluiz/dual-datepicker
|
|
2051
1936
|
*
|
|
2052
|
-
*
|
|
2053
|
-
*
|
|
1937
|
+
* This file defines the stable public API contract.
|
|
1938
|
+
* Exports from this barrel are guaranteed to follow semantic versioning.
|
|
2054
1939
|
*
|
|
2055
|
-
* @
|
|
2056
|
-
* @
|
|
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
|
-
*
|
|
1949
|
+
* DualDateRangeStore - Signal-based headless date range store
|
|
2061
1950
|
*
|
|
2062
|
-
*
|
|
2063
|
-
*
|
|
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
|
-
* @
|
|
2066
|
-
* @
|
|
1954
|
+
* @public
|
|
1955
|
+
* @since v3.5.0
|
|
2067
1956
|
*/
|
|
1957
|
+
|
|
2068
1958
|
/**
|
|
2069
|
-
*
|
|
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
|
-
*
|
|
2078
|
-
* -
|
|
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
|
-
*
|
|
2084
|
-
*
|
|
2085
|
-
*
|
|
2086
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
2622
|
-
*
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
2726
|
-
previousMonth = signal(this.dateAdapter.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
3106
|
-
this.previousMonth.set(this.dateAdapter.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
3593
|
-
const endDate = this.dateAdapter.
|
|
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
|
|
3833
|
-
useClass: NativeDateAdapter
|
|
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
|
|
3858
|
-
useClass: NativeDateAdapter
|
|
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
|
-
*
|
|
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
|
-
*
|
|
4435
|
-
*
|
|
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
|
-
*
|
|
4438
|
-
*
|
|
4123
|
+
* @public
|
|
4124
|
+
* @packageDocumentation
|
|
4125
|
+
* @module @oneluiz/dual-datepicker
|
|
4439
4126
|
*/
|
|
4440
|
-
|
|
4441
|
-
|
|
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
|
-
*
|
|
4131
|
+
* DualDatepickerComponent - Main Angular component
|
|
4462
4132
|
*
|
|
4463
|
-
*
|
|
4464
|
-
*
|
|
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
|
-
*
|
|
4470
|
-
*
|
|
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,
|
|
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
|