@vaadin/date-picker 23.3.0-alpha3 → 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.
- package/package.json +10 -10
- package/src/vaadin-date-picker-helper.d.ts +7 -0
- package/src/vaadin-date-picker-helper.js +14 -0
- package/src/vaadin-date-picker-light.d.ts +0 -6
- package/src/vaadin-date-picker-light.js +4 -42
- package/src/vaadin-date-picker-mixin.d.ts +0 -9
- package/src/vaadin-date-picker-mixin.js +73 -52
- package/src/vaadin-date-picker-month-scroller.js +74 -0
- package/src/vaadin-date-picker-overlay-content.js +297 -175
- package/src/vaadin-date-picker-overlay.js +21 -10
- package/src/vaadin-date-picker-year-scroller.js +104 -0
- package/src/vaadin-date-picker-year.js +57 -0
- package/src/vaadin-date-picker.d.ts +19 -9
- package/src/vaadin-date-picker.js +38 -36
- package/src/vaadin-infinite-scroller.js +69 -41
- package/src/vaadin-month-calendar.js +0 -6
- package/theme/lumo/vaadin-date-picker-overlay-content-styles.js +15 -52
- package/theme/lumo/vaadin-date-picker-overlay-styles.js +1 -1
- package/theme/lumo/vaadin-date-picker-year-styles.js +32 -0
- package/theme/material/vaadin-date-picker-overlay-content-styles.js +11 -36
- package/theme/material/vaadin-date-picker-overlay-styles.js +1 -1
- package/theme/material/vaadin-date-picker-year-styles.js +28 -0
- package/web-types.json +5 -5
- package/web-types.lit.json +5 -5
- package/src/vaadin-date-picker-styles.js +0 -33
|
@@ -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-
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
105
|
-
|
|
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"
|
|
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"
|
|
144
|
-
<
|
|
145
|
-
|
|
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
|
-
<
|
|
193
|
-
|
|
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:
|
|
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
|
-
|
|
301
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
376
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
-
|
|
414
|
-
this
|
|
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
|
|
419
|
-
this._visibleMonthIndex = Math.floor(this
|
|
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
|
|
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
|
|
484
|
-
const yearDelta = scrollDelta / this
|
|
485
|
-
this._scrollToPosition(this
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
644
|
-
this
|
|
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
|
|
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
|
|
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
|
-
|
|
961
|
+
afterNextRender(this, () => {
|
|
962
|
+
// Force dom-repeat elements to render
|
|
963
|
+
flush();
|
|
964
|
+
resolve();
|
|
965
|
+
});
|
|
844
966
|
});
|
|
845
967
|
}
|
|
846
968
|
|