@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
|
@@ -5,10 +5,22 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
7
7
|
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
8
|
+
import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
|
|
8
9
|
import { addListener } from '@vaadin/component-base/src/gestures.js';
|
|
9
10
|
import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js';
|
|
10
11
|
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
createDate,
|
|
14
|
+
dateAfterXMonths,
|
|
15
|
+
dateAllowed,
|
|
16
|
+
dateEquals,
|
|
17
|
+
dateSelectable,
|
|
18
|
+
firstOfMonth,
|
|
19
|
+
getClosestDate,
|
|
20
|
+
lastOfMonth,
|
|
21
|
+
monthDate,
|
|
22
|
+
monthIndex,
|
|
23
|
+
} from './vaadin-date-picker-helper.js';
|
|
12
24
|
|
|
13
25
|
export const DatePickerOverlayContentMixin = (superClass) =>
|
|
14
26
|
class DatePickerOverlayContentMixin extends superClass {
|
|
@@ -106,6 +118,17 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
106
118
|
type: Function,
|
|
107
119
|
},
|
|
108
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Reflected while data is being loaded, so the overlay can show a loading spinner.
|
|
123
|
+
* Currently set while the date metadata provider is resolving.
|
|
124
|
+
* @protected
|
|
125
|
+
*/
|
|
126
|
+
loading: {
|
|
127
|
+
type: Boolean,
|
|
128
|
+
value: false,
|
|
129
|
+
reflectToAttribute: true,
|
|
130
|
+
},
|
|
131
|
+
|
|
109
132
|
enteredDate: {
|
|
110
133
|
type: Date,
|
|
111
134
|
sync: true,
|
|
@@ -124,6 +147,18 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
124
147
|
type: Object,
|
|
125
148
|
},
|
|
126
149
|
|
|
150
|
+
/**
|
|
151
|
+
* The date-picker's date metadata controller, assigned by the host. Declared as a property,
|
|
152
|
+
* and a dependency of `__updateCalendarsConfig` below, so that the calendars are handed it
|
|
153
|
+
* and subscribed whenever it arrives, rather than relying on the host assigning it before
|
|
154
|
+
* the properties that happen to trigger that observer.
|
|
155
|
+
* @protected
|
|
156
|
+
*/
|
|
157
|
+
_dateMetadataController: {
|
|
158
|
+
type: Object,
|
|
159
|
+
sync: true,
|
|
160
|
+
},
|
|
161
|
+
|
|
127
162
|
calendars: {
|
|
128
163
|
type: Array,
|
|
129
164
|
value: () => [],
|
|
@@ -138,13 +173,47 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
138
173
|
|
|
139
174
|
static get observers() {
|
|
140
175
|
return [
|
|
141
|
-
'
|
|
176
|
+
'__updateCalendarsConfig(calendars, i18n, minDate, maxDate, showWeekNumbers, isDateDisabled, _theme, _dateMetadataController)',
|
|
177
|
+
'__updateCalendarsState(calendars, selectedDate, focusedDate, enteredDate, _ignoreTaps)',
|
|
142
178
|
'__updateCancelButton(_cancelButton, i18n)',
|
|
143
|
-
'__updateTodayButton(_todayButton, i18n, minDate, maxDate, isDateDisabled)',
|
|
144
179
|
'__updateYears(years, selectedDate, _theme)',
|
|
145
180
|
];
|
|
146
181
|
}
|
|
147
182
|
|
|
183
|
+
/** @protected */
|
|
184
|
+
disconnectedCallback() {
|
|
185
|
+
super.disconnectedCallback();
|
|
186
|
+
|
|
187
|
+
this.cancelLoadVisibleDateMetadata();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** @protected */
|
|
191
|
+
updated(props) {
|
|
192
|
+
super.updated(props);
|
|
193
|
+
|
|
194
|
+
if (props.has('loading')) {
|
|
195
|
+
setOrRemoveAttribute(this, 'aria-busy', this.loading);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (props.has('i18n')) {
|
|
199
|
+
setOrRemoveAttribute(this, 'aria-label', this.i18n?.dialogAccessibleName);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (props.has('calendars') || props.has('_dateMetadataController')) {
|
|
203
|
+
this.loadVisibleDateMetadata();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (
|
|
207
|
+
props.has('_todayButton') ||
|
|
208
|
+
props.has('i18n') ||
|
|
209
|
+
props.has('minDate') ||
|
|
210
|
+
props.has('maxDate') ||
|
|
211
|
+
props.has('isDateDisabled')
|
|
212
|
+
) {
|
|
213
|
+
this.updateTodayButton();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
148
217
|
/**
|
|
149
218
|
* Whether to scroll to a sub-month position when scrolling to a date.
|
|
150
219
|
* This is active if the month scroller is not large enough to fit a
|
|
@@ -291,27 +360,60 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
291
360
|
}
|
|
292
361
|
}
|
|
293
362
|
|
|
294
|
-
/**
|
|
295
|
-
|
|
363
|
+
/**
|
|
364
|
+
* Applies the today button's label and whether today can be selected.
|
|
365
|
+
*/
|
|
366
|
+
updateTodayButton() {
|
|
367
|
+
const todayButton = this._todayButton;
|
|
296
368
|
if (todayButton) {
|
|
297
|
-
todayButton.textContent = i18n?.today;
|
|
298
|
-
todayButton.disabled = !this._isTodayAllowed(
|
|
369
|
+
todayButton.textContent = this.i18n?.today;
|
|
370
|
+
todayButton.disabled = !this._isTodayAllowed();
|
|
299
371
|
}
|
|
300
372
|
}
|
|
301
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Requests the date metadata for the months currently rendered. Months already loaded or in flight
|
|
376
|
+
* are skipped.
|
|
377
|
+
*/
|
|
378
|
+
loadVisibleDateMetadata() {
|
|
379
|
+
const controller = this._dateMetadataController;
|
|
380
|
+
if (!controller) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
// Reduced to month indexes so the outermost months can be picked with plain arithmetic.
|
|
384
|
+
const indexes = (this.calendars ?? [])
|
|
385
|
+
.map((calendar) => calendar.month)
|
|
386
|
+
.filter(Boolean)
|
|
387
|
+
.map((month) => monthIndex(month));
|
|
388
|
+
if (indexes.length === 0) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
controller.ensureRangeLoaded(monthDate(Math.min(...indexes)), monthDate(Math.max(...indexes)));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Drops a date metadata load that navigating has scheduled but that has not run yet.
|
|
396
|
+
*/
|
|
397
|
+
cancelLoadVisibleDateMetadata() {
|
|
398
|
+
this._loadDateMetadataDebouncer?.cancel();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Config that changes rarely (locale, allowed range, week numbers, theme).
|
|
403
|
+
* Split from the interaction state below so a keyboard/selection update
|
|
404
|
+
* does not re-assign every config property on every calendar.
|
|
405
|
+
* @private
|
|
406
|
+
*/
|
|
302
407
|
// eslint-disable-next-line @typescript-eslint/max-params
|
|
303
|
-
|
|
408
|
+
__updateCalendarsConfig(
|
|
304
409
|
calendars,
|
|
305
410
|
i18n,
|
|
306
411
|
minDate,
|
|
307
412
|
maxDate,
|
|
308
|
-
selectedDate,
|
|
309
|
-
focusedDate,
|
|
310
413
|
showWeekNumbers,
|
|
311
|
-
ignoreTaps,
|
|
312
|
-
theme,
|
|
313
414
|
isDateDisabled,
|
|
314
|
-
|
|
415
|
+
theme,
|
|
416
|
+
dateMetadataController,
|
|
315
417
|
) {
|
|
316
418
|
if (calendars?.length) {
|
|
317
419
|
calendars.forEach((calendar) => {
|
|
@@ -319,17 +421,38 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
319
421
|
calendar.minDate = minDate;
|
|
320
422
|
calendar.maxDate = maxDate;
|
|
321
423
|
calendar.isDateDisabled = isDateDisabled;
|
|
424
|
+
calendar._dateMetadataController = dateMetadataController;
|
|
425
|
+
// Subscribe to controller updates (subscribing again is a no-op).
|
|
426
|
+
dateMetadataController?.subscribe(calendar);
|
|
427
|
+
calendar.showWeekNumbers = showWeekNumbers;
|
|
428
|
+
|
|
429
|
+
setOrRemoveAttribute(calendar, 'theme', theme);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Debounced variant used while navigating, so a continuous scroll triggers one load once it
|
|
436
|
+
* settles instead of a request per intermediate position.
|
|
437
|
+
* @private
|
|
438
|
+
*/
|
|
439
|
+
__scheduleLoadVisibleDateMetadata() {
|
|
440
|
+
this._loadDateMetadataDebouncer = Debouncer.debounce(this._loadDateMetadataDebouncer, timeOut.after(200), () =>
|
|
441
|
+
this.loadVisibleDateMetadata(),
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Interaction state that changes often (focus, selection, entered date, taps).
|
|
447
|
+
* @private
|
|
448
|
+
*/
|
|
449
|
+
__updateCalendarsState(calendars, selectedDate, focusedDate, enteredDate, ignoreTaps) {
|
|
450
|
+
if (calendars?.length) {
|
|
451
|
+
calendars.forEach((calendar) => {
|
|
322
452
|
calendar.focusedDate = focusedDate;
|
|
323
453
|
calendar.selectedDate = selectedDate;
|
|
324
|
-
calendar.showWeekNumbers = showWeekNumbers;
|
|
325
|
-
calendar.ignoreTaps = ignoreTaps;
|
|
326
454
|
calendar.enteredDate = enteredDate;
|
|
327
|
-
|
|
328
|
-
if (theme) {
|
|
329
|
-
calendar.setAttribute('theme', theme);
|
|
330
|
-
} else {
|
|
331
|
-
calendar.removeAttribute('theme');
|
|
332
|
-
}
|
|
455
|
+
calendar.ignoreTaps = ignoreTaps;
|
|
333
456
|
});
|
|
334
457
|
}
|
|
335
458
|
}
|
|
@@ -340,11 +463,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
340
463
|
years.forEach((year) => {
|
|
341
464
|
year.selectedDate = selectedDate;
|
|
342
465
|
|
|
343
|
-
|
|
344
|
-
year.setAttribute('theme', theme);
|
|
345
|
-
} else {
|
|
346
|
-
year.removeAttribute('theme');
|
|
347
|
-
}
|
|
466
|
+
setOrRemoveAttribute(year, 'theme', theme);
|
|
348
467
|
});
|
|
349
468
|
}
|
|
350
469
|
}
|
|
@@ -354,7 +473,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
354
473
|
* @protected
|
|
355
474
|
*/
|
|
356
475
|
_selectDate(dateToSelect) {
|
|
357
|
-
if (!this.
|
|
476
|
+
if (!this._dateSelectable(dateToSelect)) {
|
|
358
477
|
return false;
|
|
359
478
|
}
|
|
360
479
|
this.selectedDate = dateToSelect;
|
|
@@ -420,11 +539,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
420
539
|
* @private
|
|
421
540
|
*/
|
|
422
541
|
_calculateWeekScrollOffset(date) {
|
|
423
|
-
|
|
424
|
-
const temp = new Date(0, 0);
|
|
425
|
-
temp.setFullYear(date.getFullYear());
|
|
426
|
-
temp.setMonth(date.getMonth());
|
|
427
|
-
temp.setDate(1);
|
|
542
|
+
const temp = firstOfMonth(date);
|
|
428
543
|
// Determine week (=row index) of date within the month
|
|
429
544
|
let week = 0;
|
|
430
545
|
while (temp.getDate() < date.getDate()) {
|
|
@@ -452,12 +567,14 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
452
567
|
const monthPosition = this._monthScroller.position;
|
|
453
568
|
this._visibleMonthIndex = Math.floor(monthPosition);
|
|
454
569
|
this._yearScroller.position = (monthPosition + this._originDate.getMonth()) / 12;
|
|
570
|
+
this.__scheduleLoadVisibleDateMetadata();
|
|
455
571
|
}
|
|
456
572
|
|
|
457
573
|
/** @private */
|
|
458
574
|
_repositionMonthScroller() {
|
|
459
575
|
this._monthScroller.position = this._yearScroller.position * 12 - this._originDate.getMonth();
|
|
460
576
|
this._visibleMonthIndex = Math.floor(this._monthScroller.position);
|
|
577
|
+
this.__scheduleLoadVisibleDateMetadata();
|
|
461
578
|
}
|
|
462
579
|
|
|
463
580
|
/** @private */
|
|
@@ -624,8 +741,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
624
741
|
|
|
625
742
|
/** @private */
|
|
626
743
|
_differenceInMonths(date1, date2) {
|
|
627
|
-
|
|
628
|
-
return months - date2.getMonth() + date1.getMonth();
|
|
744
|
+
return monthIndex(date1) - monthIndex(date2);
|
|
629
745
|
}
|
|
630
746
|
|
|
631
747
|
/** @private */
|
|
@@ -853,13 +969,11 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
853
969
|
|
|
854
970
|
/** @private */
|
|
855
971
|
_getDateDiff(months, days) {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
}
|
|
862
|
-
return date;
|
|
972
|
+
return createDate(
|
|
973
|
+
this.focusedDate.getFullYear(),
|
|
974
|
+
this.focusedDate.getMonth() + months,
|
|
975
|
+
days ? this.focusedDate.getDate() + days : 1,
|
|
976
|
+
);
|
|
863
977
|
}
|
|
864
978
|
|
|
865
979
|
/** @private */
|
|
@@ -889,16 +1003,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
889
1003
|
|
|
890
1004
|
/** @private */
|
|
891
1005
|
_moveFocusInsideMonth(focusedDate, property) {
|
|
892
|
-
const dateToFocus =
|
|
893
|
-
dateToFocus.setFullYear(focusedDate.getFullYear());
|
|
894
|
-
|
|
895
|
-
if (property === 'minDate') {
|
|
896
|
-
dateToFocus.setMonth(focusedDate.getMonth());
|
|
897
|
-
dateToFocus.setDate(1);
|
|
898
|
-
} else {
|
|
899
|
-
dateToFocus.setMonth(focusedDate.getMonth() + 1);
|
|
900
|
-
dateToFocus.setDate(0);
|
|
901
|
-
}
|
|
1006
|
+
const dateToFocus = property === 'minDate' ? firstOfMonth(focusedDate) : lastOfMonth(focusedDate);
|
|
902
1007
|
|
|
903
1008
|
if (this._dateAllowed(dateToFocus)) {
|
|
904
1009
|
this.focusDate(dateToFocus);
|
|
@@ -917,17 +1022,18 @@ export const DatePickerOverlayContentMixin = (superClass) =>
|
|
|
917
1022
|
}
|
|
918
1023
|
|
|
919
1024
|
/** @private */
|
|
920
|
-
|
|
921
|
-
return
|
|
1025
|
+
_dateSelectable(date) {
|
|
1026
|
+
return dateSelectable(date, this.minDate, this.maxDate, this.isDateDisabled, this._dateMetadataController);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/** @private */
|
|
1030
|
+
_isTodayAllowed() {
|
|
1031
|
+
return this._dateSelectable(this._getTodayMidnight());
|
|
922
1032
|
}
|
|
923
1033
|
|
|
924
1034
|
/** @private */
|
|
925
1035
|
_getTodayMidnight() {
|
|
926
1036
|
const today = new Date();
|
|
927
|
-
|
|
928
|
-
todayMidnight.setFullYear(today.getFullYear());
|
|
929
|
-
todayMidnight.setMonth(today.getMonth());
|
|
930
|
-
todayMidnight.setDate(today.getDate());
|
|
931
|
-
return todayMidnight;
|
|
1037
|
+
return createDate(today.getFullYear(), today.getMonth(), today.getDate());
|
|
932
1038
|
}
|
|
933
1039
|
};
|
|
@@ -12,12 +12,14 @@ import { html, LitElement } from 'lit';
|
|
|
12
12
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
13
13
|
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
14
14
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
15
|
+
import { loaderStyles } from '@vaadin/component-base/src/styles/loader-styles.js';
|
|
15
16
|
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
|
|
16
17
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
17
18
|
import { overlayContentStyles } from './styles/vaadin-date-picker-overlay-content-base-styles.js';
|
|
18
19
|
import { DatePickerOverlayContentMixin } from './vaadin-date-picker-overlay-content-mixin.js';
|
|
19
20
|
|
|
20
21
|
/**
|
|
22
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
21
23
|
* @customElement vaadin-date-picker-overlay-content
|
|
22
24
|
* @extends HTMLElement
|
|
23
25
|
* @private
|
|
@@ -30,7 +32,7 @@ class DatePickerOverlayContent extends DatePickerOverlayContentMixin(
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
static get styles() {
|
|
33
|
-
return overlayContentStyles;
|
|
35
|
+
return [loaderStyles, overlayContentStyles];
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
static get lumoInjector() {
|
|
@@ -43,6 +45,8 @@ class DatePickerOverlayContent extends DatePickerOverlayContentMixin(
|
|
|
43
45
|
<slot name="months"></slot>
|
|
44
46
|
<slot name="years"></slot>
|
|
45
47
|
|
|
48
|
+
<div part="loader" aria-hidden="true"></div>
|
|
49
|
+
|
|
46
50
|
<div role="toolbar" part="toolbar">
|
|
47
51
|
<slot name="today-button"></slot>
|
|
48
52
|
<div
|
|
@@ -16,6 +16,7 @@ import { DatePickerOverlayMixin } from './vaadin-date-picker-overlay-mixin.js';
|
|
|
16
16
|
/**
|
|
17
17
|
* An element used internally by `<vaadin-date-picker>`. Not intended to be used separately.
|
|
18
18
|
*
|
|
19
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
19
20
|
* @customElement vaadin-date-picker-overlay
|
|
20
21
|
* @extends HTMLElement
|
|
21
22
|
* @private
|
|
@@ -13,11 +13,12 @@ import { datePickerYearStyles } from './styles/vaadin-date-picker-year-base-styl
|
|
|
13
13
|
/**
|
|
14
14
|
* An element used internally by `<vaadin-date-picker>`. Not intended to be used separately.
|
|
15
15
|
*
|
|
16
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
16
17
|
* @customElement vaadin-date-picker-year
|
|
17
18
|
* @extends HTMLElement
|
|
18
19
|
* @private
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
+
class DatePickerYear extends ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))) {
|
|
21
22
|
static get is() {
|
|
22
23
|
return 'vaadin-date-picker-year';
|
|
23
24
|
}
|
|
@@ -63,3 +64,5 @@ export class DatePickerYear extends ThemableMixin(PolylitMixin(LumoInjectionMixi
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
defineCustomElement(DatePickerYear);
|
|
67
|
+
|
|
68
|
+
export { DatePickerYear };
|
|
@@ -7,7 +7,13 @@ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
|
7
7
|
import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
8
8
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
9
9
|
import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
|
|
10
|
-
export {
|
|
10
|
+
export {
|
|
11
|
+
DatePickerDate,
|
|
12
|
+
DatePickerDateMetadata,
|
|
13
|
+
DatePickerDateMetadataProvider,
|
|
14
|
+
DatePickerDateRange,
|
|
15
|
+
DatePickerI18n,
|
|
16
|
+
} from './vaadin-date-picker-mixin.js';
|
|
11
17
|
|
|
12
18
|
/**
|
|
13
19
|
* Fired when the user commits a value change.
|
|
@@ -128,6 +134,7 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
|
|
|
128
134
|
* ----------------------|--------------------
|
|
129
135
|
* `years-toggle-button` | Fullscreen mode years scroller toggle
|
|
130
136
|
* `toolbar` | Toolbar with slotted buttons
|
|
137
|
+
* `loader` | Loading spinner shown while the date metadata provider is resolving
|
|
131
138
|
*
|
|
132
139
|
* The following state attributes are available on the `<vaadin-date-picker-overlay-content>` element:
|
|
133
140
|
*
|
|
@@ -136,6 +143,7 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
|
|
|
136
143
|
* `desktop` | Set when the overlay content is in desktop mode
|
|
137
144
|
* `fullscreen` | Set when the overlay content is in fullscreen mode
|
|
138
145
|
* `years-visible` | Set when the year scroller is visible in fullscreen mode
|
|
146
|
+
* `loading` | Set while the date metadata provider is resolving
|
|
139
147
|
*
|
|
140
148
|
* In order to style the month calendar, use `<vaadin-month-calendar>` shadow DOM parts:
|
|
141
149
|
*
|
|
@@ -148,6 +156,7 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
|
|
|
148
156
|
* `week-number` | Week number element
|
|
149
157
|
* `date` | Date element
|
|
150
158
|
* `disabled` | Disabled date element
|
|
159
|
+
* `loading` | Date element in a month whose metadata is currently being fetched
|
|
151
160
|
* `focused` | Focused date element
|
|
152
161
|
* `selected` | Selected date element
|
|
153
162
|
* `today` | Date element corresponding to the current day
|
|
@@ -92,6 +92,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
|
|
|
92
92
|
* ----------------------|--------------------
|
|
93
93
|
* `years-toggle-button` | Fullscreen mode years scroller toggle
|
|
94
94
|
* `toolbar` | Toolbar with slotted buttons
|
|
95
|
+
* `loader` | Loading spinner shown while the date metadata provider is resolving
|
|
95
96
|
*
|
|
96
97
|
* The following state attributes are available on the `<vaadin-date-picker-overlay-content>` element:
|
|
97
98
|
*
|
|
@@ -100,6 +101,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
|
|
|
100
101
|
* `desktop` | Set when the overlay content is in desktop mode
|
|
101
102
|
* `fullscreen` | Set when the overlay content is in fullscreen mode
|
|
102
103
|
* `years-visible` | Set when the year scroller is visible in fullscreen mode
|
|
104
|
+
* `loading` | Set while the date metadata provider is resolving
|
|
103
105
|
*
|
|
104
106
|
* In order to style the month calendar, use `<vaadin-month-calendar>` shadow DOM parts:
|
|
105
107
|
*
|
|
@@ -112,6 +114,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
|
|
|
112
114
|
* `week-number` | Week number element
|
|
113
115
|
* `date` | Date element
|
|
114
116
|
* `disabled` | Disabled date element
|
|
117
|
+
* `loading` | Date element in a month whose metadata is currently being fetched
|
|
115
118
|
* `focused` | Focused date element
|
|
116
119
|
* `selected` | Selected date element
|
|
117
120
|
* `today` | Date element corresponding to the current day
|
|
@@ -150,6 +153,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
|
|
|
150
153
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
151
154
|
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
152
155
|
*
|
|
156
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
153
157
|
* @customElement vaadin-date-picker
|
|
154
158
|
* @extends HTMLElement
|
|
155
159
|
*/
|
|
@@ -4,8 +4,17 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
|
|
7
|
+
import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
|
|
7
8
|
import { addListener } from '@vaadin/component-base/src/gestures.js';
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
dateAllowed,
|
|
11
|
+
dateEquals,
|
|
12
|
+
dateSelectable,
|
|
13
|
+
firstOfMonth,
|
|
14
|
+
getISOWeekNumber,
|
|
15
|
+
lastOfMonth,
|
|
16
|
+
normalizeDate,
|
|
17
|
+
} from './vaadin-date-picker-helper.js';
|
|
9
18
|
|
|
10
19
|
export const MonthCalendarMixin = (superClass) =>
|
|
11
20
|
class MonthCalendarMixinClass extends FocusMixin(superClass) {
|
|
@@ -87,6 +96,18 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
87
96
|
value: () => false,
|
|
88
97
|
},
|
|
89
98
|
|
|
99
|
+
/**
|
|
100
|
+
* The date-picker's controller resolving the metadata returned by its
|
|
101
|
+
* `dateMetadataProvider`. Assigned by the overlay content, which also subscribes this
|
|
102
|
+
* calendar to it.
|
|
103
|
+
* @protected
|
|
104
|
+
*/
|
|
105
|
+
_dateMetadataController: {
|
|
106
|
+
type: Object,
|
|
107
|
+
attribute: false,
|
|
108
|
+
sync: true,
|
|
109
|
+
},
|
|
110
|
+
|
|
90
111
|
enteredDate: {
|
|
91
112
|
type: Date,
|
|
92
113
|
},
|
|
@@ -148,17 +169,8 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
148
169
|
* @protected
|
|
149
170
|
*/
|
|
150
171
|
__computeDisabled(month, minDate, maxDate) {
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
firstDate.setFullYear(month.getFullYear());
|
|
154
|
-
firstDate.setMonth(month.getMonth());
|
|
155
|
-
firstDate.setDate(1);
|
|
156
|
-
|
|
157
|
-
// Last day of the month
|
|
158
|
-
const lastDate = new Date(0, 0);
|
|
159
|
-
lastDate.setFullYear(month.getFullYear());
|
|
160
|
-
lastDate.setMonth(month.getMonth() + 1);
|
|
161
|
-
lastDate.setDate(0);
|
|
172
|
+
const firstDate = firstOfMonth(month);
|
|
173
|
+
const lastDate = lastOfMonth(month);
|
|
162
174
|
|
|
163
175
|
if (
|
|
164
176
|
minDate &&
|
|
@@ -225,11 +237,8 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
225
237
|
|
|
226
238
|
/** @private */
|
|
227
239
|
__focusedDateChanged(focusedDate, days) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
} else {
|
|
231
|
-
this.setAttribute('aria-hidden', 'true');
|
|
232
|
-
}
|
|
240
|
+
const hasFocusedDate = Array.isArray(days) && days.some((date) => dateEquals(date, focusedDate));
|
|
241
|
+
setOrRemoveAttribute(this, 'aria-hidden', !hasFocusedDate);
|
|
233
242
|
}
|
|
234
243
|
|
|
235
244
|
/** @protected */
|
|
@@ -253,11 +262,7 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
253
262
|
if (month === undefined || i18n === undefined) {
|
|
254
263
|
return [];
|
|
255
264
|
}
|
|
256
|
-
|
|
257
|
-
const date = new Date(0, 0);
|
|
258
|
-
date.setFullYear(month.getFullYear());
|
|
259
|
-
date.setMonth(month.getMonth());
|
|
260
|
-
date.setDate(1);
|
|
265
|
+
const date = firstOfMonth(month);
|
|
261
266
|
|
|
262
267
|
// Rewind to first day of the week.
|
|
263
268
|
while (date.getDay() !== i18n.firstDayOfWeek) {
|
|
@@ -330,11 +335,7 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
330
335
|
|
|
331
336
|
/** @private */
|
|
332
337
|
_showWeekNumbersChanged(showWeekNumbers, i18n) {
|
|
333
|
-
|
|
334
|
-
this.setAttribute('week-numbers', '');
|
|
335
|
-
} else {
|
|
336
|
-
this.removeAttribute('week-numbers');
|
|
337
|
-
}
|
|
338
|
+
this.toggleAttribute('week-numbers', this.__computeShowWeekSeparator(showWeekNumbers, i18n));
|
|
338
339
|
}
|
|
339
340
|
|
|
340
341
|
// eslint-disable-next-line @typescript-eslint/max-params
|
|
@@ -345,6 +346,10 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
345
346
|
result.push('disabled');
|
|
346
347
|
}
|
|
347
348
|
|
|
349
|
+
if (date && this.__isMonthPending()) {
|
|
350
|
+
result.push('loading');
|
|
351
|
+
}
|
|
352
|
+
|
|
348
353
|
if (dateEquals(date, focusedDate) && (hasFocus || dateEquals(date, enteredDate))) {
|
|
349
354
|
result.push('focused');
|
|
350
355
|
}
|
|
@@ -365,6 +370,12 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
365
370
|
result.push('future');
|
|
366
371
|
}
|
|
367
372
|
|
|
373
|
+
// Only a string can name parts, so anything else the provider set is ignored.
|
|
374
|
+
const customParts = date && this._dateMetadataController?.getMetadata(date)?.part;
|
|
375
|
+
if (customParts && typeof customParts === 'string') {
|
|
376
|
+
result.push(customParts);
|
|
377
|
+
}
|
|
378
|
+
|
|
368
379
|
return result.join(' ');
|
|
369
380
|
}
|
|
370
381
|
|
|
@@ -378,14 +389,29 @@ export const MonthCalendarMixin = (superClass) =>
|
|
|
378
389
|
return String(this.__isDaySelected(date, selectedDate));
|
|
379
390
|
}
|
|
380
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Whether the displayed month is currently being fetched, which is the same state the overlay
|
|
394
|
+
* reports with its spinner. A month that has not been asked about yet is not pending: nothing is
|
|
395
|
+
* loading, so there is nothing to report.
|
|
396
|
+
* @private
|
|
397
|
+
*/
|
|
398
|
+
__isMonthPending() {
|
|
399
|
+
return !!this._dateMetadataController?.isMonthPending(this.month);
|
|
400
|
+
}
|
|
401
|
+
|
|
381
402
|
/** @private */
|
|
382
403
|
__isDayDisabled(date, minDate, maxDate, isDateDisabled) {
|
|
383
|
-
return !
|
|
404
|
+
return !dateSelectable(date, minDate, maxDate, isDateDisabled, this._dateMetadataController);
|
|
384
405
|
}
|
|
385
406
|
|
|
386
407
|
/** @private */
|
|
387
408
|
__computeDayAriaDisabled(date, min, max, isDateDisabled) {
|
|
388
|
-
if (date === undefined
|
|
409
|
+
if (date === undefined) {
|
|
410
|
+
return 'false';
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const hasProvider = !!this._dateMetadataController?.provider;
|
|
414
|
+
if (!hasProvider && min === undefined && max === undefined && isDateDisabled === undefined) {
|
|
389
415
|
return 'false';
|
|
390
416
|
}
|
|
391
417
|
|
|
@@ -12,6 +12,7 @@ import { monthCalendarStyles } from './styles/vaadin-month-calendar-base-styles.
|
|
|
12
12
|
import { MonthCalendarMixin } from './vaadin-month-calendar-mixin.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
15
16
|
* @customElement vaadin-month-calendar
|
|
16
17
|
* @extends HTMLElement
|
|
17
18
|
* @private
|