@vaadin/date-picker 25.3.0-alpha7 → 25.3.0-alpha9
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 +551 -9
- package/package.json +13 -13
- package/src/styles/vaadin-date-picker-overlay-content-base-styles.js +9 -0
- package/src/vaadin-date-metadata-controller.d.ts +94 -0
- package/src/vaadin-date-metadata-controller.js +276 -0
- package/src/vaadin-date-picker-helper.d.ts +17 -0
- package/src/vaadin-date-picker-helper.js +90 -5
- package/src/vaadin-date-picker-mixin.d.ts +90 -0
- package/src/vaadin-date-picker-mixin.js +137 -7
- package/src/vaadin-date-picker-overlay-content-mixin.js +164 -58
- package/src/vaadin-date-picker-overlay-content.js +5 -1
- package/src/vaadin-date-picker-overlay.js +1 -0
- package/src/vaadin-date-picker-year.js +4 -1
- package/src/vaadin-date-picker.d.ts +10 -1
- package/src/vaadin-date-picker.js +4 -0
- package/src/vaadin-month-calendar-mixin.js +55 -29
- package/src/vaadin-month-calendar.js +1 -0
- package/web-types.json +37 -10
- package/web-types.lit.json +19 -5
|
@@ -18,6 +18,44 @@ export interface DatePickerDate {
|
|
|
18
18
|
year: number;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* A range of dates the provider is asked about. Always covers whole months: `start` is the first
|
|
23
|
+
* day of a month and `end` the last day of a month, so a provider may group its query by month.
|
|
24
|
+
*/
|
|
25
|
+
export interface DatePickerDateRange {
|
|
26
|
+
/**
|
|
27
|
+
* The first date of the range (inclusive).
|
|
28
|
+
*/
|
|
29
|
+
start: DatePickerDate;
|
|
30
|
+
/**
|
|
31
|
+
* The last date of the range (inclusive).
|
|
32
|
+
*/
|
|
33
|
+
end: DatePickerDate;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Metadata resolved on demand for a single date.
|
|
38
|
+
*/
|
|
39
|
+
export interface DatePickerDateMetadata extends DatePickerDate {
|
|
40
|
+
/**
|
|
41
|
+
* Whether the date cannot be selected.
|
|
42
|
+
*/
|
|
43
|
+
disabled?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Part names to add to the date, so a theme can style it with `::part()`. A single name, or
|
|
46
|
+
* several separated by spaces. Do not use built-in names like `disabled` and `selected`.
|
|
47
|
+
*/
|
|
48
|
+
part?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A function called with the range of dates the calendar is about to show, returning or resolving
|
|
53
|
+
* with the metadata for the dates in that range.
|
|
54
|
+
*/
|
|
55
|
+
export type DatePickerDateMetadataProvider = (
|
|
56
|
+
range: DatePickerDateRange,
|
|
57
|
+
) => DatePickerDateMetadata[] | Promise<DatePickerDateMetadata[] | null | undefined> | null | undefined;
|
|
58
|
+
|
|
21
59
|
export interface DatePickerI18n {
|
|
22
60
|
/**
|
|
23
61
|
* An array with the full names of months starting
|
|
@@ -47,6 +85,11 @@ export interface DatePickerI18n {
|
|
|
47
85
|
* Translation of the Cancel button text.
|
|
48
86
|
*/
|
|
49
87
|
cancel?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Accessible name of the overlay content, announced by screen readers when
|
|
90
|
+
* the overlay opens.
|
|
91
|
+
*/
|
|
92
|
+
dialogAccessibleName?: string;
|
|
50
93
|
/**
|
|
51
94
|
* Used for adjusting the year value when parsing dates with short years.
|
|
52
95
|
* The year values between 0 and 99 are evaluated and adjusted.
|
|
@@ -174,6 +217,10 @@ export declare class DatePickerMixinClass {
|
|
|
174
217
|
* // Translation of the Cancel button text.
|
|
175
218
|
* cancel: 'Cancel',
|
|
176
219
|
*
|
|
220
|
+
* // Accessible name of the overlay content, announced by screen readers
|
|
221
|
+
* // when the overlay opens.
|
|
222
|
+
* dialogAccessibleName: 'Calendar',
|
|
223
|
+
*
|
|
177
224
|
* // Used for adjusting the year value when parsing dates with short years.
|
|
178
225
|
* // The year values between 0 and 99 are evaluated and adjusted.
|
|
179
226
|
* // Example: for a referenceDate of 1970-10-30;
|
|
@@ -236,6 +283,44 @@ export declare class DatePickerMixinClass {
|
|
|
236
283
|
*/
|
|
237
284
|
isDateDisabled: (date: DatePickerDate) => boolean;
|
|
238
285
|
|
|
286
|
+
/**
|
|
287
|
+
* A batch function that fetches metadata for a range of dates the calendar is about to
|
|
288
|
+
* render. It receives a `DatePickerDateRange` and returns, or resolves with, an array of
|
|
289
|
+
* `DatePickerDateMetadata` objects — a `DatePickerDate` extended with metadata such as
|
|
290
|
+
* `disabled`, e.g. `{ year, month, day, disabled: true }` — for the dates that have metadata
|
|
291
|
+
* within that range. Dates it does not mention have no metadata. `month` is 0-based: 0 is
|
|
292
|
+
* January and 11 is December.
|
|
293
|
+
*
|
|
294
|
+
* Unlike `isDateDisabled`, which is called once per date, this function is called for a range of
|
|
295
|
+
* dates at a time, and again as the calendar renders further dates. The size of the range is
|
|
296
|
+
* decided by the calendar and may span several months, and may include months it already has
|
|
297
|
+
* metadata for, whose entries are then ignored.
|
|
298
|
+
*
|
|
299
|
+
* It may return a `Promise`, so the answer can come from a server. Until it resolves, the
|
|
300
|
+
* affected dates render with the `loading` part but stay selectable, and a loading spinner is
|
|
301
|
+
* shown. Nothing is disabled before the provider has actually reported it, so a slow provider
|
|
302
|
+
* does not make the calendar unusable. If it throws or rejects, the error is logged and the
|
|
303
|
+
* affected months are requested again the next time the user navigates.
|
|
304
|
+
*
|
|
305
|
+
* `disabled` from the metadata is combined with `min`, `max` and `isDateDisabled`: a date is
|
|
306
|
+
* disabled if it is out of the min/max range, or `isDateDisabled` returns `true`, or its metadata
|
|
307
|
+
* marks it disabled. That decides what the calendar renders as disabled, what can be selected, and
|
|
308
|
+
* whether the field is valid.
|
|
309
|
+
*
|
|
310
|
+
* A value is checked against the provider even if the overlay is never opened, which loads the
|
|
311
|
+
* month holding it. Until that month answers the value is valid, and it is re-validated once the
|
|
312
|
+
* answer arrives, so `checkValidity()` can report a value as valid and then invalid.
|
|
313
|
+
*
|
|
314
|
+
* `part` from the metadata adds part names to the date, so a theme can style specific dates with
|
|
315
|
+
* `::part()` — e.g. `{ year, month, day, part: 'busy' }`. Give a single name or several separated
|
|
316
|
+
* by spaces. Do not use built-in names like `disabled` and `selected`.
|
|
317
|
+
*
|
|
318
|
+
* Keep a stable reference to the function. Assigning a new function clears the cache and
|
|
319
|
+
* re-fetches every visible range. To re-fetch while keeping the same function, because the data
|
|
320
|
+
* behind it changed, call `clearCache()`.
|
|
321
|
+
*/
|
|
322
|
+
dateMetadataProvider: DatePickerDateMetadataProvider | null | undefined;
|
|
323
|
+
|
|
239
324
|
/**
|
|
240
325
|
* Opens the dropdown.
|
|
241
326
|
*/
|
|
@@ -245,4 +330,9 @@ export declare class DatePickerMixinClass {
|
|
|
245
330
|
* Closes the dropdown.
|
|
246
331
|
*/
|
|
247
332
|
close(): void;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Clears the `dateMetadataProvider` cache and reloads the date metadata.
|
|
336
|
+
*/
|
|
337
|
+
clearCache(): void;
|
|
248
338
|
}
|
|
@@ -8,13 +8,16 @@ import { DelegateFocusMixin } from '@vaadin/a11y-base/src/delegate-focus-mixin.j
|
|
|
8
8
|
import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
9
9
|
import { KeyboardMixin } from '@vaadin/a11y-base/src/keyboard-mixin.js';
|
|
10
10
|
import { isIOS } from '@vaadin/component-base/src/browser-utils.js';
|
|
11
|
+
import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
|
|
11
12
|
import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
|
|
12
13
|
import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js';
|
|
13
14
|
import { InputConstraintsMixin } from '@vaadin/field-base/src/input-constraints-mixin.js';
|
|
14
15
|
import { VirtualKeyboardController } from '@vaadin/field-base/src/virtual-keyboard-controller.js';
|
|
16
|
+
import { DateMetadataController } from './vaadin-date-metadata-controller.js';
|
|
15
17
|
import {
|
|
16
18
|
dateAllowed,
|
|
17
19
|
dateEquals,
|
|
20
|
+
dateSelectable,
|
|
18
21
|
extractDateParts,
|
|
19
22
|
formatISODate,
|
|
20
23
|
getAdjustedYear,
|
|
@@ -42,6 +45,7 @@ export const datePickerI18nDefaults = Object.freeze({
|
|
|
42
45
|
firstDayOfWeek: 0,
|
|
43
46
|
today: 'Today',
|
|
44
47
|
cancel: 'Cancel',
|
|
48
|
+
dialogAccessibleName: 'Calendar',
|
|
45
49
|
referenceDate: '',
|
|
46
50
|
formatDate(d) {
|
|
47
51
|
const yearStr = String(d.year).replace(/\d+/u, (y) => '0000'.substr(y.length) + y);
|
|
@@ -208,6 +212,48 @@ export const DatePickerMixin = (subclass) =>
|
|
|
208
212
|
type: Function,
|
|
209
213
|
},
|
|
210
214
|
|
|
215
|
+
/**
|
|
216
|
+
* A batch function that fetches metadata for a range of dates the calendar is about to
|
|
217
|
+
* render. It receives a `DatePickerDateRange` and returns, or resolves with, an array of
|
|
218
|
+
* `DatePickerDateMetadata` objects — a `DatePickerDate` extended with metadata such as
|
|
219
|
+
* `disabled`, e.g. `{ year, month, day, disabled: true }` — for the dates that have metadata
|
|
220
|
+
* within that range. Dates it does not mention have no metadata. `month` is 0-based: 0 is
|
|
221
|
+
* January and 11 is December.
|
|
222
|
+
*
|
|
223
|
+
* Unlike `isDateDisabled`, which is called once per date, this function is called for a
|
|
224
|
+
* range of dates at a time, and again as the calendar renders further dates. The size of the
|
|
225
|
+
* range is decided by the calendar and may span several months, and may include months it
|
|
226
|
+
* already has metadata for, whose entries are then ignored.
|
|
227
|
+
*
|
|
228
|
+
* It may return a `Promise`, so the answer can come from a server. Until it resolves, the
|
|
229
|
+
* affected dates render with the `loading` part but stay selectable, and a loading spinner
|
|
230
|
+
* is shown. Nothing is disabled before the provider has actually reported it, so a slow
|
|
231
|
+
* provider does not make the calendar unusable. If it throws or rejects, the error is logged
|
|
232
|
+
* and the affected months are requested again the next time the user navigates.
|
|
233
|
+
*
|
|
234
|
+
* `disabled` from the metadata is combined with `min`, `max` and `isDateDisabled`: a date is
|
|
235
|
+
* disabled if it is out of the min/max range, or `isDateDisabled` returns `true`, or its
|
|
236
|
+
* metadata marks it disabled. That decides what the calendar renders as disabled, what can be
|
|
237
|
+
* selected, and whether the field is valid.
|
|
238
|
+
*
|
|
239
|
+
* A value is checked against the provider even if the overlay is never opened, which loads the
|
|
240
|
+
* month holding it. Until that month answers the value is valid, and it is re-validated once
|
|
241
|
+
* the answer arrives, so `checkValidity()` can report a value as valid and then invalid.
|
|
242
|
+
*
|
|
243
|
+
* `part` from the metadata adds part names to the date, so a theme can style specific dates
|
|
244
|
+
* with `::part()` — e.g. `{ year, month, day, part: 'busy' }`. Give a single name or several
|
|
245
|
+
* separated by spaces. Do not use built-in names like `disabled` and `selected`.
|
|
246
|
+
*
|
|
247
|
+
* Keep a stable reference to the function. Assigning a new function clears the cache and
|
|
248
|
+
* re-fetches every visible range. To re-fetch while keeping the same function, because the
|
|
249
|
+
* data behind it changed, call `clearCache()`.
|
|
250
|
+
*
|
|
251
|
+
* @type {DatePickerDateMetadataProvider | null | undefined}
|
|
252
|
+
*/
|
|
253
|
+
dateMetadataProvider: {
|
|
254
|
+
type: Function,
|
|
255
|
+
},
|
|
256
|
+
|
|
211
257
|
/**
|
|
212
258
|
* The earliest date that can be selected. All earlier dates will be disabled.
|
|
213
259
|
* @type {Date | undefined}
|
|
@@ -272,7 +318,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
272
318
|
}
|
|
273
319
|
|
|
274
320
|
static get constraints() {
|
|
275
|
-
return [...super.constraints, 'min', 'max'];
|
|
321
|
+
return [...super.constraints, 'min', 'max', 'dateMetadataProvider'];
|
|
276
322
|
}
|
|
277
323
|
|
|
278
324
|
constructor() {
|
|
@@ -280,6 +326,9 @@ export const DatePickerMixin = (subclass) =>
|
|
|
280
326
|
|
|
281
327
|
this._boundOnClick = this._onClick.bind(this);
|
|
282
328
|
this._boundOnScroll = this._onScroll.bind(this);
|
|
329
|
+
|
|
330
|
+
this._dateMetadataController = new DateMetadataController(this, () => this.__onDateMetadataChanged());
|
|
331
|
+
this.addController(this._dateMetadataController);
|
|
283
332
|
}
|
|
284
333
|
|
|
285
334
|
/**
|
|
@@ -322,6 +371,10 @@ export const DatePickerMixin = (subclass) =>
|
|
|
322
371
|
* // Translation of the Cancel button text.
|
|
323
372
|
* cancel: 'Cancel',
|
|
324
373
|
*
|
|
374
|
+
* // Accessible name of the overlay content, announced by screen readers
|
|
375
|
+
* // when the overlay opens.
|
|
376
|
+
* dialogAccessibleName: 'Calendar',
|
|
377
|
+
*
|
|
325
378
|
* // Used for adjusting the year value when parsing dates with short years.
|
|
326
379
|
* // The year values between 0 and 99 are evaluated and adjusted.
|
|
327
380
|
* // Example: for a referenceDate of 1970-10-30;
|
|
@@ -444,6 +497,11 @@ export const DatePickerMixin = (subclass) =>
|
|
|
444
497
|
updated(props) {
|
|
445
498
|
super.updated(props);
|
|
446
499
|
|
|
500
|
+
if (props.has('dateMetadataProvider')) {
|
|
501
|
+
this._dateMetadataController.setProvider(this.dateMetadataProvider);
|
|
502
|
+
this.__reloadDateMetadata();
|
|
503
|
+
}
|
|
504
|
+
|
|
447
505
|
if (props.has('showWeekNumbers') || props.has('__effectiveI18n')) {
|
|
448
506
|
// Currently only supported for locales that start the week on Monday.
|
|
449
507
|
this.toggleAttribute('week-numbers', this.showWeekNumbers && this.__effectiveI18n.firstDayOfWeek === 1);
|
|
@@ -486,6 +544,28 @@ export const DatePickerMixin = (subclass) =>
|
|
|
486
544
|
this.$.overlay.close();
|
|
487
545
|
}
|
|
488
546
|
|
|
547
|
+
/**
|
|
548
|
+
* Clears the `dateMetadataProvider` cache and reloads the date metadata.
|
|
549
|
+
*/
|
|
550
|
+
clearCache() {
|
|
551
|
+
this._dateMetadataController.clearCache();
|
|
552
|
+
this.__reloadDateMetadata();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Asks for what the dropped cache was holding: the months the overlay is showing, and the month
|
|
557
|
+
* of the value being validated. Requested from here rather than from the controller's
|
|
558
|
+
* notification, which would turn a provider that keeps failing into an endless retry, since a
|
|
559
|
+
* failed month is dropped and so becomes missing again.
|
|
560
|
+
* @private
|
|
561
|
+
*/
|
|
562
|
+
__reloadDateMetadata() {
|
|
563
|
+
if (this.opened) {
|
|
564
|
+
this._overlayContent?.loadVisibleDateMetadata();
|
|
565
|
+
}
|
|
566
|
+
this.__ensureSelectedDateLoaded();
|
|
567
|
+
}
|
|
568
|
+
|
|
489
569
|
/** @private */
|
|
490
570
|
__ensureContent() {
|
|
491
571
|
if (this._overlayContent) {
|
|
@@ -578,7 +658,14 @@ export const DatePickerMixin = (subclass) =>
|
|
|
578
658
|
const inputValue = this._inputElementValue;
|
|
579
659
|
const inputValid = !inputValue || (!!this._selectedDate && inputValue === this.__formatDate(this._selectedDate));
|
|
580
660
|
const isDateValid =
|
|
581
|
-
!this._selectedDate ||
|
|
661
|
+
!this._selectedDate ||
|
|
662
|
+
dateSelectable(
|
|
663
|
+
this._selectedDate,
|
|
664
|
+
this._minDate,
|
|
665
|
+
this._maxDate,
|
|
666
|
+
this.isDateDisabled,
|
|
667
|
+
this._dateMetadataController,
|
|
668
|
+
);
|
|
582
669
|
|
|
583
670
|
let inputValidity = true;
|
|
584
671
|
if (this.inputElement && this.inputElement.checkValidity) {
|
|
@@ -588,6 +675,47 @@ export const DatePickerMixin = (subclass) =>
|
|
|
588
675
|
return inputValid && isDateValid && inputValidity;
|
|
589
676
|
}
|
|
590
677
|
|
|
678
|
+
/**
|
|
679
|
+
* Asks the controller for the month holding the selected date, so a value that was set or typed
|
|
680
|
+
* without ever opening the overlay is still checked against the provider. Validation is re-run
|
|
681
|
+
* from the host callback once the month resolves.
|
|
682
|
+
* @private
|
|
683
|
+
*/
|
|
684
|
+
__ensureSelectedDateLoaded() {
|
|
685
|
+
const controller = this._dateMetadataController;
|
|
686
|
+
const awaiting = !!(controller?.provider && this._selectedDate && !controller.isMonthLoaded(this._selectedDate));
|
|
687
|
+
// Always assigned, so clearing the value or removing the provider while a request is in
|
|
688
|
+
// flight disarms the pending re-validation, and a later answer for some other month does not
|
|
689
|
+
// re-validate a value that never waited for it.
|
|
690
|
+
this.__awaitingProviderValidation = awaiting;
|
|
691
|
+
if (awaiting) {
|
|
692
|
+
controller.ensureRangeLoaded(this._selectedDate, this._selectedDate);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Called by the date metadata controller, one microtask after its state changed
|
|
698
|
+
* and coalesced, so this never writes reactive state from inside an update. The
|
|
699
|
+
* rendered months refresh on their own because they subscribe to the controller.
|
|
700
|
+
* @private
|
|
701
|
+
*/
|
|
702
|
+
__onDateMetadataChanged() {
|
|
703
|
+
const controller = this._dateMetadataController;
|
|
704
|
+
|
|
705
|
+
// Only the open overlay has a spinner to update and a today button to re-evaluate.
|
|
706
|
+
if (this._overlayContent) {
|
|
707
|
+
this._overlayContent.loading = controller.isLoading();
|
|
708
|
+
this._overlayContent.updateTodayButton();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Runs whether or not the overlay was ever opened, which is the case this exists for: a value
|
|
712
|
+
// set or typed with the overlay closed is reported invalid as soon as its month answers.
|
|
713
|
+
if (this.__awaitingProviderValidation && this._selectedDate && controller.isMonthLoaded(this._selectedDate)) {
|
|
714
|
+
this.__awaitingProviderValidation = false;
|
|
715
|
+
this._requestValidation();
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
591
719
|
/**
|
|
592
720
|
* Override method inherited from `FocusMixin`
|
|
593
721
|
* to not call `_setFocused(true)` when focus
|
|
@@ -775,6 +903,8 @@ export const DatePickerMixin = (subclass) =>
|
|
|
775
903
|
this._ignoreFocusedDateChange = true;
|
|
776
904
|
this._focusedDate = selectedDate;
|
|
777
905
|
this._ignoreFocusedDateChange = false;
|
|
906
|
+
|
|
907
|
+
this.__ensureSelectedDateLoaded();
|
|
778
908
|
}
|
|
779
909
|
|
|
780
910
|
/** @private */
|
|
@@ -845,6 +975,8 @@ export const DatePickerMixin = (subclass) =>
|
|
|
845
975
|
enteredDate,
|
|
846
976
|
) {
|
|
847
977
|
if (overlayContent) {
|
|
978
|
+
// Reuse the date-picker's controller so the overlay shares the same cache.
|
|
979
|
+
overlayContent._dateMetadataController = this._dateMetadataController;
|
|
848
980
|
overlayContent.i18n = effectiveI18n;
|
|
849
981
|
overlayContent.label = label;
|
|
850
982
|
overlayContent.minDate = minDate;
|
|
@@ -860,11 +992,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
860
992
|
/** @private */
|
|
861
993
|
__updateOverlayContentTheme(overlayContent, theme) {
|
|
862
994
|
if (overlayContent) {
|
|
863
|
-
|
|
864
|
-
overlayContent.setAttribute('theme', theme);
|
|
865
|
-
} else {
|
|
866
|
-
overlayContent.removeAttribute('theme');
|
|
867
|
-
}
|
|
995
|
+
setOrRemoveAttribute(overlayContent, 'theme', theme);
|
|
868
996
|
}
|
|
869
997
|
}
|
|
870
998
|
|
|
@@ -964,6 +1092,8 @@ export const DatePickerMixin = (subclass) =>
|
|
|
964
1092
|
|
|
965
1093
|
/** @protected */
|
|
966
1094
|
_onOverlayClosed() {
|
|
1095
|
+
this._overlayContent?.cancelLoadVisibleDateMetadata();
|
|
1096
|
+
|
|
967
1097
|
// Reset `aria-hidden` state.
|
|
968
1098
|
if (this.__showOthers) {
|
|
969
1099
|
this.__showOthers();
|