@vaadin/date-picker 23.3.0-alpha2 → 24.0.0-alpha1

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,7 +5,11 @@
5
5
  */
6
6
  import '@vaadin/button/src/vaadin-button.js';
7
7
  import './vaadin-month-calendar.js';
8
- import './vaadin-infinite-scroller.js';
8
+ import './vaadin-date-picker-month-scroller.js';
9
+ import './vaadin-date-picker-year-scroller.js';
10
+ import './vaadin-date-picker-year.js';
11
+ import { flush } from '@polymer/polymer/lib/utils/flush.js';
12
+ import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
9
13
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
10
14
  import { timeOut } from '@vaadin/component-base/src/async.js';
11
15
  import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
@@ -13,8 +17,9 @@ import { Debouncer } from '@vaadin/component-base/src/debounce.js';
13
17
  import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
14
18
  import { addListener, setTouchAction } from '@vaadin/component-base/src/gestures.js';
15
19
  import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js';
20
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
16
21
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
17
- import { dateEquals, extractDateParts, getClosestDate } from './vaadin-date-picker-helper.js';
22
+ import { dateAfterXMonths, dateEquals, extractDateParts, getClosestDate } from './vaadin-date-picker-helper.js';
18
23
 
19
24
  /**
20
25
  * @extends HTMLElement
@@ -63,62 +68,17 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
63
68
  overflow: hidden;
64
69
  }
65
70
 
66
- [part='months'],
67
- [part='years'] {
68
- height: 100%;
69
- }
70
-
71
- [part='months'] {
72
- --vaadin-infinite-scroller-item-height: 270px;
73
- position: absolute;
74
- top: 0;
75
- left: 0;
76
- right: 0;
77
- bottom: 0;
78
- }
79
-
80
- #scrollers[desktop] [part='months'] {
71
+ :host([desktop]) ::slotted([slot='months']) {
81
72
  right: 50px;
82
73
  transform: none !important;
83
74
  }
84
75
 
85
- [part='years'] {
86
- --vaadin-infinite-scroller-item-height: 80px;
87
- width: 50px;
88
- position: absolute;
89
- right: 0;
90
- transform: translateX(100%);
91
- -webkit-tap-highlight-color: transparent;
92
- -webkit-user-select: none;
93
- -moz-user-select: none;
94
- user-select: none;
95
- /* Center the year scroller position. */
96
- --vaadin-infinite-scroller-buffer-offset: 50%;
97
- }
98
-
99
- #scrollers[desktop] [part='years'] {
100
- position: absolute;
76
+ :host([desktop]) ::slotted([slot='years']) {
101
77
  transform: none !important;
102
78
  }
103
79
 
104
- [part='years']::before {
105
- content: '';
106
- display: block;
107
- background: transparent;
108
- width: 0;
109
- height: 0;
110
- position: absolute;
111
- left: 0;
112
- top: 50%;
113
- transform: translateY(-50%);
114
- border-width: 6px;
115
- border-style: solid;
116
- border-color: transparent;
117
- border-left-color: #000;
118
- }
119
-
120
- :host(.animate) [part='months'],
121
- :host(.animate) [part='years'] {
80
+ :host(.animate) ::slotted([slot='months']),
81
+ :host(.animate) ::slotted([slot='years']) {
122
82
  transition: all 200ms;
123
83
  }
124
84
 
@@ -130,7 +90,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
130
90
  }
131
91
  </style>
132
92
 
133
- <div part="overlay-header" on-touchend="_preventDefault" desktop$="[[_desktopMode]]" aria-hidden="true">
93
+ <div part="overlay-header" on-touchend="_preventDefault" aria-hidden="true">
134
94
  <div part="label">[[_formatDisplayed(selectedDate, i18n.formatDate, label)]]</div>
135
95
  <div part="clear-button" hidden$="[[!selectedDate]]"></div>
136
96
  <div part="toggle-button"></div>
@@ -140,67 +100,14 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
140
100
  </div>
141
101
  </div>
142
102
 
