@vaadin/date-picker 25.3.0-alpha8 → 25.3.0-beta1

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.
@@ -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 { dateAfterXMonths, dateAllowed, dateEquals, getClosestDate } from './vaadin-date-picker-helper.js';
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,14 +173,47 @@ export const DatePickerOverlayContentMixin = (superClass) =>
138
173
 
139
174
  static get observers() {
140
175
  return [
141
- '__updateCalendarsConfig(calendars, i18n, minDate, maxDate, showWeekNumbers, isDateDisabled, _theme)',
176
+ '__updateCalendarsConfig(calendars, i18n, minDate, maxDate, showWeekNumbers, isDateDisabled, _theme, _dateMetadataController)',
142
177
  '__updateCalendarsState(calendars, selectedDate, focusedDate, enteredDate, _ignoreTaps)',
143
178
  '__updateCancelButton(_cancelButton, i18n)',
144
- '__updateTodayButton(_todayButton, i18n, minDate, maxDate, isDateDisabled)',
145
179
  '__updateYears(years, selectedDate, _theme)',
146
180
  ];
147
181
  }
148
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
+
149
217
  /**
150
218
  * Whether to scroll to a sub-month position when scrolling to a date.
151
219
  * This is active if the month scroller is not large enough to fit a
@@ -200,6 +268,8 @@ export const DatePickerOverlayContentMixin = (superClass) =>
200
268
 
201
269
  reset() {
202
270
  this._closeYearScroller();
271
+ this._monthScroller?.reset();
272
+ this._yearScroller?.reset();
203
273
  }
204
274
 
205
275
  /**
@@ -292,14 +362,44 @@ export const DatePickerOverlayContentMixin = (superClass) =>
292
362
  }
293
363
  }
294
364
 
295
- /** @private */
296
- __updateTodayButton(todayButton, i18n, minDate, maxDate, isDateDisabled) {
365
+ /**
366
+ * Applies the today button's label and whether today can be selected.
367
+ */
368
+ updateTodayButton() {
369
+ const todayButton = this._todayButton;
297
370
  if (todayButton) {
298
- todayButton.textContent = i18n?.today;
299
- todayButton.disabled = !this._isTodayAllowed(minDate, maxDate, isDateDisabled);
371
+ todayButton.textContent = this.i18n?.today;
372
+ todayButton.disabled = !this._isTodayAllowed();
300
373
  }
301
374
  }
302
375
 
376
+ /**
377
+ * Requests the date metadata for the months currently rendered. Months already loaded or in flight
378
+ * are skipped.
379
+ */
380
+ loadVisibleDateMetadata() {
381
+ const controller = this._dateMetadataController;
382
+ if (!controller) {
383
+ return;
384
+ }
385
+ // Reduced to month indexes so the outermost months can be picked with plain arithmetic.
386
+ const indexes = (this.calendars ?? [])
387
+ .map((calendar) => calendar.month)
388
+ .filter(Boolean)
389
+ .map((month) => monthIndex(month));
390
+ if (indexes.length === 0) {
391
+ return;
392
+ }
393
+ controller.ensureRangeLoaded(monthDate(Math.min(...indexes)), monthDate(Math.max(...indexes)));
394
+ }
395
+
396
+ /**
397
+ * Drops a date metadata load that navigating has scheduled but that has not run yet.
398
+ */
399
+ cancelLoadVisibleDateMetadata() {
400
+ this._loadDateMetadataDebouncer?.cancel();
401
+ }
402
+
303
403
  /**
304
404
  * Config that changes rarely (locale, allowed range, week numbers, theme).
305
405
  * Split from the interaction state below so a keyboard/selection update
@@ -307,24 +407,43 @@ export const DatePickerOverlayContentMixin = (superClass) =>
307
407
  * @private
308
408
  */
309
409
  // eslint-disable-next-line @typescript-eslint/max-params
310
- __updateCalendarsConfig(calendars, i18n, minDate, maxDate, showWeekNumbers, isDateDisabled, theme) {
410
+ __updateCalendarsConfig(
411
+ calendars,
412
+ i18n,
413
+ minDate,
414
+ maxDate,
415
+ showWeekNumbers,
416
+ isDateDisabled,
417
+ theme,
418
+ dateMetadataController,
419
+ ) {
311
420
  if (calendars?.length) {
312
421
  calendars.forEach((calendar) => {
313
422
  calendar.i18n = i18n;
314
423
  calendar.minDate = minDate;
315
424
  calendar.maxDate = maxDate;
316
425
  calendar.isDateDisabled = isDateDisabled;
426
+ calendar._dateMetadataController = dateMetadataController;
427
+ // Subscribe to controller updates (subscribing again is a no-op).
428
+ dateMetadataController?.subscribe(calendar);
317
429
  calendar.showWeekNumbers = showWeekNumbers;
318
430
 
319
- if (theme) {
320
- calendar.setAttribute('theme', theme);
321
- } else {
322
- calendar.removeAttribute('theme');
323
- }
431
+ setOrRemoveAttribute(calendar, 'theme', theme);
324
432
  });
325
433
  }
326
434
  }
327
435
 
436
+ /**
437
+ * Debounced variant used while navigating, so a continuous scroll triggers one load once it
438
+ * settles instead of a request per intermediate position.
439
+ * @private
440
+ */
441
+ __scheduleLoadVisibleDateMetadata() {
442
+ this._loadDateMetadataDebouncer = Debouncer.debounce(this._loadDateMetadataDebouncer, timeOut.after(200), () =>
443
+ this.loadVisibleDateMetadata(),
444
+ );
445
+ }
446
+
328
447
  /**
329
448
  * Interaction state that changes often (focus, selection, entered date, taps).
330
449
  * @private
@@ -346,11 +465,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
346
465
  years.forEach((year) => {
347
466
  year.selectedDate = selectedDate;
348
467
 
349
- if (theme) {
350
- year.setAttribute('theme', theme);
351
- } else {
352
- year.removeAttribute('theme');
353
- }
468
+ setOrRemoveAttribute(year, 'theme', theme);
354
469
  });
355
470
  }
356
471
  }
@@ -360,7 +475,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
360
475
  * @protected
361
476
  */
362
477
  _selectDate(dateToSelect) {
363
- if (!this._dateAllowed(dateToSelect)) {
478
+ if (!this._dateSelectable(dateToSelect)) {
364
479
  return false;
365
480
  }
366
481
  this.selectedDate = dateToSelect;
@@ -426,11 +541,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
426
541
  * @private
427
542
  */
428
543
  _calculateWeekScrollOffset(date) {
429
- // Get first day of month
430
- const temp = new Date(0, 0);
431
- temp.setFullYear(date.getFullYear());
432
- temp.setMonth(date.getMonth());
433
- temp.setDate(1);
544
+ const temp = firstOfMonth(date);
434
545
  // Determine week (=row index) of date within the month
435
546
  let week = 0;
436
547
  while (temp.getDate() < date.getDate()) {
@@ -458,12 +569,14 @@ export const DatePickerOverlayContentMixin = (superClass) =>
458
569
  const monthPosition = this._monthScroller.position;
459
570
  this._visibleMonthIndex = Math.floor(monthPosition);
460
571
  this._yearScroller.position = (monthPosition + this._originDate.getMonth()) / 12;
572
+ this.__scheduleLoadVisibleDateMetadata();
461
573
  }
462
574
 
463
575
  /** @private */
464
576
  _repositionMonthScroller() {
465
577
  this._monthScroller.position = this._yearScroller.position * 12 - this._originDate.getMonth();
466
578
  this._visibleMonthIndex = Math.floor(this._monthScroller.position);
579
+ this.__scheduleLoadVisibleDateMetadata();
467
580
  }
468
581
 
469
582
  /** @private */
@@ -630,8 +743,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
630
743
 
631
744
  /** @private */
632
745
  _differenceInMonths(date1, date2) {
633
- const months = (date1.getFullYear() - date2.getFullYear()) * 12;
634
- return months - date2.getMonth() + date1.getMonth();
746
+ return monthIndex(date1) - monthIndex(date2);
635
747
  }
636
748
 
637
749
  /** @private */
@@ -859,13 +971,11 @@ export const DatePickerOverlayContentMixin = (superClass) =>
859
971
 
860
972
  /** @private */
861
973
  _getDateDiff(months, days) {
862
- const date = new Date(0, 0);
863
- date.setFullYear(this.focusedDate.getFullYear());
864
- date.setMonth(this.focusedDate.getMonth() + months);
865
- if (days) {
866
- date.setDate(this.focusedDate.getDate() + days);
867
- }
868
- return date;
974
+ return createDate(
975
+ this.focusedDate.getFullYear(),
976
+ this.focusedDate.getMonth() + months,
977
+ days ? this.focusedDate.getDate() + days : 1,
978
+ );
869
979
  }
870
980
 
871
981
  /** @private */
@@ -895,16 +1005,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
895
1005
 
896
1006
  /** @private */
897
1007
  _moveFocusInsideMonth(focusedDate, property) {
898
- const dateToFocus = new Date(0, 0);
899
- dateToFocus.setFullYear(focusedDate.getFullYear());
900
-
901
- if (property === 'minDate') {
902
- dateToFocus.setMonth(focusedDate.getMonth());
903
- dateToFocus.setDate(1);
904
- } else {
905
- dateToFocus.setMonth(focusedDate.getMonth() + 1);
906
- dateToFocus.setDate(0);
907
- }
1008
+ const dateToFocus = property === 'minDate' ? firstOfMonth(focusedDate) : lastOfMonth(focusedDate);
908
1009
 
909
1010
  if (this._dateAllowed(dateToFocus)) {
910
1011
  this.focusDate(dateToFocus);
@@ -923,17 +1024,18 @@ export const DatePickerOverlayContentMixin = (superClass) =>
923
1024
  }
924
1025
 
925
1026
  /** @private */
926
- _isTodayAllowed(min, max, isDateDisabled) {
927
- return this._dateAllowed(this._getTodayMidnight(), min, max, isDateDisabled);
1027
+ _dateSelectable(date) {
1028
+ return dateSelectable(date, this.minDate, this.maxDate, this.isDateDisabled, this._dateMetadataController);
1029
+ }
1030
+
1031
+ /** @private */
1032
+ _isTodayAllowed() {
1033
+ return this._dateSelectable(this._getTodayMidnight());
928
1034
  }
929
1035
 
930
1036
  /** @private */
931
1037
  _getTodayMidnight() {
932
1038
  const today = new Date();
933
- const todayMidnight = new Date(0, 0);
934
- todayMidnight.setFullYear(today.getFullYear());
935
- todayMidnight.setMonth(today.getMonth());
936
- todayMidnight.setDate(today.getDate());
937
- return todayMidnight;
1039
+ return createDate(today.getFullYear(), today.getMonth(), today.getDate());
938
1040
  }
939
1041
  };
@@ -12,6 +12,7 @@ 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';
@@ -31,7 +32,7 @@ class DatePickerOverlayContent extends DatePickerOverlayContentMixin(
31
32
  }
32
33
 
33
34
  static get styles() {
34
- return overlayContentStyles;
35
+ return [loaderStyles, overlayContentStyles];
35
36
  }
36
37
 
37
38
  static get lumoInjector() {
@@ -44,6 +45,8 @@ class DatePickerOverlayContent extends DatePickerOverlayContentMixin(
44
45
  <slot name="months"></slot>
45
46
  <slot name="years"></slot>
46
47
 
48
+ <div part="loader" aria-hidden="true"></div>
49
+
47
50
  <div role="toolbar" part="toolbar">
48
51
  <slot name="today-button"></slot>
49
52
  <div
@@ -18,7 +18,7 @@ import { datePickerYearStyles } from './styles/vaadin-date-picker-year-base-styl
18
18
  * @extends HTMLElement
19
19
  * @private
20
20
  */
21
- export class DatePickerYear extends ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))) {
21
+ class DatePickerYear extends ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))) {
22
22
  static get is() {
23
23
  return 'vaadin-date-picker-year';
24
24
  }
@@ -64,3 +64,5 @@ export class DatePickerYear extends ThemableMixin(PolylitMixin(LumoInjectionMixi
64
64
  }
65
65
 
66
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 { DatePickerDate, DatePickerI18n } from './vaadin-date-picker-mixin.js';
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.
@@ -72,12 +78,6 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
72
78
  *
73
79
  * ### Styling
74
80
  *
75
- * The following custom properties are available for styling:
76
- *
77
- * Custom property | Description | Default
78
- * -------------------------------|----------------------------|---------
79
- * `--vaadin-field-default-width` | Default width of the field | `12em`
80
- *
81
81
  * The following shadow DOM parts are available for styling:
82
82
  *
83
83
  * Part name | Description
@@ -111,6 +111,69 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
111
111
  * `opened` | Set when the overlay is opened
112
112
  * `week-numbers` | Set when week numbers are shown in the calendar
113
113
  *
114
+ * The following custom CSS properties are available for styling:
115
+ *
116
+ * Custom CSS property |
117
+ * :----------------------------------------------------------|
118
+ * | `--vaadin-date-picker-date-border-radius` |
119
+ * | `--vaadin-date-picker-date-disabled-color` |
120
+ * | `--vaadin-date-picker-date-height` |
121
+ * | `--vaadin-date-picker-date-selected-background` |
122
+ * | `--vaadin-date-picker-date-selected-color` |
123
+ * | `--vaadin-date-picker-date-today-color` |
124
+ * | `--vaadin-date-picker-date-width` |
125
+ * | `--vaadin-date-picker-month-header-color` |
126
+ * | `--vaadin-date-picker-month-header-font-size` |
127
+ * | `--vaadin-date-picker-month-header-font-weight` |
128
+ * | `--vaadin-date-picker-month-padding` |
129
+ * | `--vaadin-date-picker-overlay-max-height` |
130
+ * | `--vaadin-date-picker-overlay-width` |
131
+ * | `--vaadin-date-picker-toolbar-padding` |
132
+ * | `--vaadin-date-picker-week-divider-color` |
133
+ * | `--vaadin-date-picker-week-number-color` |
134
+ * | `--vaadin-date-picker-week-number-font-size` |
135
+ * | `--vaadin-date-picker-weekday-color` |
136
+ * | `--vaadin-date-picker-weekday-font-size` |
137
+ * | `--vaadin-date-picker-weekday-font-weight` |
138
+ * | `--vaadin-date-picker-year-scroller-background` |
139
+ * | `--vaadin-date-picker-year-scroller-border-color` |
140
+ * | `--vaadin-date-picker-year-scroller-current-year-color` |
141
+ * | `--vaadin-date-picker-year-scroller-width` |
142
+ * | `--vaadin-field-default-width` |
143
+ * | `--vaadin-input-field-background` |
144
+ * | `--vaadin-input-field-border-color` |
145
+ * | `--vaadin-input-field-border-radius` |
146
+ * | `--vaadin-input-field-border-width` |
147
+ * | `--vaadin-input-field-bottom-end-radius` |
148
+ * | `--vaadin-input-field-bottom-start-radius` |
149
+ * | `--vaadin-input-field-button-text-color` |
150
+ * | `--vaadin-input-field-container-gap` |
151
+ * | `--vaadin-input-field-disabled-background` |
152
+ * | `--vaadin-input-field-disabled-text-color` |
153
+ * | `--vaadin-input-field-error-color` |
154
+ * | `--vaadin-input-field-error-font-size` |
155
+ * | `--vaadin-input-field-error-font-weight` |
156
+ * | `--vaadin-input-field-error-line-height` |
157
+ * | `--vaadin-input-field-gap` |
158
+ * | `--vaadin-input-field-helper-color` |
159
+ * | `--vaadin-input-field-helper-font-size` |
160
+ * | `--vaadin-input-field-helper-font-weight` |
161
+ * | `--vaadin-input-field-helper-line-height` |
162
+ * | `--vaadin-input-field-label-color` |
163
+ * | `--vaadin-input-field-label-font-size` |
164
+ * | `--vaadin-input-field-label-font-weight` |
165
+ * | `--vaadin-input-field-label-line-height` |
166
+ * | `--vaadin-input-field-padding` |
167
+ * | `--vaadin-input-field-placeholder-color` |
168
+ * | `--vaadin-input-field-required-indicator` |
169
+ * | `--vaadin-input-field-required-indicator-color` |
170
+ * | `--vaadin-input-field-top-end-radius` |
171
+ * | `--vaadin-input-field-top-start-radius` |
172
+ * | `--vaadin-input-field-value-color` |
173
+ * | `--vaadin-input-field-value-font-size` |
174
+ * | `--vaadin-input-field-value-font-weight` |
175
+ * | `--vaadin-input-field-value-line-height` |
176
+ *
114
177
  * ### Internal components
115
178
  *
116
179
  * In addition to `<vaadin-date-picker>` itself, the following internal
@@ -128,6 +191,7 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
128
191
  * ----------------------|--------------------
129
192
  * `years-toggle-button` | Fullscreen mode years scroller toggle
130
193
  * `toolbar` | Toolbar with slotted buttons
194
+ * `loader` | Loading spinner shown while the date metadata provider is resolving
131
195
  *
132
196
  * The following state attributes are available on the `<vaadin-date-picker-overlay-content>` element:
133
197
  *
@@ -136,6 +200,7 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
136
200
  * `desktop` | Set when the overlay content is in desktop mode
137
201
  * `fullscreen` | Set when the overlay content is in fullscreen mode
138
202
  * `years-visible` | Set when the year scroller is visible in fullscreen mode
203
+ * `loading` | Set while the date metadata provider is resolving
139
204
  *
140
205
  * In order to style the month calendar, use `<vaadin-month-calendar>` shadow DOM parts:
141
206
  *
@@ -148,6 +213,7 @@ export interface DatePickerEventMap extends HTMLElementEventMap, DatePickerCusto
148
213
  * `week-number` | Week number element
149
214
  * `date` | Date element
150
215
  * `disabled` | Disabled date element
216
+ * `loading` | Date element in a month whose metadata is currently being fetched
151
217
  * `focused` | Focused date element
152
218
  * `selected` | Selected date element
153
219
  * `today` | Date element corresponding to the current day
@@ -36,12 +36,6 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
36
36
  *
37
37
  * ### Styling
38
38
  *
39
- * The following custom properties are available for styling:
40
- *
41
- * Custom property | Description | Default
42
- * -------------------------------|----------------------------|---------
43
- * `--vaadin-field-default-width` | Default width of the field | `12em`
44
- *
45
39
  * The following shadow DOM parts are available for styling:
46
40
  *
47
41
  * Part name | Description
@@ -75,6 +69,69 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
75
69
  * `opened` | Set when the overlay is opened
76
70
  * `week-numbers` | Set when week numbers are shown in the calendar
77
71
  *
72
+ * The following custom CSS properties are available for styling:
73
+ *
74
+ * Custom CSS property |
75
+ * :----------------------------------------------------------|
76
+ * | `--vaadin-date-picker-date-border-radius` |
77
+ * | `--vaadin-date-picker-date-disabled-color` |
78
+ * | `--vaadin-date-picker-date-height` |
79
+ * | `--vaadin-date-picker-date-selected-background` |
80
+ * | `--vaadin-date-picker-date-selected-color` |
81
+ * | `--vaadin-date-picker-date-today-color` |
82
+ * | `--vaadin-date-picker-date-width` |
83
+ * | `--vaadin-date-picker-month-header-color` |
84
+ * | `--vaadin-date-picker-month-header-font-size` |
85
+ * | `--vaadin-date-picker-month-header-font-weight` |
86
+ * | `--vaadin-date-picker-month-padding` |
87
+ * | `--vaadin-date-picker-overlay-max-height` |
88
+ * | `--vaadin-date-picker-overlay-width` |
89
+ * | `--vaadin-date-picker-toolbar-padding` |
90
+ * | `--vaadin-date-picker-week-divider-color` |
91
+ * | `--vaadin-date-picker-week-number-color` |
92
+ * | `--vaadin-date-picker-week-number-font-size` |
93
+ * | `--vaadin-date-picker-weekday-color` |
94
+ * | `--vaadin-date-picker-weekday-font-size` |
95
+ * | `--vaadin-date-picker-weekday-font-weight` |
96
+ * | `--vaadin-date-picker-year-scroller-background` |
97
+ * | `--vaadin-date-picker-year-scroller-border-color` |
98
+ * | `--vaadin-date-picker-year-scroller-current-year-color` |
99
+ * | `--vaadin-date-picker-year-scroller-width` |
100
+ * | `--vaadin-field-default-width` |
101
+ * | `--vaadin-input-field-background` |
102
+ * | `--vaadin-input-field-border-color` |
103
+ * | `--vaadin-input-field-border-radius` |
104
+ * | `--vaadin-input-field-border-width` |
105
+ * | `--vaadin-input-field-bottom-end-radius` |
106
+ * | `--vaadin-input-field-bottom-start-radius` |
107
+ * | `--vaadin-input-field-button-text-color` |
108
+ * | `--vaadin-input-field-container-gap` |
109
+ * | `--vaadin-input-field-disabled-background` |
110
+ * | `--vaadin-input-field-disabled-text-color` |
111
+ * | `--vaadin-input-field-error-color` |
112
+ * | `--vaadin-input-field-error-font-size` |
113
+ * | `--vaadin-input-field-error-font-weight` |
114
+ * | `--vaadin-input-field-error-line-height` |
115
+ * | `--vaadin-input-field-gap` |
116
+ * | `--vaadin-input-field-helper-color` |
117
+ * | `--vaadin-input-field-helper-font-size` |
118
+ * | `--vaadin-input-field-helper-font-weight` |
119
+ * | `--vaadin-input-field-helper-line-height` |
120
+ * | `--vaadin-input-field-label-color` |
121
+ * | `--vaadin-input-field-label-font-size` |
122
+ * | `--vaadin-input-field-label-font-weight` |
123
+ * | `--vaadin-input-field-label-line-height` |
124
+ * | `--vaadin-input-field-padding` |
125
+ * | `--vaadin-input-field-placeholder-color` |
126
+ * | `--vaadin-input-field-required-indicator` |
127
+ * | `--vaadin-input-field-required-indicator-color` |
128
+ * | `--vaadin-input-field-top-end-radius` |
129
+ * | `--vaadin-input-field-top-start-radius` |
130
+ * | `--vaadin-input-field-value-color` |
131
+ * | `--vaadin-input-field-value-font-size` |
132
+ * | `--vaadin-input-field-value-font-weight` |
133
+ * | `--vaadin-input-field-value-line-height` |
134
+ *
78
135
  * ### Internal components
79
136
  *
80
137
  * In addition to `<vaadin-date-picker>` itself, the following internal
@@ -92,6 +149,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
92
149
  * ----------------------|--------------------
93
150
  * `years-toggle-button` | Fullscreen mode years scroller toggle
94
151
  * `toolbar` | Toolbar with slotted buttons
152
+ * `loader` | Loading spinner shown while the date metadata provider is resolving
95
153
  *
96
154
  * The following state attributes are available on the `<vaadin-date-picker-overlay-content>` element:
97
155
  *
@@ -100,6 +158,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
100
158
  * `desktop` | Set when the overlay content is in desktop mode
101
159
  * `fullscreen` | Set when the overlay content is in fullscreen mode
102
160
  * `years-visible` | Set when the year scroller is visible in fullscreen mode
161
+ * `loading` | Set while the date metadata provider is resolving
103
162
  *
104
163
  * In order to style the month calendar, use `<vaadin-month-calendar>` shadow DOM parts:
105
164
  *
@@ -112,6 +171,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
112
171
  * `week-number` | Week number element
113
172
  * `date` | Date element
114
173
  * `disabled` | Disabled date element
174
+ * `loading` | Date element in a month whose metadata is currently being fetched
115
175
  * `focused` | Focused date element
116
176
  * `selected` | Selected date element
117
177
  * `today` | Date element corresponding to the current day
@@ -37,6 +37,10 @@ template.innerHTML = `
37
37
  box-sizing: border-box;
38
38
  top: var(--vaadin-infinite-scroller-buffer-offset, 0);
39
39
  }
40
+
41
+ ::slotted(div) {
42
+ height: var(--vaadin-infinite-scroller-item-height);
43
+ }
40
44
  </style>
41
45
 
42
46
  <div id="scroller" tabindex="-1">
@@ -149,7 +153,7 @@ export class InfiniteScroller extends HTMLElement {
149
153
  this.$.scroller.scrollTop = this.itemHeight * (index - this._firstIndex) + this._buffers[0].translateY;
150
154
  } else {
151
155
  this._initialIndex = ~~index;
152
- this._reset();
156
+ this.reset();
153
157
  this._scrollDisabled = true;
154
158
  this.$.scroller.scrollTop += (index % 1) * this.itemHeight;
155
159
  this._scrollDisabled = false;
@@ -235,7 +239,7 @@ export class InfiniteScroller extends HTMLElement {
235
239
  });
236
240
 
237
241
  if (!this._buffers[0].translateY) {
238
- this._reset();
242
+ this.reset();
239
243
  }
240
244
 
241
245
  this._initDone = true;
@@ -262,7 +266,7 @@ export class InfiniteScroller extends HTMLElement {
262
266
  if (scrollTop < this._bufferHeight || scrollTop > this._initialScroll * 2 - this._bufferHeight) {
263
267
  // Scrolled near the end/beginning of the scrollable area -> reset.
264
268
  this._initialIndex = ~~this.position;
265
- this._reset();
269
+ this.reset();
266
270
  }
267
271
 
268
272
  // Check if we scrolled enough to translate the buffer positions.
@@ -288,8 +292,18 @@ export class InfiniteScroller extends HTMLElement {
288
292
  });
289
293
  }
290
294
 
291
- /** @private */
292
- _reset() {
295
+ /**
296
+ * Resets the scroller to the last starting index, and re-measures the
297
+ * item height in case the computed value of the CSS custom property
298
+ * has changed.
299
+ */
300
+ reset() {
301
+ if (!this._activated || !this.isConnected) {
302
+ return;
303
+ }
304
+
305
+ this._itemHeightVal = null;
306
+
293
307
  this._scrollDisabled = true;
294
308
  this.$.scroller.scrollTop = this._initialScroll;
295
309
  this._buffers[0].translateY = this._initialScroll - this._bufferHeight;
@@ -314,7 +328,6 @@ export class InfiniteScroller extends HTMLElement {
314
328
  this._buffers.forEach((buffer) => {
315
329
  for (let i = 0; i < this.bufferSize; i++) {
316
330
  const itemWrapper = document.createElement('div');
317
- itemWrapper.style.height = `${this.itemHeight}px`;
318
331
  itemWrapper.instance = {};
319
332
 
320
333
  const slotName = `vaadin-infinite-scroller-item-content-${generateUniqueId()}`;