@vaadin/date-picker 25.2.7 → 25.3.0-alpha10
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 +93 -0
- package/src/vaadin-date-picker-mixin.js +140 -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,47 @@ 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
|
+
* The date focused when the overlay opens is not moved if the provider reports it disabled. Use
|
|
311
|
+
* `initialPosition` to open on a date that can be selected.
|
|
312
|
+
*
|
|
313
|
+
* A value is checked against the provider even if the overlay is never opened, which loads the
|
|
314
|
+
* month holding it. Until that month answers the value is valid, and it is re-validated once the
|
|
315
|
+
* answer arrives, so `checkValidity()` can report a value as valid and then invalid.
|
|
316
|
+
*
|
|
317
|
+
* `part` from the metadata adds part names to the date, so a theme can style specific dates with
|
|
318
|
+
* `::part()` — e.g. `{ year, month, day, part: 'busy' }`. Give a single name or several separated
|
|
319
|
+
* by spaces. Do not use built-in names like `disabled` and `selected`.
|
|
320
|
+
*
|
|
321
|
+
* Keep a stable reference to the function. Assigning a new function clears the cache and
|
|
322
|
+
* re-fetches every visible range. To re-fetch while keeping the same function, because the data
|
|
323
|
+
* behind it changed, call `clearCache()`.
|
|
324
|
+
*/
|
|
325
|
+
dateMetadataProvider: DatePickerDateMetadataProvider | null | undefined;
|
|
326
|
+
|
|
239
327
|
/**
|
|
240
328
|
* Opens the dropdown.
|
|
241
329
|
*/
|
|
@@ -245,4 +333,9 @@ export declare class DatePickerMixinClass {
|
|
|
245
333
|
* Closes the dropdown.
|
|
246
334
|
*/
|
|
247
335
|
close(): void;
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Clears the `dateMetadataProvider` cache and reloads the date metadata.
|
|
339
|
+
*/
|
|
340
|
+
clearCache(): void;
|
|
248
341
|
}
|
|
@@ -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,51 @@ 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
|
+
* The date focused when the overlay opens is not moved if the provider reports it disabled.
|
|
240
|
+
* Use `initialPosition` to open on a date that can be selected.
|
|
241
|
+
*
|
|
242
|
+
* A value is checked against the provider even if the overlay is never opened, which loads the
|
|
243
|
+
* month holding it. Until that month answers the value is valid, and it is re-validated once
|
|
244
|
+
* the answer arrives, so `checkValidity()` can report a value as valid and then invalid.
|
|
245
|
+
*
|
|
246
|
+
* `part` from the metadata adds part names to the date, so a theme can style specific dates
|
|
247
|
+
* with `::part()` — e.g. `{ year, month, day, part: 'busy' }`. Give a single name or several
|
|
248
|
+
* separated by spaces. Do not use built-in names like `disabled` and `selected`.
|
|
249
|
+
*
|
|
250
|
+
* Keep a stable reference to the function. Assigning a new function clears the cache and
|
|
251
|
+
* re-fetches every visible range. To re-fetch while keeping the same function, because the
|
|
252
|
+
* data behind it changed, call `clearCache()`.
|
|
253
|
+
*
|
|
254
|
+
* @type {DatePickerDateMetadataProvider | null | undefined}
|
|
255
|
+
*/
|
|
256
|
+
dateMetadataProvider: {
|
|
257
|
+
type: Function,
|
|
258
|
+
},
|
|
259
|
+
|
|
211
260
|
/**
|
|
212
261
|
* The earliest date that can be selected. All earlier dates will be disabled.
|
|
213
262
|
* @type {Date | undefined}
|
|
@@ -272,7 +321,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
272
321
|
}
|
|
273
322
|
|
|
274
323
|
static get constraints() {
|
|
275
|
-
return [...super.constraints, 'min', 'max'];
|
|
324
|
+
return [...super.constraints, 'min', 'max', 'dateMetadataProvider'];
|
|
276
325
|
}
|
|
277
326
|
|
|
278
327
|
constructor() {
|
|
@@ -280,6 +329,9 @@ export const DatePickerMixin = (subclass) =>
|
|
|
280
329
|
|
|
281
330
|
this._boundOnClick = this._onClick.bind(this);
|
|
282
331
|
this._boundOnScroll = this._onScroll.bind(this);
|
|
332
|
+
|
|
333
|
+
this._dateMetadataController = new DateMetadataController(this, () => this.__onDateMetadataChanged());
|
|
334
|
+
this.addController(this._dateMetadataController);
|
|
283
335
|
}
|
|
284
336
|
|
|
285
337
|
/**
|
|
@@ -322,6 +374,10 @@ export const DatePickerMixin = (subclass) =>
|
|
|
322
374
|
* // Translation of the Cancel button text.
|
|
323
375
|
* cancel: 'Cancel',
|
|
324
376
|
*
|
|
377
|
+
* // Accessible name of the overlay content, announced by screen readers
|
|
378
|
+
* // when the overlay opens.
|
|
379
|
+
* dialogAccessibleName: 'Calendar',
|
|
380
|
+
*
|
|
325
381
|
* // Used for adjusting the year value when parsing dates with short years.
|
|
326
382
|
* // The year values between 0 and 99 are evaluated and adjusted.
|
|
327
383
|
* // Example: for a referenceDate of 1970-10-30;
|
|
@@ -444,6 +500,11 @@ export const DatePickerMixin = (subclass) =>
|
|
|
444
500
|
updated(props) {
|
|
445
501
|
super.updated(props);
|
|
446
502
|
|
|
503
|
+
if (props.has('dateMetadataProvider')) {
|
|
504
|
+
this._dateMetadataController.setProvider(this.dateMetadataProvider);
|
|
505
|
+
this.__reloadDateMetadata();
|
|
506
|
+
}
|
|
507
|
+
|
|
447
508
|
if (props.has('showWeekNumbers') || props.has('__effectiveI18n')) {
|
|
448
509
|
// Currently only supported for locales that start the week on Monday.
|
|
449
510
|
this.toggleAttribute('week-numbers', this.showWeekNumbers && this.__effectiveI18n.firstDayOfWeek === 1);
|
|
@@ -486,6 +547,28 @@ export const DatePickerMixin = (subclass) =>
|
|
|
486
547
|
this.$.overlay.close();
|
|
487
548
|
}
|
|
488
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Clears the `dateMetadataProvider` cache and reloads the date metadata.
|
|
552
|
+
*/
|
|
553
|
+
clearCache() {
|
|
554
|
+
this._dateMetadataController.clearCache();
|
|
555
|
+
this.__reloadDateMetadata();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Asks for what the dropped cache was holding: the months the overlay is showing, and the month
|
|
560
|
+
* of the value being validated. Requested from here rather than from the controller's
|
|
561
|
+
* notification, which would turn a provider that keeps failing into an endless retry, since a
|
|
562
|
+
* failed month is dropped and so becomes missing again.
|
|
563
|
+
* @private
|
|
564
|
+
*/
|
|
565
|
+
__reloadDateMetadata() {
|
|
566
|
+
if (this.opened) {
|
|
567
|
+
this._overlayContent?.loadVisibleDateMetadata();
|
|
568
|
+
}
|
|
569
|
+
this.__ensureSelectedDateLoaded();
|
|
570
|
+
}
|
|
571
|
+
|
|
489
572
|
/** @private */
|
|
490
573
|
__ensureContent() {
|
|
491
574
|
if (this._overlayContent) {
|
|
@@ -578,7 +661,14 @@ export const DatePickerMixin = (subclass) =>
|
|
|
578
661
|
const inputValue = this._inputElementValue;
|
|
579
662
|
const inputValid = !inputValue || (!!this._selectedDate && inputValue === this.__formatDate(this._selectedDate));
|
|
580
663
|
const isDateValid =
|
|
581
|
-
!this._selectedDate ||
|
|
664
|
+
!this._selectedDate ||
|
|
665
|
+
dateSelectable(
|
|
666
|
+
this._selectedDate,
|
|
667
|
+
this._minDate,
|
|
668
|
+
this._maxDate,
|
|
669
|
+
this.isDateDisabled,
|
|
670
|
+
this._dateMetadataController,
|
|
671
|
+
);
|
|
582
672
|
|
|
583
673
|
let inputValidity = true;
|
|
584
674
|
if (this.inputElement && this.inputElement.checkValidity) {
|
|
@@ -588,6 +678,47 @@ export const DatePickerMixin = (subclass) =>
|
|
|
588
678
|
return inputValid && isDateValid && inputValidity;
|
|
589
679
|
}
|
|
590
680
|
|
|
681
|
+
/**
|
|
682
|
+
* Asks the controller for the month holding the selected date, so a value that was set or typed
|
|
683
|
+
* without ever opening the overlay is still checked against the provider. Validation is re-run
|
|
684
|
+
* from the host callback once the month resolves.
|
|
685
|
+
* @private
|
|
686
|
+
*/
|
|
687
|
+
__ensureSelectedDateLoaded() {
|
|
688
|
+
const controller = this._dateMetadataController;
|
|
689
|
+
const awaiting = !!(controller?.provider && this._selectedDate && !controller.isMonthLoaded(this._selectedDate));
|
|
690
|
+
// Always assigned, so clearing the value or removing the provider while a request is in
|
|
691
|
+
// flight disarms the pending re-validation, and a later answer for some other month does not
|
|
692
|
+
// re-validate a value that never waited for it.
|
|
693
|
+
this.__awaitingProviderValidation = awaiting;
|
|
694
|
+
if (awaiting) {
|
|
695
|
+
controller.ensureRangeLoaded(this._selectedDate, this._selectedDate);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Called by the date metadata controller, one microtask after its state changed
|
|
701
|
+
* and coalesced, so this never writes reactive state from inside an update. The
|
|
702
|
+
* rendered months refresh on their own because they subscribe to the controller.
|
|
703
|
+
* @private
|
|
704
|
+
*/
|
|
705
|
+
__onDateMetadataChanged() {
|
|
706
|
+
const controller = this._dateMetadataController;
|
|
707
|
+
|
|
708
|
+
// Only the open overlay has a spinner to update and a today button to re-evaluate.
|
|
709
|
+
if (this._overlayContent) {
|
|
710
|
+
this._overlayContent.loading = controller.isLoading();
|
|
711
|
+
this._overlayContent.updateTodayButton();
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Runs whether or not the overlay was ever opened, which is the case this exists for: a value
|
|
715
|
+
// set or typed with the overlay closed is reported invalid as soon as its month answers.
|
|
716
|
+
if (this.__awaitingProviderValidation && this._selectedDate && controller.isMonthLoaded(this._selectedDate)) {
|
|
717
|
+
this.__awaitingProviderValidation = false;
|
|
718
|
+
this._requestValidation();
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
591
722
|
/**
|
|
592
723
|
* Override method inherited from `FocusMixin`
|
|
593
724
|
* to not call `_setFocused(true)` when focus
|
|
@@ -775,6 +906,8 @@ export const DatePickerMixin = (subclass) =>
|
|
|
775
906
|
this._ignoreFocusedDateChange = true;
|
|
776
907
|
this._focusedDate = selectedDate;
|
|
777
908
|
this._ignoreFocusedDateChange = false;
|
|
909
|
+
|
|
910
|
+
this.__ensureSelectedDateLoaded();
|
|
778
911
|
}
|
|
779
912
|
|
|
780
913
|
/** @private */
|
|
@@ -845,6 +978,8 @@ export const DatePickerMixin = (subclass) =>
|
|
|
845
978
|
enteredDate,
|
|
846
979
|
) {
|
|
847
980
|
if (overlayContent) {
|
|
981
|
+
// Reuse the date-picker's controller so the overlay shares the same cache.
|
|
982
|
+
overlayContent._dateMetadataController = this._dateMetadataController;
|
|
848
983
|
overlayContent.i18n = effectiveI18n;
|
|
849
984
|
overlayContent.label = label;
|
|
850
985
|
overlayContent.minDate = minDate;
|
|
@@ -860,11 +995,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
860
995
|
/** @private */
|
|
861
996
|
__updateOverlayContentTheme(overlayContent, theme) {
|
|
862
997
|
if (overlayContent) {
|
|
863
|
-
|
|
864
|
-
overlayContent.setAttribute('theme', theme);
|
|
865
|
-
} else {
|
|
866
|
-
overlayContent.removeAttribute('theme');
|
|
867
|
-
}
|
|
998
|
+
setOrRemoveAttribute(overlayContent, 'theme', theme);
|
|
868
999
|
}
|
|
869
1000
|
}
|
|
870
1001
|
|
|
@@ -964,6 +1095,8 @@ export const DatePickerMixin = (subclass) =>
|
|
|
964
1095
|
|
|
965
1096
|
/** @protected */
|
|
966
1097
|
_onOverlayClosed() {
|
|
1098
|
+
this._overlayContent?.cancelLoadVisibleDateMetadata();
|
|
1099
|
+
|
|
967
1100
|
// Reset `aria-hidden` state.
|
|
968
1101
|
if (this.__showOthers) {
|
|
969
1102
|
this.__showOthers();
|