@tylertech/forge 3.9.2 → 3.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +169 -11
- package/dist/lib.js +22 -22
- package/dist/lib.js.map +3 -3
- package/dist/vscode.html-custom-data.json +20 -0
- package/esm/app-bar/profile-button/app-bar-profile-button-adapter.js +1 -1
- package/esm/core/mask/date-input-mask.js +18 -1
- package/esm/core/utils/date-utils.d.ts +14 -5
- package/esm/core/utils/date-utils.js +228 -69
- package/esm/date-picker/base/base-date-picker-constants.d.ts +19 -4
- package/esm/date-picker/base/base-date-picker-constants.js +20 -1
- package/esm/date-picker/base/base-date-picker-core.d.ts +11 -3
- package/esm/date-picker/base/base-date-picker-core.js +68 -13
- package/esm/date-picker/base/base-date-picker.d.ts +10 -2
- package/esm/date-picker/base/base-date-picker.js +17 -1
- package/esm/date-picker/date-picker-core.js +1 -1
- package/esm/date-range-picker/date-range-picker-core.d.ts +1 -0
- package/esm/date-range-picker/date-range-picker-core.js +14 -4
- package/esm/floating-action-button/floating-action-button-adapter.d.ts +0 -4
- package/esm/floating-action-button/floating-action-button-adapter.js +3 -24
- package/esm/floating-action-button/floating-action-button-core.d.ts +0 -1
- package/esm/floating-action-button/floating-action-button-core.js +0 -3
- package/esm/floating-action-button/floating-action-button.d.ts +0 -1
- package/esm/floating-action-button/floating-action-button.js +0 -3
- package/esm/list/list/list.js +1 -1
- package/esm/list/list-item/list-item.js +1 -1
- package/esm/split-view/split-view-panel/split-view-panel.js +1 -1
- package/esm/time-picker/time-picker-core.d.ts +1 -0
- package/esm/time-picker/time-picker-core.js +80 -16
- package/package.json +1 -1
- package/sass/list/list/list.scss +4 -2
- package/sass/list/list-item/list-item.scss +3 -1
|
@@ -672,6 +672,11 @@
|
|
|
672
672
|
"description": "Whether the mask format is displayed in the input or not. Only applies if `masked` is `true`.",
|
|
673
673
|
"values": []
|
|
674
674
|
},
|
|
675
|
+
{
|
|
676
|
+
"name": "date-format",
|
|
677
|
+
"description": "The date format to use for the date picker.",
|
|
678
|
+
"values": [{ "name": "DatePickerDateFormat" }]
|
|
679
|
+
},
|
|
675
680
|
{
|
|
676
681
|
"name": "show-today",
|
|
677
682
|
"description": "Whether the today button is visible in the popup.",
|
|
@@ -682,6 +687,11 @@
|
|
|
682
687
|
"description": "The type for the `value` property and `forge-date-picker-change` event.",
|
|
683
688
|
"values": [{ "name": "DatePickerValueMode" }]
|
|
684
689
|
},
|
|
690
|
+
{
|
|
691
|
+
"name": "shortcuts",
|
|
692
|
+
"description": "When specifies as an attribute you can only use the `off` value to disable shortcuts. Otherwise, you can use the `shortcuts` property to set your own shortcuts.",
|
|
693
|
+
"values": [{ "name": "DatePickerShortcuts" }]
|
|
694
|
+
},
|
|
685
695
|
{ "name": "year-range", "description": "The year range.", "values": [] }
|
|
686
696
|
],
|
|
687
697
|
"references": []
|
|
@@ -751,6 +761,11 @@
|
|
|
751
761
|
"description": "Whether the mask format is displayed in the input or not. Only applies if `masked` is `true`.",
|
|
752
762
|
"values": []
|
|
753
763
|
},
|
|
764
|
+
{
|
|
765
|
+
"name": "date-format",
|
|
766
|
+
"description": "The date format to use for the date picker.",
|
|
767
|
+
"values": [{ "name": "DatePickerDateFormat" }]
|
|
768
|
+
},
|
|
754
769
|
{
|
|
755
770
|
"name": "show-today",
|
|
756
771
|
"description": "Whether the today button is visible in the popup.",
|
|
@@ -761,6 +776,11 @@
|
|
|
761
776
|
"description": "The type for the `value` property and `forge-date-picker-change` event.",
|
|
762
777
|
"values": [{ "name": "DatePickerValueMode" }]
|
|
763
778
|
},
|
|
779
|
+
{
|
|
780
|
+
"name": "shortcuts",
|
|
781
|
+
"description": "When specifies as an attribute you can only use the `off` value to disable shortcuts. Otherwise, you can use the `shortcuts` property to set your own shortcuts.",
|
|
782
|
+
"values": [{ "name": "DatePickerShortcuts" }]
|
|
783
|
+
},
|
|
764
784
|
{ "name": "year-range", "description": "The year range.", "values": [] }
|
|
765
785
|
],
|
|
766
786
|
"references": []
|
|
@@ -87,7 +87,7 @@ export class AppBarProfileButtonAdapter extends BaseAdapter {
|
|
|
87
87
|
async closePopup() {
|
|
88
88
|
if (this._popupElement) {
|
|
89
89
|
await this._popupElement.hideAsync();
|
|
90
|
-
this._popupElement
|
|
90
|
+
this._popupElement?.remove();
|
|
91
91
|
this._popupElement = undefined;
|
|
92
92
|
this._profileCardElement = undefined;
|
|
93
93
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* License: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { isNumeric } from '@tylertech/forge-core';
|
|
7
|
-
import { InputMask, MaskedRange, createMask } from 'imask';
|
|
7
|
+
import { InputMask, MaskedEnum, MaskedRange, createMask } from 'imask';
|
|
8
8
|
export const DEFAULT_DATE_MASK = '0`0{/}`0`0{/}`0`0`0`0';
|
|
9
9
|
export class DateInputMask {
|
|
10
10
|
constructor(_element, _options = {}) {
|
|
@@ -49,6 +49,16 @@ export class DateInputMask {
|
|
|
49
49
|
to: 12,
|
|
50
50
|
maxLength: 2
|
|
51
51
|
},
|
|
52
|
+
Mmm: {
|
|
53
|
+
mask: MaskedEnum,
|
|
54
|
+
enum: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
|
55
|
+
matchValue: (enumStr, inputStr, matchFrom) => MaskedEnum.DEFAULTS.matchValue(enumStr.toLowerCase(), inputStr.toLowerCase(), matchFrom)
|
|
56
|
+
},
|
|
57
|
+
MMM: {
|
|
58
|
+
mask: MaskedEnum,
|
|
59
|
+
enum: ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'],
|
|
60
|
+
matchValue: (enumStr, inputStr, matchFrom) => MaskedEnum.DEFAULTS.matchValue(enumStr.toLowerCase(), inputStr.toLowerCase(), matchFrom)
|
|
61
|
+
},
|
|
52
62
|
DD: {
|
|
53
63
|
mask: MaskedRange,
|
|
54
64
|
autofix: true,
|
|
@@ -62,6 +72,13 @@ export class DateInputMask {
|
|
|
62
72
|
from: 0,
|
|
63
73
|
to: 9999,
|
|
64
74
|
maxLength: 4
|
|
75
|
+
},
|
|
76
|
+
YY: {
|
|
77
|
+
mask: MaskedRange,
|
|
78
|
+
autofix: true,
|
|
79
|
+
from: 0,
|
|
80
|
+
to: 99,
|
|
81
|
+
maxLength: 2
|
|
65
82
|
}
|
|
66
83
|
}
|
|
67
84
|
};
|
|
@@ -3,18 +3,27 @@
|
|
|
3
3
|
* Copyright Tyler Technologies, Inc.
|
|
4
4
|
* License: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
export type SupportedDateFormats = 'MM/DD/YYYY' | 'MM/DD/YY' | 'DD/MMM/YYYY' | 'MM-DD-YYYY' | 'MM-DD-YY' | 'DD-MMM-YYYY' | 'YYYY-MM-DD' | 'YYYY-MMM-DD' | 'DD.MM.YYYY' | 'DD.MM.YY';
|
|
6
7
|
export declare const ISO_8601_REGEX: RegExp;
|
|
7
8
|
export declare const ISO_TIMEZONE_REGEX: RegExp;
|
|
8
9
|
/**
|
|
9
10
|
* Parses a date string value to a `Date` object in local time.
|
|
10
|
-
*
|
|
11
|
+
* Supports multiple formats as defined in SupportedDateFormats.
|
|
12
|
+
*
|
|
13
|
+
* @param value The date string value to parse
|
|
14
|
+
* @param format Optional format hint for better parsing accuracy
|
|
15
|
+
* @returns Parsed Date object or null if parsing fails
|
|
11
16
|
*/
|
|
12
|
-
export declare function parseDateString(value: string): Date | null;
|
|
17
|
+
export declare function parseDateString(value: string, format?: SupportedDateFormats): Date | null;
|
|
13
18
|
/**
|
|
14
|
-
* Formats a
|
|
15
|
-
*
|
|
19
|
+
* Formats a Date object to a specified string format.
|
|
20
|
+
*
|
|
21
|
+
* @param date The Date object to format
|
|
22
|
+
* @param format The desired output format (defaults to 'MM/DD/YYYY')
|
|
23
|
+
* @returns Formatted date string
|
|
24
|
+
* @throws Error if date is invalid or format is unsupported
|
|
16
25
|
*/
|
|
17
|
-
export declare function formatDate(date: Date): string;
|
|
26
|
+
export declare function formatDate(date: Date, format?: SupportedDateFormats): string;
|
|
18
27
|
/**
|
|
19
28
|
* Determines if two date objects are equal.
|
|
20
29
|
*
|
|
@@ -7,89 +7,248 @@ import { isValidDate } from '@tylertech/forge-core';
|
|
|
7
7
|
export const ISO_8601_REGEX = /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
|
|
8
8
|
export const ISO_TIMEZONE_REGEX = /a-z/i;
|
|
9
9
|
const FUTURE_YEAR_COERCION = 10; // The number of years in the future to coerce two-digit years into current century
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
const MONTH_ABBREVIATIONS = {
|
|
11
|
+
JAN: 1,
|
|
12
|
+
FEB: 2,
|
|
13
|
+
MAR: 3,
|
|
14
|
+
APR: 4,
|
|
15
|
+
MAY: 5,
|
|
16
|
+
JUN: 6,
|
|
17
|
+
JUL: 7,
|
|
18
|
+
AUG: 8,
|
|
19
|
+
SEP: 9,
|
|
20
|
+
OCT: 10,
|
|
21
|
+
NOV: 11,
|
|
22
|
+
DEC: 12
|
|
23
|
+
};
|
|
24
|
+
const MONTH_NAMES = {
|
|
25
|
+
1: 'JAN',
|
|
26
|
+
2: 'FEB',
|
|
27
|
+
3: 'MAR',
|
|
28
|
+
4: 'APR',
|
|
29
|
+
5: 'MAY',
|
|
30
|
+
6: 'JUN',
|
|
31
|
+
7: 'JUL',
|
|
32
|
+
8: 'AUG',
|
|
33
|
+
9: 'SEP',
|
|
34
|
+
10: 'OCT',
|
|
35
|
+
11: 'NOV',
|
|
36
|
+
12: 'DEC'
|
|
37
|
+
};
|
|
38
|
+
const DATE_PATTERNS = [
|
|
39
|
+
// YYYY-MM-DD (ISO-like numeric)
|
|
40
|
+
{
|
|
41
|
+
regex: /^(\d{4})-(\d{1,2})-(\d{1,2})$/,
|
|
42
|
+
parser: ([year, month, day]) => parseNumericDate(year, month, day, 'YMD')
|
|
43
|
+
},
|
|
44
|
+
// YYYY-MMM-DD
|
|
45
|
+
{
|
|
46
|
+
regex: /^(\d{4})-([A-Za-z]{3})-(\d{1,2})$/,
|
|
47
|
+
parser: ([year, monthAbbr, day]) => parseWithMonthAbbr(year, monthAbbr, day, 'YMD')
|
|
48
|
+
},
|
|
49
|
+
// MM/DD/YYYY or MM/DD/YY
|
|
50
|
+
{
|
|
51
|
+
regex: /^(\d{1,2})\/(\d{1,2})\/(\d{2,4})$/,
|
|
52
|
+
parser: ([month, day, year]) => parseNumericDate(month, day, year, 'MDY')
|
|
53
|
+
},
|
|
54
|
+
// MM-DD-YYYY or MM-DD-YY
|
|
55
|
+
{
|
|
56
|
+
regex: /^(\d{1,2})-(\d{1,2})-(\d{2,4})$/,
|
|
57
|
+
parser: ([month, day, year]) => parseNumericDate(month, day, year, 'MDY')
|
|
58
|
+
},
|
|
59
|
+
// DD/MMM/YYYY
|
|
60
|
+
{
|
|
61
|
+
regex: /^(\d{1,2})\/([A-Za-z]{3})\/(\d{2,4})$/,
|
|
62
|
+
parser: ([day, monthAbbr, year]) => parseWithMonthAbbr(day, monthAbbr, year, 'DMY')
|
|
63
|
+
},
|
|
64
|
+
// DD-MMM-YYYY
|
|
65
|
+
{
|
|
66
|
+
regex: /^(\d{1,2})-([A-Za-z]{3})-(\d{2,4})$/,
|
|
67
|
+
parser: ([day, monthAbbr, year]) => parseWithMonthAbbr(day, monthAbbr, year, 'DMY')
|
|
68
|
+
},
|
|
69
|
+
// Numeric without separators (MMDDYYYY or MMDDYY)
|
|
70
|
+
{
|
|
71
|
+
regex: /^(\d{2})(\d{2})(\d{2,4})$/,
|
|
72
|
+
parser: ([month, day, year]) => parseNumericDate(month, day, year, 'MDY')
|
|
73
|
+
},
|
|
74
|
+
// DD.MM.YYYY or DD.MM.YY
|
|
75
|
+
{
|
|
76
|
+
regex: /^(\d{1,2})\.(\d{1,2})\.(\d{2,4})$/,
|
|
77
|
+
parser: ([day, month, year]) => parseNumericDate(day, month, year, 'DMY')
|
|
26
78
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
79
|
+
];
|
|
80
|
+
const FORMAT_CONFIGS = {
|
|
81
|
+
'MM/DD/YYYY': {
|
|
82
|
+
pattern: 'MM/DD/YYYY',
|
|
83
|
+
formatter: date => `${pad(date.getMonth() + 1)}/${pad(date.getDate())}/${date.getFullYear()}`
|
|
84
|
+
},
|
|
85
|
+
'MM/DD/YY': {
|
|
86
|
+
pattern: 'MM/DD/YY',
|
|
87
|
+
formatter: date => `${pad(date.getMonth() + 1)}/${pad(date.getDate())}/${String(date.getFullYear()).slice(-2)}`
|
|
88
|
+
},
|
|
89
|
+
'DD/MMM/YYYY': {
|
|
90
|
+
pattern: 'DD/MMM/YYYY',
|
|
91
|
+
formatter: date => `${pad(date.getDate())}/${MONTH_NAMES[(date.getMonth() + 1)]}/${date.getFullYear()}`
|
|
92
|
+
},
|
|
93
|
+
'MM-DD-YYYY': {
|
|
94
|
+
pattern: 'MM-DD-YYYY',
|
|
95
|
+
formatter: date => `${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${date.getFullYear()}`
|
|
96
|
+
},
|
|
97
|
+
'MM-DD-YY': {
|
|
98
|
+
pattern: 'MM-DD-YY',
|
|
99
|
+
formatter: date => `${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${String(date.getFullYear()).slice(-2)}`
|
|
100
|
+
},
|
|
101
|
+
'DD-MMM-YYYY': {
|
|
102
|
+
pattern: 'DD-MMM-YYYY',
|
|
103
|
+
formatter: date => `${pad(date.getDate())}-${MONTH_NAMES[(date.getMonth() + 1)]}-${date.getFullYear()}`
|
|
104
|
+
},
|
|
105
|
+
'YYYY-MM-DD': {
|
|
106
|
+
pattern: 'YYYY-MM-DD',
|
|
107
|
+
formatter: date => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
|
|
108
|
+
},
|
|
109
|
+
'YYYY-MMM-DD': {
|
|
110
|
+
pattern: 'YYYY-MMM-DD',
|
|
111
|
+
formatter: date => `${date.getFullYear()}-${MONTH_NAMES[(date.getMonth() + 1)]}-${pad(date.getDate())}`
|
|
112
|
+
},
|
|
113
|
+
'DD.MM.YYYY': {
|
|
114
|
+
pattern: 'DD.MM.YYYY',
|
|
115
|
+
formatter: date => `${pad(date.getDate())}.${pad(date.getMonth() + 1)}.${date.getFullYear()}`
|
|
116
|
+
},
|
|
117
|
+
'DD.MM.YY': {
|
|
118
|
+
pattern: 'DD.MM.YY',
|
|
119
|
+
formatter: date => `${pad(date.getDate())}.${pad(date.getMonth() + 1)}.${String(date.getFullYear()).slice(-2)}`
|
|
31
120
|
}
|
|
32
|
-
|
|
33
|
-
|
|
121
|
+
};
|
|
122
|
+
function pad(num) {
|
|
123
|
+
return String(num).padStart(2, '0');
|
|
124
|
+
}
|
|
125
|
+
function normalizeYear(year) {
|
|
126
|
+
if (year.length === 3) {
|
|
127
|
+
year = year.padStart(4, '0');
|
|
34
128
|
}
|
|
35
|
-
|
|
36
|
-
|
|
129
|
+
if (year.length === 2) {
|
|
130
|
+
const currentYear = new Date().getFullYear();
|
|
131
|
+
const minYear = currentYear - (100 - FUTURE_YEAR_COERCION);
|
|
132
|
+
const maxYear = currentYear + FUTURE_YEAR_COERCION;
|
|
133
|
+
const minCentury = String(minYear).slice(0, 2);
|
|
134
|
+
const maxCentury = String(maxYear).slice(0, 2);
|
|
135
|
+
const maxYearLastTwo = +String(maxYear).slice(2);
|
|
136
|
+
return +year <= maxYearLastTwo ? +`${maxCentury}${year}` : +`${minCentury}${year}`;
|
|
37
137
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
138
|
+
return +year;
|
|
139
|
+
}
|
|
140
|
+
function validateAndClampDate(month, day, year) {
|
|
141
|
+
const clampedMonth = Math.min(Math.max(month, 1), 12);
|
|
142
|
+
const maxDaysInMonth = new Date(year, clampedMonth, 0).getDate();
|
|
143
|
+
const clampedDay = Math.min(Math.max(day, 1), maxDaysInMonth);
|
|
144
|
+
return { month: clampedMonth, day: clampedDay, year };
|
|
145
|
+
}
|
|
146
|
+
function parseMonthAbbreviation(abbr) {
|
|
147
|
+
const upperAbbr = abbr.toUpperCase();
|
|
148
|
+
return MONTH_ABBREVIATIONS[upperAbbr] || null;
|
|
149
|
+
}
|
|
150
|
+
function parseNumericDate(part1, part2, part3, order) {
|
|
151
|
+
let monthStr;
|
|
152
|
+
let dayStr;
|
|
153
|
+
let yearStr;
|
|
154
|
+
if (order === 'MDY') {
|
|
155
|
+
[monthStr, dayStr, yearStr] = [part1, part2, part3];
|
|
43
156
|
}
|
|
44
|
-
if (
|
|
45
|
-
|
|
157
|
+
else if (order === 'YMD') {
|
|
158
|
+
[yearStr, monthStr, dayStr] = [part1, part2, part3];
|
|
46
159
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
160
|
+
else {
|
|
161
|
+
// DMY
|
|
162
|
+
[dayStr, monthStr, yearStr] = [part1, part2, part3];
|
|
50
163
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
164
|
+
// Handle edge cases for incomplete input
|
|
165
|
+
if (monthStr === '0') {
|
|
166
|
+
monthStr = '1';
|
|
167
|
+
}
|
|
168
|
+
if (dayStr === '0') {
|
|
169
|
+
dayStr = '1';
|
|
170
|
+
}
|
|
171
|
+
const year = normalizeYear(yearStr);
|
|
172
|
+
const month = +monthStr;
|
|
173
|
+
const day = +dayStr;
|
|
174
|
+
const { month: validMonth, day: validDay, year: validYear } = validateAndClampDate(month, day, year);
|
|
175
|
+
const date = new Date(validYear, validMonth - 1, validDay, 0, 0, 0, 0);
|
|
176
|
+
return isValidDate(date) ? date : null;
|
|
177
|
+
}
|
|
178
|
+
function parseWithMonthAbbr(part1, monthAbbr, part3, order) {
|
|
179
|
+
const monthNum = parseMonthAbbreviation(monthAbbr);
|
|
180
|
+
if (!monthNum) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
let dayStr;
|
|
184
|
+
let yearStr;
|
|
185
|
+
if (order === 'DMY') {
|
|
186
|
+
[dayStr, yearStr] = [part1, part3];
|
|
58
187
|
}
|
|
59
188
|
else {
|
|
189
|
+
[yearStr, dayStr] = [part1, part3];
|
|
190
|
+
}
|
|
191
|
+
const year = normalizeYear(yearStr);
|
|
192
|
+
const day = +dayStr;
|
|
193
|
+
const { day: validDay, year: validYear } = validateAndClampDate(monthNum, day, year);
|
|
194
|
+
const date = new Date(validYear, monthNum - 1, validDay, 0, 0, 0, 0);
|
|
195
|
+
return isValidDate(date) ? date : null;
|
|
196
|
+
}
|
|
197
|
+
function parseISO8601(value) {
|
|
198
|
+
if (!ISO_8601_REGEX.test(value)) {
|
|
60
199
|
return null;
|
|
61
200
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const normalizedMaxYear = +String(maxYear).slice(2);
|
|
69
|
-
year = +year <= normalizedMaxYear ? `${maxYearCentury}${year}` : `${minYearCentury}${year}`;
|
|
70
|
-
}
|
|
71
|
-
let numMonth = +month;
|
|
72
|
-
let numDay = +day;
|
|
73
|
-
const numYear = +year;
|
|
74
|
-
if (numMonth > 12) {
|
|
75
|
-
numMonth = 12;
|
|
76
|
-
}
|
|
77
|
-
const maxDaysInMonth = new Date(numYear, numMonth, 0).getDate();
|
|
78
|
-
if (numDay > maxDaysInMonth) {
|
|
79
|
-
numDay = maxDaysInMonth;
|
|
80
|
-
}
|
|
81
|
-
const parsedDate = new Date(numYear, numMonth - 1, numDay, 0, 0, 0, 0);
|
|
82
|
-
return isValidDate(parsedDate) ? parsedDate : null;
|
|
201
|
+
const date = new Date(value);
|
|
202
|
+
// Handle timezone offset for ISO strings without explicit timezone
|
|
203
|
+
if (!ISO_TIMEZONE_REGEX.test(value)) {
|
|
204
|
+
date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
|
|
205
|
+
}
|
|
206
|
+
return isValidDate(date) ? date : null;
|
|
83
207
|
}
|
|
84
208
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
209
|
+
* Parses a date string value to a `Date` object in local time.
|
|
210
|
+
* Supports multiple formats as defined in SupportedDateFormats.
|
|
211
|
+
*
|
|
212
|
+
* @param value The date string value to parse
|
|
213
|
+
* @param format Optional format hint for better parsing accuracy
|
|
214
|
+
* @returns Parsed Date object or null if parsing fails
|
|
87
215
|
*/
|
|
88
|
-
export function
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
216
|
+
export function parseDateString(value, format) {
|
|
217
|
+
const cleanValue = value.replace(/[_\s]/g, '');
|
|
218
|
+
// Try ISO 8601 first
|
|
219
|
+
const isoDate = parseISO8601(cleanValue);
|
|
220
|
+
if (isoDate) {
|
|
221
|
+
return isoDate;
|
|
222
|
+
}
|
|
223
|
+
// Try each pattern until one matches
|
|
224
|
+
for (const pattern of DATE_PATTERNS) {
|
|
225
|
+
const match = cleanValue.match(pattern.regex);
|
|
226
|
+
if (match) {
|
|
227
|
+
const result = pattern.parser(match.slice(1));
|
|
228
|
+
if (result) {
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Formats a Date object to a specified string format.
|
|
237
|
+
*
|
|
238
|
+
* @param date The Date object to format
|
|
239
|
+
* @param format The desired output format (defaults to 'MM/DD/YYYY')
|
|
240
|
+
* @returns Formatted date string
|
|
241
|
+
* @throws Error if date is invalid or format is unsupported
|
|
242
|
+
*/
|
|
243
|
+
export function formatDate(date, format = 'MM/DD/YYYY') {
|
|
244
|
+
if (!isValidDate(date)) {
|
|
245
|
+
throw new Error('Invalid date provided');
|
|
246
|
+
}
|
|
247
|
+
const config = FORMAT_CONFIGS[format];
|
|
248
|
+
if (!config) {
|
|
249
|
+
throw new Error(`Unsupported format: ${format}`);
|
|
250
|
+
}
|
|
251
|
+
return config.formatter(date);
|
|
93
252
|
}
|
|
94
253
|
/**
|
|
95
254
|
* Determines if two date objects are equal.
|
|
@@ -5,10 +5,19 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Masked, InputMask, type AppendFlags, type FactoryArg } from 'imask';
|
|
7
7
|
import { DayOfWeek, ICalendarDateSelectEventData } from '../../calendar';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
export
|
|
8
|
+
import { type IDatePickerComponent } from '../date-picker';
|
|
9
|
+
import { SupportedDateFormats } from '../../core/utils/date-utils';
|
|
10
|
+
export type DatePickerParseCallback = (value: string) => Date | null;
|
|
11
|
+
export type DatePickerFormatCallback = (value: Date | null) => string | null;
|
|
12
|
+
export type DatePickerPrepareMaskCallback = (value: string, masked: Masked<string>, flags: AppendFlags, maskInstance: InputMask<FactoryArg>) => string;
|
|
13
|
+
export type DatePickerValueMode = 'object' | 'string' | 'iso-string';
|
|
14
|
+
export type DatePickerShortcuts = IDatePickerShortcuts | 'off' | undefined;
|
|
15
|
+
export type DatePickerDateFormat = SupportedDateFormats;
|
|
16
|
+
export interface IDatePickerShortcuts {
|
|
17
|
+
[key: string]: (context: {
|
|
18
|
+
instance: IDatePickerComponent;
|
|
19
|
+
}) => Date | null | undefined | void;
|
|
20
|
+
}
|
|
12
21
|
export interface IDatePickerCalendarDropdownConfig<T> {
|
|
13
22
|
value?: T | null;
|
|
14
23
|
min?: Date | null;
|
|
@@ -45,7 +54,9 @@ export declare const BASE_DATE_PICKER_CONSTANTS: {
|
|
|
45
54
|
MASKED: string;
|
|
46
55
|
MASK_FORMAT: string;
|
|
47
56
|
SHOW_MASK_FORMAT: string;
|
|
57
|
+
DATE_FORMAT: string;
|
|
48
58
|
VALUE_MODE: string;
|
|
59
|
+
SHORTCUTS: string;
|
|
49
60
|
ALLOW_INVALID_DATE: string;
|
|
50
61
|
SHOW_TODAY: string;
|
|
51
62
|
SHOW_CLEAR: string;
|
|
@@ -61,4 +72,8 @@ export declare const BASE_DATE_PICKER_CONSTANTS: {
|
|
|
61
72
|
INPUT: string;
|
|
62
73
|
TOGGLE: string;
|
|
63
74
|
};
|
|
75
|
+
defaults: {
|
|
76
|
+
DATE_FORMAT: DatePickerDateFormat;
|
|
77
|
+
};
|
|
78
|
+
supportedDateFormats: SupportedDateFormats[];
|
|
64
79
|
};
|
|
@@ -12,7 +12,9 @@ const observedAttributes = {
|
|
|
12
12
|
MASKED: 'masked',
|
|
13
13
|
MASK_FORMAT: 'mask-format',
|
|
14
14
|
SHOW_MASK_FORMAT: 'show-mask-format',
|
|
15
|
+
DATE_FORMAT: 'date-format',
|
|
15
16
|
VALUE_MODE: 'value-mode',
|
|
17
|
+
SHORTCUTS: 'shortcuts',
|
|
16
18
|
ALLOW_INVALID_DATE: 'allow-invalid-date',
|
|
17
19
|
SHOW_TODAY: 'show-today',
|
|
18
20
|
SHOW_CLEAR: 'show-clear',
|
|
@@ -28,8 +30,25 @@ const selectors = {
|
|
|
28
30
|
INPUT: 'input',
|
|
29
31
|
TOGGLE: '[forge-date-picker-toggle],[data-forge-date-picker-toggle]'
|
|
30
32
|
};
|
|
33
|
+
const defaults = {
|
|
34
|
+
DATE_FORMAT: 'MM/DD/YYYY'
|
|
35
|
+
};
|
|
36
|
+
const supportedDateFormats = [
|
|
37
|
+
'MM/DD/YYYY',
|
|
38
|
+
'MM/DD/YY',
|
|
39
|
+
'DD/MMM/YYYY',
|
|
40
|
+
'MM-DD-YYYY',
|
|
41
|
+
'MM-DD-YY',
|
|
42
|
+
'DD-MMM-YYYY',
|
|
43
|
+
'YYYY-MM-DD',
|
|
44
|
+
'YYYY-MMM-DD',
|
|
45
|
+
'DD.MM.YYYY',
|
|
46
|
+
'DD.MM.YY'
|
|
47
|
+
];
|
|
31
48
|
export const BASE_DATE_PICKER_CONSTANTS = {
|
|
32
49
|
observedAttributes,
|
|
33
50
|
attributes,
|
|
34
|
-
selectors
|
|
51
|
+
selectors,
|
|
52
|
+
defaults,
|
|
53
|
+
supportedDateFormats
|
|
35
54
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { CalendarMode, DayOfWeek, ICalendarDateSelectEventData } from '../../calendar/calendar-constants';
|
|
7
7
|
import { IBaseDatePickerAdapter } from './base-date-picker-adapter';
|
|
8
|
-
import { DatePickerFormatCallback, DatePickerParseCallback, DatePickerPrepareMaskCallback, DatePickerValueMode, IDatePickerCalendarDropdownText } from './base-date-picker-constants';
|
|
8
|
+
import { DatePickerFormatCallback, DatePickerParseCallback, DatePickerPrepareMaskCallback, IDatePickerShortcuts, DatePickerValueMode, IDatePickerCalendarDropdownText, DatePickerShortcuts, DatePickerDateFormat } from './base-date-picker-constants';
|
|
9
9
|
export interface IBaseDatePickerCore<TValue> {
|
|
10
10
|
value: TValue | null | undefined;
|
|
11
11
|
min: Date | string | null | undefined;
|
|
@@ -20,6 +20,7 @@ export interface IBaseDatePickerCore<TValue> {
|
|
|
20
20
|
masked: boolean;
|
|
21
21
|
maskFormat: string;
|
|
22
22
|
showMaskFormat: boolean;
|
|
23
|
+
dateFormat: DatePickerDateFormat;
|
|
23
24
|
valueMode: DatePickerValueMode;
|
|
24
25
|
notifyInputValueChanges: boolean;
|
|
25
26
|
allowInvalidDate: boolean;
|
|
@@ -45,7 +46,9 @@ export declare abstract class BaseDatePickerCore<TAdapter extends IBaseDatePicke
|
|
|
45
46
|
protected _masked: boolean;
|
|
46
47
|
protected _maskFormat: string;
|
|
47
48
|
protected _showMaskFormat: boolean;
|
|
49
|
+
protected _dateFormat: DatePickerDateFormat;
|
|
48
50
|
protected _valueMode: DatePickerValueMode;
|
|
51
|
+
protected _shortcuts: IDatePickerShortcuts | undefined;
|
|
49
52
|
protected _notifyInputValueChanges: boolean;
|
|
50
53
|
protected _allowInvalidDate: boolean;
|
|
51
54
|
protected _showToday: boolean;
|
|
@@ -95,6 +98,7 @@ export declare abstract class BaseDatePickerCore<TAdapter extends IBaseDatePicke
|
|
|
95
98
|
protected _onInputBlur(evt: FocusEvent): void;
|
|
96
99
|
protected _openCalendar(emitOpenEvent?: boolean): void;
|
|
97
100
|
protected _closeCalendar(emitCloseEvent?: boolean): void;
|
|
101
|
+
private _getDefaultShortcuts;
|
|
98
102
|
protected _onInputKeydown(evt: KeyboardEvent): void;
|
|
99
103
|
protected _getSanitizedDateString(value: string): string;
|
|
100
104
|
private _onToggleMousedown;
|
|
@@ -102,19 +106,21 @@ export declare abstract class BaseDatePickerCore<TAdapter extends IBaseDatePicke
|
|
|
102
106
|
private _onMonthChanged;
|
|
103
107
|
private _onActiveDescendantChanged;
|
|
104
108
|
protected _formatInputValue(): void;
|
|
105
|
-
protected _formatDate(date: Date | null | undefined): string;
|
|
109
|
+
protected _formatDate(date: Date | null | undefined): string | null;
|
|
106
110
|
protected _parseDateString(value: string): Date | null;
|
|
107
111
|
protected _coerceDateValue(value?: Date | string | null): Date | null | undefined;
|
|
108
112
|
protected _getTypedValue(value: Date | null | undefined): Date | string | null | undefined;
|
|
109
113
|
protected _isDateValueAcceptable(value?: Date | null): boolean;
|
|
110
114
|
protected _initializeMask(): void;
|
|
111
|
-
|
|
115
|
+
protected _applyMask(): void;
|
|
112
116
|
protected _applyMin(): void;
|
|
113
117
|
protected _applyMax(): void;
|
|
114
118
|
private _applyDisableDayCallback;
|
|
115
119
|
private _applyDisabled;
|
|
116
120
|
get valueMode(): DatePickerValueMode;
|
|
117
121
|
set valueMode(value: DatePickerValueMode);
|
|
122
|
+
get shortcuts(): DatePickerShortcuts;
|
|
123
|
+
set shortcuts(value: DatePickerShortcuts);
|
|
118
124
|
get min(): Date | string | null | undefined;
|
|
119
125
|
set min(value: Date | string | null | undefined);
|
|
120
126
|
get max(): Date | string | null | undefined;
|
|
@@ -133,6 +139,8 @@ export declare abstract class BaseDatePickerCore<TAdapter extends IBaseDatePicke
|
|
|
133
139
|
set maskFormat(value: string);
|
|
134
140
|
get showMaskFormat(): boolean;
|
|
135
141
|
set showMaskFormat(value: boolean);
|
|
142
|
+
get dateFormat(): DatePickerDateFormat;
|
|
143
|
+
set dateFormat(value: DatePickerDateFormat);
|
|
136
144
|
get notifyInputValueChanges(): boolean;
|
|
137
145
|
set notifyInputValueChanges(value: boolean);
|
|
138
146
|
get allowInvalidDate(): boolean;
|