@genesislcap/foundation-ui 14.448.0 → 14.449.1-alpha-12eee28.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.
@@ -22,6 +22,14 @@ export declare class DatePicker extends FoundationElement {
22
22
  format: string;
23
23
  formatChanged(oldValue: string, newValue: string): void;
24
24
  lang: string;
25
+ langChanged(): void;
26
+ /**
27
+ * @attr use-locale-format - when true, `format` is set from dayjs locale `L`.
28
+ * Uses the browser locale (`navigator.language`) unless `lang` is set on the element.
29
+ * @default false
30
+ */
31
+ useLocaleFormat: boolean;
32
+ useLocaleFormatChanged(oldValue: boolean, newValue: boolean): void;
25
33
  visible: boolean;
26
34
  /**
27
35
  * @attr inline-calendar - display the calendar beneath the value display.
@@ -68,6 +76,19 @@ export declare class DatePicker extends FoundationElement {
68
76
  * ```
69
77
  */
70
78
  label: string;
79
+ /**
80
+ * @attr allow-empty - when true, no initial date is selected; the input shows a placeholder until the user picks a date.
81
+ * @default false
82
+ * @example
83
+ * ```html
84
+ * <rapid-date-picker allow-empty format="DD/MM/YYYY"></rapid-date-picker>
85
+ * ```
86
+ */
87
+ allowEmpty: boolean;
88
+ /**
89
+ * @attr placeholder - hint shown when allow-empty is set and no date is selected. Defaults from `format` (e.g. DD/MM/YYYY → dd/mm/yyyy).
90
+ */
91
+ placeholder: string;
71
92
  /**
72
93
  * @attr value - sets the value of the date picker
73
94
  * @example
@@ -116,8 +137,50 @@ export declare class DatePicker extends FoundationElement {
116
137
  disabledDaysOfWeek: number[];
117
138
  calendar: Calendar;
118
139
  substringNumber: number;
140
+ private suppressValueChanged;
141
+ private cachedFormat;
142
+ disconnectedCallback(): void;
119
143
  connectedCallback(): void;
144
+ /**
145
+ * Clears the selected date (only when `allow-empty` is enabled).
146
+ * @public
147
+ */
148
+ clear(): void;
149
+ /**
150
+ * @internal
151
+ */
152
+ get inputValue(): string;
153
+ /**
154
+ * @internal
155
+ */
156
+ get inputPlaceholder(): string;
157
+ /**
158
+ * Locale for display format and calendar labels: explicit `lang`, or browser when using locale format.
159
+ * @internal
160
+ */
161
+ private get effectiveLang();
162
+ private applyLocaleFormat;
163
+ private cacheUserFormat;
164
+ private restoreUserFormat;
165
+ private applyEffectiveLangChange;
166
+ private syncValueAttribute;
167
+ private initializeEmpty;
168
+ private initializeWithDefaultDate;
169
+ private resetToDefaultDate;
170
+ private applyValue;
171
+ /**
172
+ * Resets to the empty/cleared view.
173
+ * @param emitEvent - emit `value-changed` for user-driven clears (public `clear()`); skip for
174
+ * host-driven attribute writes since the host is the source of the change.
175
+ */
176
+ private clearToEmpty;
120
177
  updateHeaderText(): void;
178
+ protected visibleChanged(_prev: boolean, next: boolean): void;
179
+ private clickOutside;
180
+ private keydownOutside;
181
+ private handleKeydownOutside;
182
+ private handleClickOutside;
183
+ private closeCalendar;
121
184
  toggleCalendar(): void;
122
185
  toggleVisibility(): void;
123
186
  isSelectedDate(date: any): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../../../src/date-picker/date-picker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAM/D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAGlC,OAAO,GAAG,MAAM,OAAO,CAAC;AAGxB,eAAO,MAAM,iCAAiC,EAAE,cAA0B,CAAC;AAE3E,eAAO,MAAM,uBAAuB,IAAK,CAAC;AAE1C;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,iBAAiB;IAC/C;;;;;;;;;OASG;IACG,MAAM,SAAgB;IAC5B,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAK1C,IAAI,SAAW;IACf,OAAO,UAAS;IAEtB;;;;;;;;;OASG;IACsD,cAAc,EAAE,OAAO,CAAC;IACjF,qBAAqB;IAIrB;;;;;;;;;OASG;IACoD,YAAY,EAAE,OAAO,CAAC;IAC7E,2BAA2B;IAI3B;;;;;;;;;OASG;IACG,QAAQ,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAY;IAE/D;;;;;;;;OAQG;IACG,KAAK,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACG,KAAK,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAU/C;;OAEG;IACS,WAAW,EAAE,GAAG,CAAC;IAC7B;;OAEG;IACS,YAAY,EAAE,MAAM,CAAC;IACjC;;OAEG;IACS,WAAW,EAAE,MAAM,CAAC;IAChC;;OAEG;IACS,uBAAuB,EAAE,MAAM,CAAC;IAC5C;;OAEG;IACS,UAAU,EAAE,MAAM,CAAC;IAE/B;;OAEG;IACS,SAAS,EAAE,GAAG,EAAE,CAAC;IAE7B;;;;;;;;;OASG;IACS,kBAAkB,EAAE,MAAM,EAAE,CAAM;IAClC,QAAQ,EAAE,QAAQ,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IAExB,iBAAiB;IAqBjB,gBAAgB,IAAI,IAAI;IAKxB,cAAc,IAAI,IAAI;IAUtB,gBAAgB,IAAI,IAAI;IASxB,cAAc,CAAC,IAAI,KAAA,GAAG,OAAO;IAI7B,sBAAsB,IAAI,OAAO;IASjC,SAAS,IAAI,IAAI;IAMjB,SAAS,IAAI,IAAI;IAMjB,SAAS,CAAC,GAAG,KAAA,GAAG,IAAI;IAapB,gBAAgB,IAAI,GAAG,EAAE;CAkB1B;AACD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;qBAM/B,CAAC"}
1
+ {"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../../../src/date-picker/date-picker.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.template.d.ts","sourceRoot":"","sources":["../../../src/date-picker/date-picker.template.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAOhD,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,oEAmEnD,CAAC;AAEF,eAAO,MAAM,4BAA4B,iEAExC,CAAC"}
1
+ {"version":3,"file":"date-picker.template.d.ts","sourceRoot":"","sources":["../../../src/date-picker/date-picker.template.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAOhD,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,oEA0EnD,CAAC;AAEF,eAAO,MAAM,4BAA4B,iEAExC,CAAC"}
@@ -0,0 +1,37 @@
1
+ import dayjs from 'dayjs';
2
+ import 'dayjs/locale/en-gb';
3
+ /**
4
+ * Maps BCP-47 tags to dayjs locale ids (lowercase, e.g. `fr-FR` → `fr-fr`).
5
+ * English variants map to dayjs bundles: `en-US` → `en`, `en-GB` → `en-gb`.
6
+ * @public
7
+ */
8
+ export declare function resolveDayjsLocale(lang: string): string;
9
+ /**
10
+ * Browser BCP-47 locale (e.g. `en-GB`), or `en` when unavailable (SSR/tests).
11
+ * @public
12
+ */
13
+ export declare function getBrowserLocale(): string;
14
+ /**
15
+ * Display format from dayjs locale `L` (e.g. en / en-US → MM/DD/YYYY, en-gb → DD/MM/YYYY).
16
+ * Falls back to `en` when the locale is not loaded in dayjs (import the matching `dayjs/locale/*` bundle).
17
+ * @public
18
+ */
19
+ export declare function getDisplayFormatForLang(lang?: string): string;
20
+ /**
21
+ * Maps a dayjs-style display format to a lowercase placeholder hint (e.g. DD/MM/YYYY → dd/mm/yyyy).
22
+ * @public
23
+ */
24
+ export declare function formatToPlaceholder(format: string): string;
25
+ export declare function hasDateValue(value: string | null | undefined): boolean;
26
+ /**
27
+ * Internal wire format for the date picker's `value` attribute.
28
+ * Display formatting is independent (see `format` / `getDisplayFormatForLang`).
29
+ * @public
30
+ */
31
+ export declare const DATE_PICKER_VALUE_FORMAT = "MM-DD-YYYY";
32
+ /**
33
+ * Strictly parses a date picker value (`displayFormat` or internal `DATE_PICKER_VALUE_FORMAT`). Returns null when invalid.
34
+ * @public
35
+ */
36
+ export declare function parseDatePickerValue(dateString: string, displayFormat: string): dayjs.Dayjs | null;
37
+ //# sourceMappingURL=date-picker.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-picker.util.d.ts","sourceRoot":"","sources":["../../../src/date-picker/date-picker.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,oBAAoB,CAAC;AAM5B;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASvD;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,GAAE,MAAa,GAAG,MAAM,CAUnE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEtE;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,GACpB,KAAK,CAAC,KAAK,GAAG,IAAI,CAWpB"}
@@ -1,4 +1,5 @@
1
1
  export * from './date-picker.styles';
2
2
  export * from './date-picker.template';
3
3
  export * from './date-picker';
4
+ export * from './date-picker.util';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/date-picker/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/date-picker/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { LogLevel, logType } from '@genesislcap/foundation-logger';
2
- export { LogLevel, logType };
1
+ import { LogLevel, LogType } from '@genesislcap/foundation-logger';
2
+ export { LogLevel, LogType as logType };
3
3
  export declare const logger: import("@genesislcap/foundation-logger").Logger;
4
4
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,QAAQ,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAE7B,eAAO,MAAM,MAAM,iDAAgC,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,QAAQ,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,OAAO,IAAI,OAAO,EAAE,CAAC;AAExC,eAAO,MAAM,MAAM,iDAAgC,CAAC"}
@@ -6,9 +6,11 @@ import dayjs from 'dayjs';
6
6
  import customParseFormat from 'dayjs/plugin/customParseFormat';
7
7
  import localeData from 'dayjs/plugin/localeData';
8
8
  import LocalizedFormat from 'dayjs/plugin/localizedFormat';
9
+ import { wasClickOutsideElement } from '../utils';
9
10
  import Calendar from './calendar';
10
11
  import { foundationDatePickerStyles as styles } from './date-picker.styles';
11
12
  import { foundationDatePickerTemplate as template } from './date-picker.template';
13
+ import { DATE_PICKER_VALUE_FORMAT, formatToPlaceholder, getBrowserLocale, getDisplayFormatForLang, hasDateValue, parseDatePickerValue, } from './date-picker.util';
12
14
  import Day from './day';
13
15
  import { getNextEnabledDay } from './day.util';
14
16
  export const foundationDatePickerShadowOptions = undefined;
@@ -31,7 +33,7 @@ export class DatePicker extends FoundationElement {
31
33
  * </rapid-date-picker>
32
34
  * ```
33
35
  */
34
- this.format = 'MM-DD-YYYY';
36
+ this.format = DATE_PICKER_VALUE_FORMAT;
35
37
  this.lang = 'en-US';
36
38
  this.visible = false;
37
39
  /**
@@ -56,53 +58,284 @@ export class DatePicker extends FoundationElement {
56
58
  * ```
57
59
  */
58
60
  this.disabledDaysOfWeek = [];
61
+ this.suppressValueChanged = false;
62
+ this.cachedFormat = DATE_PICKER_VALUE_FORMAT;
63
+ this.clickOutside = (e) => this.handleClickOutside(e);
64
+ this.keydownOutside = (e) => this.handleKeydownOutside(e);
59
65
  }
60
66
  formatChanged(oldValue, newValue) {
61
- if (newValue && this.calendar) {
67
+ if (!this.useLocaleFormat && newValue) {
68
+ this.cacheUserFormat(newValue);
69
+ }
70
+ if (newValue && this.calendar && hasDateValue(this.value)) {
62
71
  this.formatDate = dayjs(this.currentDate.Date).format(newValue);
63
72
  }
64
73
  }
74
+ langChanged() {
75
+ this.applyEffectiveLangChange();
76
+ }
77
+ useLocaleFormatChanged(oldValue, newValue) {
78
+ // Pre-mount: format/useLocaleFormat may be set in any order; let connectedCallback do the
79
+ // initial cache so we don't snapshot the default format before the `format` attribute is parsed.
80
+ if (!this.calendar) {
81
+ return;
82
+ }
83
+ if (newValue && !oldValue) {
84
+ this.cacheUserFormat(this.format);
85
+ }
86
+ else if (!newValue && oldValue) {
87
+ this.restoreUserFormat();
88
+ }
89
+ this.applyEffectiveLangChange();
90
+ }
65
91
  inlineCalendarChanged() {
92
+ if (this.inlineCalendar) {
93
+ document.removeEventListener('mousedown', this.clickOutside);
94
+ document.removeEventListener('keydown', this.keydownOutside);
95
+ }
96
+ else if (this.visible) {
97
+ document.addEventListener('mousedown', this.clickOutside);
98
+ document.addEventListener('keydown', this.keydownOutside);
99
+ }
66
100
  this.toggleVisibility();
67
101
  }
68
102
  hideWeekendsCalendarChanged() {
69
103
  this.toggleVisibility();
70
104
  }
71
105
  valueChanged(oldValue, newValue) {
72
- if (newValue && this.calendar) {
73
- const localeFormat = dayjs().localeData().longDateFormat('L');
74
- this.currentDate = new Day(dayjs(newValue, localeFormat).toDate(), this.lang);
75
- this.calendar.goToDate(this.currentDate.monthNumber, this.currentDate.year);
76
- this.formatDate = dayjs(this.currentDate.Date).format(this.format);
77
- this.updateHeaderText();
78
- this.monthList = this.getMonthDaysGrid();
106
+ if (this.suppressValueChanged) {
107
+ return;
108
+ }
109
+ if (!this.calendar) {
110
+ return;
111
+ }
112
+ if (!hasDateValue(newValue)) {
113
+ if (this.allowEmpty) {
114
+ this.clearToEmpty(false);
115
+ }
116
+ else {
117
+ this.resetToDefaultDate();
118
+ }
119
+ return;
79
120
  }
121
+ this.applyValue(newValue);
122
+ }
123
+ disconnectedCallback() {
124
+ document.removeEventListener('mousedown', this.clickOutside);
125
+ document.removeEventListener('keydown', this.keydownOutside);
126
+ super.disconnectedCallback();
80
127
  }
81
128
  connectedCallback() {
82
- var _a;
83
129
  super.connectedCallback();
84
130
  dayjs.extend(customParseFormat);
85
131
  dayjs.extend(LocalizedFormat);
86
132
  dayjs.extend(localeData);
87
133
  this.substringNumber = 3;
134
+ if (this.useLocaleFormat) {
135
+ this.cacheUserFormat(this.format);
136
+ this.applyLocaleFormat();
137
+ }
88
138
  const dateString = this.value;
89
- const dayjsDate = dateString ? dayjs(dateString, 'MM-DD-YYYY') : dayjs();
90
- let currentDate = new Day(dayjsDate.toDate(), this.lang);
139
+ if (this.allowEmpty && !hasDateValue(dateString)) {
140
+ this.initializeEmpty();
141
+ return;
142
+ }
143
+ if (hasDateValue(dateString)) {
144
+ this.applyValue(dateString);
145
+ return;
146
+ }
147
+ this.initializeWithDefaultDate();
148
+ }
149
+ /**
150
+ * Clears the selected date (only when `allow-empty` is enabled).
151
+ * @public
152
+ */
153
+ clear() {
154
+ if (!this.allowEmpty) {
155
+ return;
156
+ }
157
+ this.clearToEmpty(true);
158
+ }
159
+ /**
160
+ * @internal
161
+ */
162
+ get inputValue() {
163
+ return hasDateValue(this.value) ? this.formatDate : '';
164
+ }
165
+ /**
166
+ * @internal
167
+ */
168
+ get inputPlaceholder() {
169
+ var _a;
170
+ if (!this.allowEmpty || hasDateValue(this.value)) {
171
+ return '';
172
+ }
173
+ return ((_a = this.placeholder) === null || _a === void 0 ? void 0 : _a.trim()) || formatToPlaceholder(this.format);
174
+ }
175
+ /**
176
+ * Locale for display format and calendar labels: explicit `lang`, or browser when using locale format.
177
+ * @internal
178
+ */
179
+ get effectiveLang() {
180
+ if (this.useLocaleFormat && !this.hasAttribute('lang')) {
181
+ return getBrowserLocale();
182
+ }
183
+ return this.lang;
184
+ }
185
+ applyLocaleFormat() {
186
+ this.format = getDisplayFormatForLang(this.effectiveLang);
187
+ }
188
+ cacheUserFormat(format) {
189
+ const trimmed = format === null || format === void 0 ? void 0 : format.trim();
190
+ if (trimmed) {
191
+ this.cachedFormat = trimmed;
192
+ }
193
+ }
194
+ restoreUserFormat() {
195
+ this.format = this.cachedFormat;
196
+ }
197
+ applyEffectiveLangChange() {
198
+ var _a, _b;
199
+ if (!this.calendar) {
200
+ return;
201
+ }
202
+ if (this.useLocaleFormat) {
203
+ this.applyLocaleFormat();
204
+ }
205
+ const viewYear = this.calendar.year;
206
+ const viewMonth = this.calendar.month.number;
207
+ const selectedDate = (_b = (_a = this.currentDate) === null || _a === void 0 ? void 0 : _a.Date) !== null && _b !== void 0 ? _b : dayjs().toDate();
208
+ this.currentDate = new Day(selectedDate, this.effectiveLang);
209
+ this.calendar = new Calendar(viewYear, viewMonth, this.effectiveLang);
210
+ if (hasDateValue(this.value)) {
211
+ this.formatDate = dayjs(this.currentDate.Date).format(this.format);
212
+ }
213
+ this.updateHeaderText();
214
+ this.monthList = this.getMonthDaysGrid();
215
+ }
216
+ syncValueAttribute(value) {
217
+ this.suppressValueChanged = true;
218
+ this.value = value;
219
+ this.suppressValueChanged = false;
220
+ }
221
+ initializeEmpty() {
222
+ const viewDate = dayjs();
223
+ this.currentDate = new Day(viewDate.toDate(), this.effectiveLang);
224
+ this.calendar = new Calendar(this.currentDate.year, this.currentDate.monthNumber, this.effectiveLang);
225
+ this.formatDate = '';
226
+ this.syncValueAttribute('');
227
+ this.currentMonth = this.calendar.month.name;
228
+ this.currentYear = this.calendar.year;
229
+ this.monthList = this.getMonthDaysGrid();
230
+ }
231
+ initializeWithDefaultDate() {
232
+ this.resetToDefaultDate(true);
233
+ }
234
+ resetToDefaultDate(createCalendar = false) {
235
+ var _a;
236
+ let currentDate = new Day(dayjs().toDate(), this.effectiveLang);
91
237
  if ((_a = this.disabledDaysOfWeek) === null || _a === void 0 ? void 0 : _a.length) {
92
- currentDate = getNextEnabledDay(this.disabledDaysOfWeek, currentDate, this.lang);
238
+ currentDate = getNextEnabledDay(this.disabledDaysOfWeek, currentDate, this.effectiveLang);
93
239
  }
94
240
  this.currentDate = currentDate;
95
- this.calendar = new Calendar(this.currentDate.year, this.currentDate.monthNumber, this.lang);
96
- this.value = dayjs(this.currentDate.Date).format('MM-DD-YYYY');
241
+ if (createCalendar || !this.calendar) {
242
+ this.calendar = new Calendar(this.currentDate.year, this.currentDate.monthNumber, this.effectiveLang);
243
+ }
244
+ else {
245
+ this.calendar.goToDate(this.currentDate.monthNumber, this.currentDate.year);
246
+ }
247
+ this.formatDate = dayjs(this.currentDate.Date).format(this.format);
248
+ this.syncValueAttribute(dayjs(this.currentDate.Date).format(DATE_PICKER_VALUE_FORMAT));
97
249
  this.currentMonth = this.calendar.month.name;
98
250
  this.currentYear = this.calendar.year;
251
+ this.monthList = this.getMonthDaysGrid();
252
+ }
253
+ applyValue(dateString) {
254
+ const parsed = parseDatePickerValue(dateString, this.format);
255
+ if (!parsed) {
256
+ if (this.allowEmpty) {
257
+ this.clearToEmpty(false);
258
+ }
259
+ else {
260
+ this.resetToDefaultDate();
261
+ }
262
+ return;
263
+ }
264
+ // Honor the host's value verbatim. Disabled-day roll-forward only applies to the
265
+ // default "today" fallback (see `resetToDefaultDate`); programmatic writes should not
266
+ // be silently shifted, and user clicks on disabled day cells are blocked in the template.
267
+ this.currentDate = new Day(parsed.toDate(), this.effectiveLang);
268
+ if (!this.calendar) {
269
+ this.calendar = new Calendar(this.currentDate.year, this.currentDate.monthNumber, this.effectiveLang);
270
+ }
271
+ else {
272
+ this.calendar.goToDate(this.currentDate.monthNumber, this.currentDate.year);
273
+ }
99
274
  this.formatDate = dayjs(this.currentDate.Date).format(this.format);
275
+ this.syncValueAttribute(dayjs(this.currentDate.Date).format(DATE_PICKER_VALUE_FORMAT));
276
+ this.updateHeaderText();
100
277
  this.monthList = this.getMonthDaysGrid();
101
278
  }
279
+ /**
280
+ * Resets to the empty/cleared view.
281
+ * @param emitEvent - emit `value-changed` for user-driven clears (public `clear()`); skip for
282
+ * host-driven attribute writes since the host is the source of the change.
283
+ */
284
+ clearToEmpty(emitEvent) {
285
+ const viewDate = dayjs();
286
+ this.currentDate = new Day(viewDate.toDate(), this.effectiveLang);
287
+ if (this.calendar) {
288
+ this.calendar.goToDate(this.currentDate.monthNumber, this.currentDate.year);
289
+ this.updateHeaderText();
290
+ this.monthList = this.getMonthDaysGrid();
291
+ }
292
+ this.formatDate = '';
293
+ this.syncValueAttribute('');
294
+ if (emitEvent) {
295
+ this.$emit('value-changed', undefined);
296
+ }
297
+ }
102
298
  updateHeaderText() {
103
299
  this.currentMonth = this.calendar.month.name;
104
300
  this.currentYear = this.calendar.year;
105
301
  }
302
+ visibleChanged(_prev, next) {
303
+ if (this.inlineCalendar) {
304
+ return;
305
+ }
306
+ if (next) {
307
+ document.addEventListener('mousedown', this.clickOutside);
308
+ document.addEventListener('keydown', this.keydownOutside);
309
+ }
310
+ else {
311
+ document.removeEventListener('mousedown', this.clickOutside);
312
+ document.removeEventListener('keydown', this.keydownOutside);
313
+ }
314
+ }
315
+ handleKeydownOutside(event) {
316
+ if (this.inlineCalendar || !this.visible) {
317
+ return;
318
+ }
319
+ if (event.key === 'Escape') {
320
+ event.preventDefault();
321
+ this.closeCalendar();
322
+ }
323
+ }
324
+ handleClickOutside(event) {
325
+ if (this.inlineCalendar || !this.visible) {
326
+ return;
327
+ }
328
+ if (wasClickOutsideElement(event, this)) {
329
+ this.closeCalendar();
330
+ }
331
+ }
332
+ closeCalendar() {
333
+ if (!this.visible) {
334
+ return;
335
+ }
336
+ this.visible = false;
337
+ this.toggleVisibility();
338
+ }
106
339
  toggleCalendar() {
107
340
  this.visible = !this.visible;
108
341
  this.toggleVisibility();
@@ -115,6 +348,9 @@ export class DatePicker extends FoundationElement {
115
348
  this.calendarDropdownClasses = classNames(['visible', this.visible], this.position, ['inline', this.inlineCalendar], ['hide-weekends', this.hideWeekends]);
116
349
  }
117
350
  isSelectedDate(date) {
351
+ if (this.allowEmpty && !hasDateValue(this.value)) {
352
+ return false;
353
+ }
118
354
  return dayjs(date.Date).isSame(this.currentDate.Date, 'day');
119
355
  }
120
356
  isCurrentCalendarMonth() {
@@ -135,14 +371,16 @@ export class DatePicker extends FoundationElement {
135
371
  this.monthList = this.getMonthDaysGrid();
136
372
  }
137
373
  selectDay(day) {
138
- if (dayjs(day.Date).isSame(this.currentDate.Date))
374
+ const sameDayAsCurrent = dayjs(day.Date).isSame(this.currentDate.Date, 'day');
375
+ if (sameDayAsCurrent && hasDateValue(this.value)) {
139
376
  return;
377
+ }
140
378
  this.currentDate = day;
141
379
  if (day.monthNumber !== this.calendar.month.number) {
142
380
  this.prevMonth();
143
381
  }
144
382
  this.formatDate = dayjs(this.currentDate.Date).format(this.format);
145
- this.value = dayjs(day.Date).format('MM-DD-YYYY');
383
+ this.syncValueAttribute(dayjs(day.Date).format(DATE_PICKER_VALUE_FORMAT));
146
384
  this.$emit('value-changed', this.value);
147
385
  this.toggleCalendar();
148
386
  }
@@ -168,6 +406,9 @@ __decorate([
168
406
  __decorate([
169
407
  attr
170
408
  ], DatePicker.prototype, "lang", void 0);
409
+ __decorate([
410
+ attr({ attribute: 'use-locale-format', mode: 'boolean' })
411
+ ], DatePicker.prototype, "useLocaleFormat", void 0);
171
412
  __decorate([
172
413
  attr
173
414
  ], DatePicker.prototype, "visible", void 0);
@@ -183,6 +424,12 @@ __decorate([
183
424
  __decorate([
184
425
  attr
185
426
  ], DatePicker.prototype, "label", void 0);
427
+ __decorate([
428
+ attr({ attribute: 'allow-empty', mode: 'boolean' })
429
+ ], DatePicker.prototype, "allowEmpty", void 0);
430
+ __decorate([
431
+ attr
432
+ ], DatePicker.prototype, "placeholder", void 0);
186
433
  __decorate([
187
434
  attr
188
435
  ], DatePicker.prototype, "value", void 0);
@@ -5,7 +5,15 @@ const SUNDAY_INDEX = 1;
5
5
  const SATURDAY_INDEX = 7;
6
6
  const WEEKEND_DAY_INDICES = [SUNDAY_INDEX, SATURDAY_INDEX];
7
7
  export const getPrefixedDatePicker = (prefix) => html `
8
- <${prefix}-text-field data-test-id="date-picker" readonly :value="${(x) => x.formatDate}" part="date-toggle" class="date-toggle" @click="${(x) => x.toggleCalendar()}">
8
+ <${prefix}-text-field
9
+ data-test-id="date-picker"
10
+ readonly
11
+ :value="${(x) => x.inputValue}"
12
+ placeholder="${(x) => x.inputPlaceholder}"
13
+ part="date-toggle"
14
+ class="date-toggle"
15
+ @click="${(x) => x.toggleCalendar()}"
16
+ >
9
17
  ${(x) => x.label}
10
18
  <div class="input-icon-container" part="input-icon-container" slot="end"><${prefix}-icon variant="regular" name="calendar-alt" size="sm"></${prefix}-icon></div>
11
19
  </${prefix}-text-field>
@@ -0,0 +1,92 @@
1
+ import dayjs from 'dayjs';
2
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
3
+ import localeData from 'dayjs/plugin/localeData';
4
+ import LocalizedFormat from 'dayjs/plugin/localizedFormat';
5
+ import 'dayjs/locale/en-gb';
6
+ dayjs.extend(customParseFormat);
7
+ dayjs.extend(localeData);
8
+ dayjs.extend(LocalizedFormat);
9
+ /**
10
+ * Maps BCP-47 tags to dayjs locale ids (lowercase, e.g. `fr-FR` → `fr-fr`).
11
+ * English variants map to dayjs bundles: `en-US` → `en`, `en-GB` → `en-gb`.
12
+ * @public
13
+ */
14
+ export function resolveDayjsLocale(lang) {
15
+ const normalized = ((lang === null || lang === void 0 ? void 0 : lang.trim()) || 'en').toLowerCase().replace(/_/g, '-');
16
+ if (normalized === 'en-us' || normalized === 'en') {
17
+ return 'en';
18
+ }
19
+ if (normalized === 'en-gb') {
20
+ return 'en-gb';
21
+ }
22
+ return normalized;
23
+ }
24
+ function dayjsLocaleWasApplied(requested, applied) {
25
+ if (requested === applied) {
26
+ return true;
27
+ }
28
+ const language = requested.split('-')[0];
29
+ return applied === language;
30
+ }
31
+ /**
32
+ * Browser BCP-47 locale (e.g. `en-GB`), or `en` when unavailable (SSR/tests).
33
+ * @public
34
+ */
35
+ export function getBrowserLocale() {
36
+ if (typeof navigator !== 'undefined' && navigator.language) {
37
+ return navigator.language;
38
+ }
39
+ return 'en';
40
+ }
41
+ /**
42
+ * Display format from dayjs locale `L` (e.g. en / en-US → MM/DD/YYYY, en-gb → DD/MM/YYYY).
43
+ * Falls back to `en` when the locale is not loaded in dayjs (import the matching `dayjs/locale/*` bundle).
44
+ * @public
45
+ */
46
+ export function getDisplayFormatForLang(lang = 'en') {
47
+ const locale = resolveDayjsLocale(lang);
48
+ const instance = dayjs().locale(locale);
49
+ const appliedLocale = instance.locale();
50
+ if (!dayjsLocaleWasApplied(locale, appliedLocale)) {
51
+ return dayjs().locale('en').localeData().longDateFormat('L') || 'MM/DD/YYYY';
52
+ }
53
+ return instance.localeData().longDateFormat('L') || 'MM/DD/YYYY';
54
+ }
55
+ /**
56
+ * Maps a dayjs-style display format to a lowercase placeholder hint (e.g. DD/MM/YYYY → dd/mm/yyyy).
57
+ * @public
58
+ */
59
+ export function formatToPlaceholder(format) {
60
+ return format
61
+ .replace(/YYYY/g, 'yyyy')
62
+ .replace(/YY(?!Y)/g, 'yy')
63
+ .replace(/DD/g, 'dd')
64
+ .replace(/D(?!D)/g, 'd')
65
+ .replace(/MM/g, 'mm')
66
+ .replace(/M(?!M)/g, 'm');
67
+ }
68
+ export function hasDateValue(value) {
69
+ return value !== null && value !== undefined && String(value).trim() !== '';
70
+ }
71
+ /**
72
+ * Internal wire format for the date picker's `value` attribute.
73
+ * Display formatting is independent (see `format` / `getDisplayFormatForLang`).
74
+ * @public
75
+ */
76
+ export const DATE_PICKER_VALUE_FORMAT = 'MM-DD-YYYY';
77
+ /**
78
+ * Strictly parses a date picker value (`displayFormat` or internal `DATE_PICKER_VALUE_FORMAT`). Returns null when invalid.
79
+ * @public
80
+ */
81
+ export function parseDatePickerValue(dateString, displayFormat) {
82
+ const trimmed = String(dateString).trim();
83
+ const display = dayjs(trimmed, displayFormat, true);
84
+ if (display.isValid()) {
85
+ return display;
86
+ }
87
+ const internal = dayjs(trimmed, DATE_PICKER_VALUE_FORMAT, true);
88
+ if (internal.isValid()) {
89
+ return internal;
90
+ }
91
+ return null;
92
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './date-picker.styles';
2
2
  export * from './date-picker.template';
3
3
  export * from './date-picker';
4
+ export * from './date-picker.util';
@@ -1,3 +1,2 @@
1
- import { createLogger, LogLevel } from '@genesislcap/foundation-logger';
2
- export { LogLevel };
1
+ import { createLogger } from '@genesislcap/foundation-logger';
3
2
  export const logger = createLogger('foundation-ui');