143
- <div id="scrollers" desktop$="[[_desktopMode]]">
144
- <vaadin-infinite-scroller
145
- id="monthScroller"
146
- on-custom-scroll="_onMonthScroll"
147
- on-touchstart="_onMonthScrollTouchStart"
148
- buffer-size="3"
149
- active="[[initialPosition]]"
150
- part="months"
151
- >
152
- <template>
153
- <vaadin-month-calendar
154
- i18n="[[i18n]]"
155
- month="[[_dateAfterXMonths(index)]]"
156
- selected-date="{{selectedDate}}"
157
- focused-date="[[focusedDate]]"
158
- ignore-taps="[[_ignoreTaps]]"
159
- show-week-numbers="[[showWeekNumbers]]"
160
- min-date="[[minDate]]"
161
- max-date="[[maxDate]]"
162
- part="month"
163
- theme$="[[_theme]]"
164
- on-keydown="__onMonthCalendarKeyDown"
165
- >
166
- </vaadin-month-calendar>
167
- </template>
168
- </vaadin-infinite-scroller>
169
- <vaadin-infinite-scroller
170
- id="yearScroller"
171
- on-custom-scroll="_onYearScroll"
172
- on-touchstart="_onYearScrollTouchStart"
173
- buffer-size="12"
174
- active="[[initialPosition]]"
175
- part="years"
176
- aria-hidden="true"
177
- >
178
- <template>
179
- <div
180
- part="year-number"
181
- current$="[[_isCurrentYear(index)]]"
182
- selected$="[[_isSelectedYear(index, selectedDate)]]"
183
- >
184
- [[_yearAfterXYears(index)]]
185
- </div>
186
- <div part="year-separator" aria-hidden="true"></div>
187
- </template>
188
- </vaadin-infinite-scroller>
103
+ <div id="scrollers">
104
+ <slot name="months"></slot>
105
+ <slot name="years"></slot>
189
106
  </div>
190
107
 
191
108
  <div on-touchend="_preventDefault" role="toolbar" part="toolbar">
192
- <vaadin-button
193
- id="todayButton"
194
- part="today-button"
195
- theme="tertiary"
196
- disabled="[[!_isTodayAllowed(minDate, maxDate)]]"
197
- on-keydown="__onTodayButtonKeyDown"
198
- >
199
- [[i18n.today]]
200
- </vaadin-button>
201
- <vaadin-button id="cancelButton" part="cancel-button" theme="tertiary" on-keydown="__onCancelButtonKeyDown">
202
- [[i18n.cancel]]
203
- </vaadin-button>
109
+ <slot name="today-button"></slot>
110
+ <slot name="cancel-button"></slot>
204
111
  </div>
205
112
  `;
206
113
  }
@@ -249,7 +156,10 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
249
156
 
250
157
  _visibleMonthIndex: Number,
251
158
 
252
- _desktopMode: Boolean,
159
+ _desktopMode: {
160
+ type: Boolean,
161
+ observer: '_desktopModeChanged',
162
+ },
253
163
 
254
164
  _desktopMediaQuery: {
255
165
  type: String,
@@ -270,6 +180,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
270
180
 
271
181
  showWeekNumbers: {
272
182
  type: Boolean,
183
+ value: false,
273
184
  },
274
185
 
275
186
  _ignoreTaps: Boolean,
@@ -290,15 +201,50 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
290
201
  * Input label
291
202
  */
292
203
  label: String,
204
+
205
+ _cancelButton: {
206
+ type: Object,
207
+ },
208
+
209
+ _todayButton: {
210
+ type: Object,
211
+ },
212
+
213
+ calendars: {
214
+ type: Array,
215
+ value: () => [],
216
+ },
217
+
218
+ years: {
219
+ type: Array,
220
+ value: () => [],
221
+ },
293
222
  };
294
223
  }
295
224
 
225
+ static get observers() {
226
+ return [
227
+ '__updateCalendars(calendars, i18n, minDate, maxDate, selectedDate, focusedDate, showWeekNumbers, _ignoreTaps, _theme)',
228
+ '__updateCancelButton(_cancelButton, i18n)',
229
+ '__updateTodayButton(_todayButton, i18n, minDate, maxDate)',
230
+ '__updateYears(years, selectedDate, _theme)',
231
+ ];
232
+ }
233
+
296
234
  get __isRTL() {
297
235
  return this.getAttribute('dir') === 'rtl';
298
236
  }
299
237
 
300
- get calendars() {
301
- return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')];
238
+ /**
239
+ * Whether to scroll to a sub-month position when scrolling to a date.
240
+ * This is active if the month scroller is not large enough to fit a
241
+ * full month. In that case we want to scroll to a position between
242
+ * two months in order to have the focused date in the visible area.
243
+ * @returns {boolean} whether to use sub-month scrolling
244
+ * @private
245
+ */
246
+ get __useSubMonthScrolling() {
247
+ return this._monthScroller.clientHeight < this._monthScroller.itemHeight + this._monthScroller.bufferOffset;
302
248
  }
303
249
 
304
250
  get focusableDateElement() {
@@ -312,10 +258,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
312
258
 
313
259
  addListener(this.$.scrollers, 'track', this._track.bind(this));
314
260
  addListener(this.shadowRoot.querySelector('[part="clear-button"]'), 'tap', this._clear.bind(this));
315
- addListener(this.shadowRoot.querySelector('[part="today-button"]'), 'tap', this._onTodayTap.bind(this));
316
- addListener(this.shadowRoot.querySelector('[part="cancel-button"]'), 'tap', this._cancel.bind(this));
317
261
  addListener(this.shadowRoot.querySelector('[part="toggle-button"]'), 'tap', this._cancel.bind(this));
318
- addListener(this.shadowRoot.querySelector('[part="years"]'), 'tap', this._onYearTap.bind(this));
319
262
  addListener(
320
263
  this.shadowRoot.querySelector('[part="years-toggle-button"]'),
321
264
  'tap',
@@ -327,6 +270,37 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
327
270
  this._desktopMode = matches;
328
271
  }),
329
272
  );
273
+
274
+ this.addController(
275
+ new SlotController(
276
+ this,
277
+ 'today-button',
278
+ () => document.createElement('vaadin-button'),
279
+ (_, btn) => {
280
+ btn.setAttribute('theme', 'tertiary');
281
+ btn.addEventListener('keydown', (e) => this.__onTodayButtonKeyDown(e));
282
+ addListener(btn, 'tap', this._onTodayTap.bind(this));
283
+ this._todayButton = btn;
284
+ },
285
+ ),
286
+ );
287
+
288
+ this.addController(
289
+ new SlotController(
290
+ this,
291
+ 'cancel-button',
292
+ () => document.createElement('vaadin-button'),
293
+ (_, btn) => {
294
+ btn.setAttribute('theme', 'tertiary');
295
+ btn.addEventListener('keydown', (e) => this.__onCancelButtonKeyDown(e));
296
+ addListener(btn, 'tap', this._cancel.bind(this));
297
+ this._cancelButton = btn;
298
+ },
299
+ ),
300
+ );
301
+
302
+ this.__initMonthScroller();
303
+ this.__initYearScroller();
330
304
  }
331
305
 
332
306
  /**
@@ -347,14 +321,135 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
347
321
  * Focuses the cancel button
348
322
  */
349
323
  focusCancel() {
350
- this.$.cancelButton.focus();
324
+ this._cancelButton.focus();
351
325
  }
352
326
 
353
327
  /**
354
328
  * Scrolls the list to the given Date.
355
329
  */
356
330
  scrollToDate(date, animate) {
357
- this._scrollToPosition(this._differenceInMonths(date, this._originDate), animate);
331
+ const offset = this.__useSubMonthScrolling ? this._calculateWeekScrollOffset(date) : 0;
332
+ this._scrollToPosition(this._differenceInMonths(date, this._originDate) + offset, animate);
333
+ this._monthScroller.forceUpdate();
334
+ }
335
+
336
+ __initMonthScroller() {
337
+ this.addController(
338
+ new SlotController(
339
+ this,
340
+ 'months',
341
+ () => document.createElement('vaadin-date-picker-month-scroller'),
342
+ (_, scroller) => {
343
+ scroller.addEventListener('custom-scroll', () => {
344
+ this._onMonthScroll();
345
+ });
346
+
347
+ scroller.addEventListener('touchstart', () => {
348
+ this._onMonthScrollTouchStart();
349
+ });
350
+
351
+ scroller.addEventListener('keydown', (e) => {
352
+ this.__onMonthCalendarKeyDown(e);
353
+ });
354
+
355
+ scroller.addEventListener('init-done', () => {
356
+ const calendars = [...this.querySelectorAll('vaadin-month-calendar')];
357
+
358
+ // Two-way binding for selectedDate property
359
+ calendars.forEach((calendar) => {
360
+ calendar.addEventListener('selected-date-changed', (e) => {
361
+ this.selectedDate = e.detail.value;
362
+ });
363
+ });
364
+
365
+ this.calendars = calendars;
366
+ });
367
+
368
+ this._monthScroller = scroller;
369
+ },
370
+ ),
371
+ );
372
+ }
373
+
374
+ __initYearScroller() {
375
+ this.addController(
376
+ new SlotController(
377
+ this,
378
+ 'years',
379
+ () => document.createElement('vaadin-date-picker-year-scroller'),
380
+ (_, scroller) => {
381
+ scroller.setAttribute('aria-hidden', 'true');
382
+
383
+ addListener(scroller, 'tap', (e) => {
384
+ this._onYearTap(e);
385
+ });
386
+
387
+ scroller.addEventListener('custom-scroll', () => {
388
+ this._onYearScroll();
389
+ });
390
+
391
+ scroller.addEventListener('touchstart', () => {
392
+ this._onYearScrollTouchStart();
393
+ });
394
+
395
+ scroller.addEventListener('init-done', () => {
396
+ this.years = [...this.querySelectorAll('vaadin-date-picker-year')];
397
+ });
398
+
399
+ this._yearScroller = scroller;
400
+ },
401
+ ),
402
+ );
403
+ }
404
+
405
+ __updateCancelButton(cancelButton, i18n) {
406
+ if (cancelButton) {
407
+ cancelButton.textContent = i18n && i18n.cancel;
408
+ }
409
+ }
410
+
411
+ __updateTodayButton(todayButton, i18n, minDate, maxDate) {
412
+ if (todayButton) {
413
+ todayButton.textContent = i18n && i18n.today;
414
+ todayButton.disabled = !this._isTodayAllowed(minDate, maxDate);
415
+ }
416
+ }
417
+
418
+ // eslint-disable-next-line max-params
419
+ __updateCalendars(calendars, i18n, minDate, maxDate, selectedDate, focusedDate, showWeekNumbers, ignoreTaps, theme) {
420
+ if (calendars && calendars.length) {
421
+ calendars.forEach((calendar) => {
422
+ calendar.setProperties({
423
+ i18n,
424
+ minDate,
425
+ maxDate,
426
+ focusedDate,
427
+ selectedDate,
428
+ showWeekNumbers,
429
+ ignoreTaps,
430
+ });
431
+
432
+ if (theme) {
433
+ calendar.setAttribute('theme', theme);
434
+ } else {
435
+ calendar.removeAttribute('theme');
436
+ }
437
+ });
438
+ }
439
+ }
440
+
441
+ __updateYears(years, selectedDate, theme) {
442
+ if (years && years.length) {
443
+ years.forEach((year) => {
444
+ year.selectedDate = selectedDate;
445
+
446
+ if (theme) {
447
+ year.setAttribute('theme', theme);
448
+ } else {
449
+ year.removeAttribute('theme');
450
+ }
451
+ });
452
+ }
358
453
  }
359
454
 
360
455
  /**
@@ -368,55 +463,95 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
368
463
  );
369
464
  }
370
465
 
466
+ _desktopModeChanged(desktopMode) {
467
+ this.toggleAttribute('desktop', desktopMode);
468
+ }
469
+
371
470
  _focusedDateChanged(focusedDate) {
372
471
  this.revealDate(focusedDate);
373
472
  }
374
473
 
375
- _isCurrentYear(yearsFromNow) {
376
- return yearsFromNow === 0;
377
- }
474
+ /**
475
+ * Scrolls the month and year scrollers enough to reveal the given date.
476
+ */
477
+ revealDate(date, animate = true) {
478
+ if (!date) {
479
+ return;
480
+ }
481
+ const diff = this._differenceInMonths(date, this._originDate);
482
+ // If scroll area does not fit the full month, then always scroll with an offset to
483
+ // approximately display the week of the date
484
+ if (this.__useSubMonthScrolling) {
485
+ const offset = this._calculateWeekScrollOffset(date);
486
+ this._scrollToPosition(diff + offset, animate);
487
+ return;
488
+ }
489
+
490
+ // Otherwise determine if we need to scroll to make the month of the date visible
491
+ const scrolledAboveViewport = this._monthScroller.position > diff;
378
492
 
379
- _isSelectedYear(yearsFromNow, selectedDate) {
380
- if (selectedDate) {
381
- return selectedDate.getFullYear() === this._originDate.getFullYear() + yearsFromNow;
493
+ const visibleArea = Math.max(
494
+ this._monthScroller.itemHeight,
495
+ this._monthScroller.clientHeight - this._monthScroller.bufferOffset * 2,
496
+ );
497
+ const visibleItems = visibleArea / this._monthScroller.itemHeight;
498
+ const scrolledBelowViewport = this._monthScroller.position + visibleItems - 1 < diff;
499
+
500
+ if (scrolledAboveViewport) {
501
+ this._scrollToPosition(diff, animate);
502
+ } else if (scrolledBelowViewport) {
503
+ this._scrollToPosition(diff - visibleItems + 1, animate);
382
504
  }
383
505
  }
384
506
 
385
507
  /**
386
- * Scrolls the month and year scrollers enough to reveal the given date.
508
+ * Calculates an offset to be added to the month scroll position
509
+ * when using sub-month scrolling, in order ensure that the week
510
+ * that the date is in is visible even for small scroll areas.
511
+ * As the month scroller uses a month as minimal scroll unit
512
+ * (a value of `1` equals one month), we can not exactly identify
513
+ * the position of a specific week. This is a best effort
514
+ * implementation based on manual testing.
515
+ * @param date the date for which to calculate the offset
516
+ * @returns {number} the offset
517
+ * @private
387
518
  */
388
- revealDate(date, animate = true) {
389
- if (date) {
390
- const diff = this._differenceInMonths(date, this._originDate);
391
- const scrolledAboveViewport = this.$.monthScroller.position > diff;
392
-
393
- const visibleArea = Math.max(
394
- this.$.monthScroller.itemHeight,
395
- this.$.monthScroller.clientHeight - this.$.monthScroller.bufferOffset * 2,
396
- );
397
- const visibleItems = visibleArea / this.$.monthScroller.itemHeight;
398
- const scrolledBelowViewport = this.$.monthScroller.position + visibleItems - 1 < diff;
399
-
400
- if (scrolledAboveViewport) {
401
- this._scrollToPosition(diff, animate);
402
- } else if (scrolledBelowViewport) {
403
- this._scrollToPosition(diff - visibleItems + 1, animate);
519
+ _calculateWeekScrollOffset(date) {
520
+ // Get first day of month
521
+ const temp = new Date(0, 0);
522
+ temp.setFullYear(date.getFullYear());
523
+ temp.setMonth(date.getMonth());
524
+ temp.setDate(1);
525
+ // Determine week (=row index) of date within the month
526
+ let week = 0;
527
+ while (temp.getDate() < date.getDate()) {
528
+ temp.setDate(temp.getDate() + 1);
529
+ if (temp.getDay() === this.i18n.firstDayOfWeek) {
530
+ week += 1;
404
531
  }
405
532
  }
533
+ // Calculate magic number that approximately keeps the week visible
534
+ return week / 6;
406
535
  }
407
536
 
408
537
  _initialPositionChanged(initialPosition) {
538
+ if (this._monthScroller && this._yearScroller) {
539
+ this._monthScroller.active = true;
540
+ this._yearScroller.active = true;
541
+ }
542
+
409
543
  this.scrollToDate(initialPosition);
410
544
  }
411
545
 
412
546
  _repositionYearScroller() {
413
- this._visibleMonthIndex = Math.floor(this.$.monthScroller.position);
414
- this.$.yearScroller.position = (this.$.monthScroller.position + this._originDate.getMonth()) / 12;
547
+ const monthPosition = this._monthScroller.position;
548
+ this._visibleMonthIndex = Math.floor(monthPosition);
549
+ this._yearScroller.position = (monthPosition + this._originDate.getMonth()) / 12;
415
550
  }
416
551
 
417
552
  _repositionMonthScroller() {
418
- this.$.monthScroller.position = this.$.yearScroller.position * 12 - this._originDate.getMonth();
419
- this._visibleMonthIndex = Math.floor(this.$.monthScroller.position);
553
+ this._monthScroller.position = this._yearScroller.position * 12 - this._originDate.getMonth();
554
+ this._visibleMonthIndex = Math.floor(this._monthScroller.position);
420
555
  }
421
556
 
422
557
  _onMonthScroll() {
@@ -459,7 +594,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
459
594
  _onTodayTap() {
460
595
  const today = new Date();
461
596
 
462
- if (Math.abs(this.$.monthScroller.position - this._differenceInMonths(today, this._originDate)) < 0.001) {
597
+ if (Math.abs(this._monthScroller.position - this._differenceInMonths(today, this._originDate)) < 0.001) {
463
598
  // Select today only if the month scroller is positioned approximately
464
599
  // at the beginning of the current month
465
600
  this._selectDate(today);
@@ -480,9 +615,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
480
615
  _onYearTap(e) {
481
616
  if (!this._ignoreTaps && !this._notTapping) {
482
617
  const scrollDelta =
483
- e.detail.y - (this.$.yearScroller.getBoundingClientRect().top + this.$.yearScroller.clientHeight / 2);
484
- const yearDelta = scrollDelta / this.$.yearScroller.itemHeight;
485
- this._scrollToPosition(this.$.monthScroller.position + yearDelta * 12, true);
618
+ e.detail.y - (this._yearScroller.getBoundingClientRect().top + this._yearScroller.clientHeight / 2);
619
+ const yearDelta = scrollDelta / this._yearScroller.itemHeight;
620
+ this._scrollToPosition(this._monthScroller.position + yearDelta * 12, true);
486
621
  }
487
622
  }
488
623
 
@@ -493,7 +628,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
493
628
  }
494
629
 
495
630
  if (!animate) {
496
- this.$.monthScroller.position = targetPosition;
631
+ this._monthScroller.position = targetPosition;
497
632
  this._targetPosition = undefined;
498
633
  this._repositionYearScroller();
499
634
  this.__tryFocusDate();
@@ -518,7 +653,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
518
653
  };
519
654
 
520
655
  let start = 0;
521
- const initialPosition = this.$.monthScroller.position;
656
+ const initialPosition = this._monthScroller.position;
522
657
 
523
658
  const smoothScroll = (timestamp) => {
524
659
  start = start || timestamp;
@@ -531,7 +666,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
531
666
  this._targetPosition - initialPosition,
532
667
  this.scrollDuration,
533
668
  );
534
- this.$.monthScroller.position = currentPos;
669
+ this._monthScroller.position = currentPos;
535
670
  window.requestAnimationFrame(smoothScroll);
536
671
  } else {
537
672
  this.dispatchEvent(
@@ -545,7 +680,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
545
680
  }),
546
681
  );
547
682
 
548
- this.$.monthScroller.position = this._targetPosition;
683
+ this._monthScroller.position = this._targetPosition;
549
684
  this._targetPosition = undefined;
550
685
 
551
686
  revealResolve();
@@ -640,26 +775,13 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
640
775
 
641
776
  _translateXChanged(x) {
642
777
  if (!this._desktopMode) {
643
- this.$.monthScroller.style.transform = `translateX(${x - this._yearScrollerWidth}px)`;
644
- this.$.yearScroller.style.transform = `translateX(${x}px)`;
778
+ this._monthScroller.style.transform = `translateX(${x - this._yearScrollerWidth}px)`;
779
+ this._yearScroller.style.transform = `translateX(${x}px)`;
645
780
  }
646
781
  }
647
782
 
648
- _yearAfterXYears(index) {
649
- const result = new Date(this._originDate);
650
- result.setFullYear(parseInt(index) + this._originDate.getFullYear());
651
- return result.getFullYear();
652
- }
653
-
654
783
  _yearAfterXMonths(months) {
655
- return this._dateAfterXMonths(months).getFullYear();
656
- }
657
-
658
- _dateAfterXMonths(months) {
659
- const result = new Date(this._originDate);
660
- result.setDate(1);
661
- result.setMonth(parseInt(months) + this._originDate.getMonth());
662
- return result;
784
+ return dateAfterXMonths(months).getFullYear();
663
785
  }
664
786
 
665
787
  _differenceInMonths(date1, date2) {
@@ -667,10 +789,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
667
789
  return months - date2.getMonth() + date1.getMonth();
668
790
  }
669
791
 
670
- _differenceInYears(date1, date2) {
671
- return this._differenceInMonths(date1, date2) / 12;
672
- }
673
-
674
792
  _clear() {
675
793
  this._selectDate('');
676
794
  }
@@ -767,7 +885,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
767
885
 
768
886
  if (this.hasAttribute('fullscreen')) {
769
887
  // Trap focus in the overlay
770
- this.$.cancelButton.focus();
888
+ this.focusCancel();
771
889
  } else {
772
890
  this.__focusInput();
773
891
  }
@@ -840,7 +958,11 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
840
958
  // Wait for `vaadin-month-calendar` elements to be rendered
841
959
  if (!this.calendars.length) {
842
960
  await new Promise((resolve) => {
843
- setTimeout(resolve);
961
+ afterNextRender(this, () => {
962
+ // Force dom-repeat elements to render
963
+ flush();
964
+ resolve();
965
+ });
844
966
  });
845
967
  }
846
968