@vaadin/date-picker 24.2.0-alpha5 → 24.2.0-alpha7
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 +15 -11
- package/src/vaadin-date-picker-mixin.js +50 -25
- package/src/vaadin-date-picker-month-scroller.js +6 -19
- package/src/vaadin-date-picker-overlay-content-mixin.js +1032 -0
- package/src/vaadin-date-picker-overlay-content-styles.js +68 -0
- package/src/vaadin-date-picker-overlay-content.js +14 -1012
- package/src/vaadin-date-picker-overlay-styles.js +23 -0
- package/src/vaadin-date-picker-overlay.js +7 -19
- package/src/vaadin-date-picker-styles.js +21 -0
- package/src/vaadin-date-picker-year-scroller.js +6 -20
- package/src/vaadin-date-picker.js +2 -16
- package/src/vaadin-infinite-scroller.js +118 -117
- package/src/vaadin-month-calendar-mixin.js +303 -0
- package/src/vaadin-month-calendar-styles.js +64 -0
- package/src/vaadin-month-calendar.js +27 -319
- package/web-types.json +2 -2
- package/web-types.lit.json +2 -2
|
@@ -8,90 +8,28 @@ import './vaadin-month-calendar.js';
|
|
|
8
8
|
import './vaadin-date-picker-month-scroller.js';
|
|
9
9
|
import './vaadin-date-picker-year-scroller.js';
|
|
10
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';
|
|
13
11
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
14
|
-
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
15
12
|
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
16
|
-
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
17
13
|
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
15
|
+
import { DatePickerOverlayContentMixin } from './vaadin-date-picker-overlay-content-mixin.js';
|
|
16
|
+
import { overlayContentStyles } from './vaadin-date-picker-overlay-content-styles.js';
|
|
17
|
+
|
|
18
|
+
registerStyles('vaadin-date-picker-overlay-content', overlayContentStyles, {
|
|
19
|
+
moduleId: 'vaadin-date-picker-overlay-content-styles',
|
|
20
|
+
});
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
23
|
* @extends HTMLElement
|
|
26
24
|
* @private
|
|
27
25
|
*/
|
|
28
|
-
class DatePickerOverlayContent extends
|
|
26
|
+
class DatePickerOverlayContent extends DatePickerOverlayContentMixin(
|
|
27
|
+
ControllerMixin(ThemableMixin(DirMixin(PolymerElement))),
|
|
28
|
+
) {
|
|
29
29
|
static get template() {
|
|
30
30
|
return html`
|
|
31
|
-
<style>
|
|
32
|
-
:host {
|
|
33
|
-
display: flex;
|
|
34
|
-
flex-direction: column;
|
|
35
|
-
height: 100%;
|
|
36
|
-
width: 100%;
|
|
37
|
-
outline: none;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
[part='overlay-header'] {
|
|
41
|
-
display: flex;
|
|
42
|
-
flex-shrink: 0;
|
|
43
|
-
flex-wrap: nowrap;
|
|
44
|
-
align-items: center;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
:host(:not([fullscreen])) [part='overlay-header'] {
|
|
48
|
-
display: none;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
[part='label'] {
|
|
52
|
-
flex-grow: 1;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
[hidden] {
|
|
56
|
-
display: none !important;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
[part='years-toggle-button'] {
|
|
60
|
-
display: flex;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
#scrollers {
|
|
64
|
-
display: flex;
|
|
65
|
-
height: 100%;
|
|
66
|
-
width: 100%;
|
|
67
|
-
position: relative;
|
|
68
|
-
overflow: hidden;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
:host([desktop]) ::slotted([slot='months']) {
|
|
72
|
-
right: 50px;
|
|
73
|
-
transform: none !important;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
:host([desktop]) ::slotted([slot='years']) {
|
|
77
|
-
transform: none !important;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
:host(.animate) ::slotted([slot='months']),
|
|
81
|
-
:host(.animate) ::slotted([slot='years']) {
|
|
82
|
-
transition: all 200ms;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
[part='toolbar'] {
|
|
86
|
-
display: flex;
|
|
87
|
-
justify-content: space-between;
|
|
88
|
-
z-index: 2;
|
|
89
|
-
flex-shrink: 0;
|
|
90
|
-
}
|
|
91
|
-
</style>
|
|
92
|
-
|
|
93
31
|
<div part="overlay-header" on-touchend="_preventDefault" aria-hidden="true">
|
|
94
|
-
<div part="label">[[_formatDisplayed(selectedDate, i18n
|
|
32
|
+
<div part="label">[[_formatDisplayed(selectedDate, i18n, label)]]</div>
|
|
95
33
|
<div part="clear-button" hidden$="[[!selectedDate]]"></div>
|
|
96
34
|
<div part="toggle-button"></div>
|
|
97
35
|
|
|
@@ -116,950 +54,14 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
|
|
|
116
54
|
return 'vaadin-date-picker-overlay-content';
|
|
117
55
|
}
|
|
118
56
|
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
scrollDuration: {
|
|
122
|
-
type: Number,
|
|
123
|
-
value: 300,
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* The value for this element.
|
|
128
|
-
*/
|
|
129
|
-
selectedDate: {
|
|
130
|
-
type: Date,
|
|
131
|
-
value: null,
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Date value which is focused using keyboard.
|
|
136
|
-
*/
|
|
137
|
-
focusedDate: {
|
|
138
|
-
type: Date,
|
|
139
|
-
notify: true,
|
|
140
|
-
observer: '_focusedDateChanged',
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
_focusedMonthDate: Number,
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Date which should be visible when there is no value selected.
|
|
147
|
-
*/
|
|
148
|
-
initialPosition: {
|
|
149
|
-
type: Date,
|
|
150
|
-
observer: '_initialPositionChanged',
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
_originDate: {
|
|
154
|
-
value: new Date(),
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
_visibleMonthIndex: Number,
|
|
158
|
-
|
|
159
|
-
_desktopMode: {
|
|
160
|
-
type: Boolean,
|
|
161
|
-
observer: '_desktopModeChanged',
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
_desktopMediaQuery: {
|
|
165
|
-
type: String,
|
|
166
|
-
value: '(min-width: 375px)',
|
|
167
|
-
},
|
|
168
|
-
|
|
169
|
-
_translateX: {
|
|
170
|
-
observer: '_translateXChanged',
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
_yearScrollerWidth: {
|
|
174
|
-
value: 50,
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
i18n: {
|
|
178
|
-
type: Object,
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
showWeekNumbers: {
|
|
182
|
-
type: Boolean,
|
|
183
|
-
value: false,
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
_ignoreTaps: Boolean,
|
|
187
|
-
|
|
188
|
-
_notTapping: Boolean,
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* The earliest date that can be selected. All earlier dates will be disabled.
|
|
192
|
-
*/
|
|
193
|
-
minDate: Date,
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* The latest date that can be selected. All later dates will be disabled.
|
|
197
|
-
*/
|
|
198
|
-
maxDate: Date,
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Input label
|
|
202
|
-
*/
|
|
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
|
-
},
|
|
222
|
-
};
|
|
223
|
-
}
|
|
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
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Whether to scroll to a sub-month position when scrolling to a date.
|
|
236
|
-
* This is active if the month scroller is not large enough to fit a
|
|
237
|
-
* full month. In that case we want to scroll to a position between
|
|
238
|
-
* two months in order to have the focused date in the visible area.
|
|
239
|
-
* @returns {boolean} whether to use sub-month scrolling
|
|
240
|
-
* @private
|
|
241
|
-
*/
|
|
242
|
-
get __useSubMonthScrolling() {
|
|
243
|
-
return this._monthScroller.clientHeight < this._monthScroller.itemHeight + this._monthScroller.bufferOffset;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
get focusableDateElement() {
|
|
247
|
-
return this.calendars.map((calendar) => calendar.focusableDateElement).find(Boolean);
|
|
248
|
-
}
|
|
249
|
-
|
|
57
|
+
/** @protected */
|
|
250
58
|
ready() {
|
|
251
59
|
super.ready();
|
|
252
60
|
|
|
253
61
|
this.setAttribute('role', 'dialog');
|
|
254
62
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
addListener(this.shadowRoot.querySelector('[part="toggle-button"]'), 'tap', this._cancel.bind(this));
|
|
258
|
-
addListener(
|
|
259
|
-
this.shadowRoot.querySelector('[part="years-toggle-button"]'),
|
|
260
|
-
'tap',
|
|
261
|
-
this._toggleYearScroller.bind(this),
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
this.addController(
|
|
265
|
-
new MediaQueryController(this._desktopMediaQuery, (matches) => {
|
|
266
|
-
this._desktopMode = matches;
|
|
267
|
-
}),
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
this.addController(
|
|
271
|
-
new SlotController(this, 'today-button', 'vaadin-button', {
|
|
272
|
-
observe: false,
|
|
273
|
-
initializer: (btn) => {
|
|
274
|
-
btn.setAttribute('theme', 'tertiary');
|
|
275
|
-
btn.addEventListener('keydown', (e) => this.__onTodayButtonKeyDown(e));
|
|
276
|
-
addListener(btn, 'tap', this._onTodayTap.bind(this));
|
|
277
|
-
this._todayButton = btn;
|
|
278
|
-
},
|
|
279
|
-
}),
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
this.addController(
|
|
283
|
-
new SlotController(this, 'cancel-button', 'vaadin-button', {
|
|
284
|
-
observe: false,
|
|
285
|
-
initializer: (btn) => {
|
|
286
|
-
btn.setAttribute('theme', 'tertiary');
|
|
287
|
-
btn.addEventListener('keydown', (e) => this.__onCancelButtonKeyDown(e));
|
|
288
|
-
addListener(btn, 'tap', this._cancel.bind(this));
|
|
289
|
-
this._cancelButton = btn;
|
|
290
|
-
},
|
|
291
|
-
}),
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
this.__initMonthScroller();
|
|
295
|
-
this.__initYearScroller();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Fired when the scroller reaches the target scrolling position.
|
|
300
|
-
* @event scroll-animation-finished
|
|
301
|
-
* @param {Number} detail.position new position
|
|
302
|
-
* @param {Number} detail.oldPosition old position
|
|
303
|
-
*/
|
|
304
|
-
|
|
305
|
-
connectedCallback() {
|
|
306
|
-
super.connectedCallback();
|
|
307
|
-
this._closeYearScroller();
|
|
308
|
-
this._toggleAnimateClass(true);
|
|
309
|
-
setTouchAction(this.$.scrollers, 'pan-y');
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Focuses the cancel button
|
|
314
|
-
*/
|
|
315
|
-
focusCancel() {
|
|
316
|
-
this._cancelButton.focus();
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Scrolls the list to the given Date.
|
|
321
|
-
*/
|
|
322
|
-
scrollToDate(date, animate) {
|
|
323
|
-
const offset = this.__useSubMonthScrolling ? this._calculateWeekScrollOffset(date) : 0;
|
|
324
|
-
this._scrollToPosition(this._differenceInMonths(date, this._originDate) + offset, animate);
|
|
325
|
-
this._monthScroller.forceUpdate();
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
__initMonthScroller() {
|
|
329
|
-
this.addController(
|
|
330
|
-
new SlotController(this, 'months', 'vaadin-date-picker-month-scroller', {
|
|
331
|
-
observe: false,
|
|
332
|
-
initializer: (scroller) => {
|
|
333
|
-
scroller.addEventListener('custom-scroll', () => {
|
|
334
|
-
this._onMonthScroll();
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
scroller.addEventListener('touchstart', () => {
|
|
338
|
-
this._onMonthScrollTouchStart();
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
scroller.addEventListener('keydown', (e) => {
|
|
342
|
-
this.__onMonthCalendarKeyDown(e);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
scroller.addEventListener('init-done', () => {
|
|
346
|
-
const calendars = [...this.querySelectorAll('vaadin-month-calendar')];
|
|
347
|
-
|
|
348
|
-
// Two-way binding for selectedDate property
|
|
349
|
-
calendars.forEach((calendar) => {
|
|
350
|
-
calendar.addEventListener('selected-date-changed', (e) => {
|
|
351
|
-
this.selectedDate = e.detail.value;
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
this.calendars = calendars;
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
this._monthScroller = scroller;
|
|
359
|
-
},
|
|
360
|
-
}),
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
__initYearScroller() {
|
|
365
|
-
this.addController(
|
|
366
|
-
new SlotController(this, 'years', 'vaadin-date-picker-year-scroller', {
|
|
367
|
-
observe: false,
|
|
368
|
-
initializer: (scroller) => {
|
|
369
|
-
scroller.setAttribute('aria-hidden', 'true');
|
|
370
|
-
|
|
371
|
-
addListener(scroller, 'tap', (e) => {
|
|
372
|
-
this._onYearTap(e);
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
scroller.addEventListener('custom-scroll', () => {
|
|
376
|
-
this._onYearScroll();
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
scroller.addEventListener('touchstart', () => {
|
|
380
|
-
this._onYearScrollTouchStart();
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
scroller.addEventListener('init-done', () => {
|
|
384
|
-
this.years = [...this.querySelectorAll('vaadin-date-picker-year')];
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
this._yearScroller = scroller;
|
|
388
|
-
},
|
|
389
|
-
}),
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
__updateCancelButton(cancelButton, i18n) {
|
|
394
|
-
if (cancelButton) {
|
|
395
|
-
cancelButton.textContent = i18n && i18n.cancel;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
__updateTodayButton(todayButton, i18n, minDate, maxDate) {
|
|
400
|
-
if (todayButton) {
|
|
401
|
-
todayButton.textContent = i18n && i18n.today;
|
|
402
|
-
todayButton.disabled = !this._isTodayAllowed(minDate, maxDate);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// eslint-disable-next-line max-params
|
|
407
|
-
__updateCalendars(calendars, i18n, minDate, maxDate, selectedDate, focusedDate, showWeekNumbers, ignoreTaps, theme) {
|
|
408
|
-
if (calendars && calendars.length) {
|
|
409
|
-
calendars.forEach((calendar) => {
|
|
410
|
-
calendar.setProperties({
|
|
411
|
-
i18n,
|
|
412
|
-
minDate,
|
|
413
|
-
maxDate,
|
|
414
|
-
focusedDate,
|
|
415
|
-
selectedDate,
|
|
416
|
-
showWeekNumbers,
|
|
417
|
-
ignoreTaps,
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
if (theme) {
|
|
421
|
-
calendar.setAttribute('theme', theme);
|
|
422
|
-
} else {
|
|
423
|
-
calendar.removeAttribute('theme');
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
__updateYears(years, selectedDate, theme) {
|
|
430
|
-
if (years && years.length) {
|
|
431
|
-
years.forEach((year) => {
|
|
432
|
-
year.selectedDate = selectedDate;
|
|
433
|
-
|
|
434
|
-
if (theme) {
|
|
435
|
-
year.setAttribute('theme', theme);
|
|
436
|
-
} else {
|
|
437
|
-
year.removeAttribute('theme');
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Select a date and fire event indicating user interaction.
|
|
445
|
-
* @protected
|
|
446
|
-
*/
|
|
447
|
-
_selectDate(dateToSelect) {
|
|
448
|
-
this.selectedDate = dateToSelect;
|
|
449
|
-
this.dispatchEvent(
|
|
450
|
-
new CustomEvent('date-selected', { detail: { date: dateToSelect }, bubbles: true, composed: true }),
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
_desktopModeChanged(desktopMode) {
|
|
455
|
-
this.toggleAttribute('desktop', desktopMode);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
_focusedDateChanged(focusedDate) {
|
|
459
|
-
this.revealDate(focusedDate);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Scrolls the month and year scrollers enough to reveal the given date.
|
|
464
|
-
*/
|
|
465
|
-
revealDate(date, animate = true) {
|
|
466
|
-
if (!date) {
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
const diff = this._differenceInMonths(date, this._originDate);
|
|
470
|
-
// If scroll area does not fit the full month, then always scroll with an offset to
|
|
471
|
-
// approximately display the week of the date
|
|
472
|
-
if (this.__useSubMonthScrolling) {
|
|
473
|
-
const offset = this._calculateWeekScrollOffset(date);
|
|
474
|
-
this._scrollToPosition(diff + offset, animate);
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// Otherwise determine if we need to scroll to make the month of the date visible
|
|
479
|
-
const scrolledAboveViewport = this._monthScroller.position > diff;
|
|
480
|
-
|
|
481
|
-
const visibleArea = Math.max(
|
|
482
|
-
this._monthScroller.itemHeight,
|
|
483
|
-
this._monthScroller.clientHeight - this._monthScroller.bufferOffset * 2,
|
|
484
|
-
);
|
|
485
|
-
const visibleItems = visibleArea / this._monthScroller.itemHeight;
|
|
486
|
-
const scrolledBelowViewport = this._monthScroller.position + visibleItems - 1 < diff;
|
|
487
|
-
|
|
488
|
-
if (scrolledAboveViewport) {
|
|
489
|
-
this._scrollToPosition(diff, animate);
|
|
490
|
-
} else if (scrolledBelowViewport) {
|
|
491
|
-
this._scrollToPosition(diff - visibleItems + 1, animate);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Calculates an offset to be added to the month scroll position
|
|
497
|
-
* when using sub-month scrolling, in order ensure that the week
|
|
498
|
-
* that the date is in is visible even for small scroll areas.
|
|
499
|
-
* As the month scroller uses a month as minimal scroll unit
|
|
500
|
-
* (a value of `1` equals one month), we can not exactly identify
|
|
501
|
-
* the position of a specific week. This is a best effort
|
|
502
|
-
* implementation based on manual testing.
|
|
503
|
-
* @param date the date for which to calculate the offset
|
|
504
|
-
* @returns {number} the offset
|
|
505
|
-
* @private
|
|
506
|
-
*/
|
|
507
|
-
_calculateWeekScrollOffset(date) {
|
|
508
|
-
// Get first day of month
|
|
509
|
-
const temp = new Date(0, 0);
|
|
510
|
-
temp.setFullYear(date.getFullYear());
|
|
511
|
-
temp.setMonth(date.getMonth());
|
|
512
|
-
temp.setDate(1);
|
|
513
|
-
// Determine week (=row index) of date within the month
|
|
514
|
-
let week = 0;
|
|
515
|
-
while (temp.getDate() < date.getDate()) {
|
|
516
|
-
temp.setDate(temp.getDate() + 1);
|
|
517
|
-
if (temp.getDay() === this.i18n.firstDayOfWeek) {
|
|
518
|
-
week += 1;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
// Calculate magic number that approximately keeps the week visible
|
|
522
|
-
return week / 6;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
_initialPositionChanged(initialPosition) {
|
|
526
|
-
if (this._monthScroller && this._yearScroller) {
|
|
527
|
-
this._monthScroller.active = true;
|
|
528
|
-
this._yearScroller.active = true;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
this.scrollToDate(initialPosition);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
_repositionYearScroller() {
|
|
535
|
-
const monthPosition = this._monthScroller.position;
|
|
536
|
-
this._visibleMonthIndex = Math.floor(monthPosition);
|
|
537
|
-
this._yearScroller.position = (monthPosition + this._originDate.getMonth()) / 12;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
_repositionMonthScroller() {
|
|
541
|
-
this._monthScroller.position = this._yearScroller.position * 12 - this._originDate.getMonth();
|
|
542
|
-
this._visibleMonthIndex = Math.floor(this._monthScroller.position);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
_onMonthScroll() {
|
|
546
|
-
this._repositionYearScroller();
|
|
547
|
-
this._doIgnoreTaps();
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
_onYearScroll() {
|
|
551
|
-
this._repositionMonthScroller();
|
|
552
|
-
this._doIgnoreTaps();
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
_onYearScrollTouchStart() {
|
|
556
|
-
this._notTapping = false;
|
|
557
|
-
setTimeout(() => {
|
|
558
|
-
this._notTapping = true;
|
|
559
|
-
}, 300);
|
|
560
|
-
|
|
561
|
-
this._repositionMonthScroller();
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
_onMonthScrollTouchStart() {
|
|
565
|
-
this._repositionYearScroller();
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
_doIgnoreTaps() {
|
|
569
|
-
this._ignoreTaps = true;
|
|
570
|
-
this._debouncer = Debouncer.debounce(this._debouncer, timeOut.after(300), () => {
|
|
571
|
-
this._ignoreTaps = false;
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
_formatDisplayed(date, formatDate, label) {
|
|
576
|
-
if (date) {
|
|
577
|
-
return formatDate(extractDateParts(date));
|
|
578
|
-
}
|
|
579
|
-
return label;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
_onTodayTap() {
|
|
583
|
-
const today = new Date();
|
|
584
|
-
|
|
585
|
-
if (Math.abs(this._monthScroller.position - this._differenceInMonths(today, this._originDate)) < 0.001) {
|
|
586
|
-
// Select today only if the month scroller is positioned approximately
|
|
587
|
-
// at the beginning of the current month
|
|
588
|
-
this._selectDate(today);
|
|
589
|
-
this._close();
|
|
590
|
-
} else {
|
|
591
|
-
this._scrollToCurrentMonth();
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
_scrollToCurrentMonth() {
|
|
596
|
-
if (this.focusedDate) {
|
|
597
|
-
this.focusedDate = new Date();
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
this.scrollToDate(new Date(), true);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
_onYearTap(e) {
|
|
604
|
-
if (!this._ignoreTaps && !this._notTapping) {
|
|
605
|
-
const scrollDelta =
|
|
606
|
-
e.detail.y - (this._yearScroller.getBoundingClientRect().top + this._yearScroller.clientHeight / 2);
|
|
607
|
-
const yearDelta = scrollDelta / this._yearScroller.itemHeight;
|
|
608
|
-
this._scrollToPosition(this._monthScroller.position + yearDelta * 12, true);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
_scrollToPosition(targetPosition, animate) {
|
|
613
|
-
if (this._targetPosition !== undefined) {
|
|
614
|
-
this._targetPosition = targetPosition;
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
if (!animate) {
|
|
619
|
-
this._monthScroller.position = targetPosition;
|
|
620
|
-
this._targetPosition = undefined;
|
|
621
|
-
this._repositionYearScroller();
|
|
622
|
-
this.__tryFocusDate();
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
this._targetPosition = targetPosition;
|
|
627
|
-
|
|
628
|
-
let revealResolve;
|
|
629
|
-
this._revealPromise = new Promise((resolve) => {
|
|
630
|
-
revealResolve = resolve;
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
// http://gizma.com/easing/
|
|
634
|
-
const easingFunction = (t, b, c, d) => {
|
|
635
|
-
t /= d / 2;
|
|
636
|
-
if (t < 1) {
|
|
637
|
-
return (c / 2) * t * t + b;
|
|
638
|
-
}
|
|
639
|
-
t -= 1;
|
|
640
|
-
return (-c / 2) * (t * (t - 2) - 1) + b;
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
let start = 0;
|
|
644
|
-
const initialPosition = this._monthScroller.position;
|
|
645
|
-
|
|
646
|
-
const smoothScroll = (timestamp) => {
|
|
647
|
-
if (!start) {
|
|
648
|
-
start = timestamp;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
const currentTime = timestamp - start;
|
|
652
|
-
|
|
653
|
-
if (currentTime < this.scrollDuration) {
|
|
654
|
-
const currentPos = easingFunction(
|
|
655
|
-
currentTime,
|
|
656
|
-
initialPosition,
|
|
657
|
-
this._targetPosition - initialPosition,
|
|
658
|
-
this.scrollDuration,
|
|
659
|
-
);
|
|
660
|
-
this._monthScroller.position = currentPos;
|
|
661
|
-
window.requestAnimationFrame(smoothScroll);
|
|
662
|
-
} else {
|
|
663
|
-
this.dispatchEvent(
|
|
664
|
-
new CustomEvent('scroll-animation-finished', {
|
|
665
|
-
bubbles: true,
|
|
666
|
-
composed: true,
|
|
667
|
-
detail: {
|
|
668
|
-
position: this._targetPosition,
|
|
669
|
-
oldPosition: initialPosition,
|
|
670
|
-
},
|
|
671
|
-
}),
|
|
672
|
-
);
|
|
673
|
-
|
|
674
|
-
this._monthScroller.position = this._targetPosition;
|
|
675
|
-
this._targetPosition = undefined;
|
|
676
|
-
|
|
677
|
-
revealResolve();
|
|
678
|
-
this._revealPromise = undefined;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
setTimeout(this._repositionYearScroller.bind(this), 1);
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
// Start the animation.
|
|
685
|
-
window.requestAnimationFrame(smoothScroll);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
_limit(value, range) {
|
|
689
|
-
return Math.min(range.max, Math.max(range.min, value));
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
_handleTrack(e) {
|
|
693
|
-
// Check if horizontal movement threshold (dx) not exceeded or
|
|
694
|
-
// scrolling fast vertically (ddy).
|
|
695
|
-
if (Math.abs(e.detail.dx) < 10 || Math.abs(e.detail.ddy) > 10) {
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// If we're flinging quickly -> start animating already.
|
|
700
|
-
if (Math.abs(e.detail.ddx) > this._yearScrollerWidth / 3) {
|
|
701
|
-
this._toggleAnimateClass(true);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
const newTranslateX = this._translateX + e.detail.ddx;
|
|
705
|
-
this._translateX = this._limit(newTranslateX, {
|
|
706
|
-
min: 0,
|
|
707
|
-
max: this._yearScrollerWidth,
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
_track(e) {
|
|
712
|
-
if (this._desktopMode) {
|
|
713
|
-
// No need to track for swipe gestures on desktop.
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
switch (e.detail.state) {
|
|
718
|
-
case 'start':
|
|
719
|
-
this._toggleAnimateClass(false);
|
|
720
|
-
break;
|
|
721
|
-
case 'track':
|
|
722
|
-
this._handleTrack(e);
|
|
723
|
-
break;
|
|
724
|
-
case 'end':
|
|
725
|
-
this._toggleAnimateClass(true);
|
|
726
|
-
if (this._translateX >= this._yearScrollerWidth / 2) {
|
|
727
|
-
this._closeYearScroller();
|
|
728
|
-
} else {
|
|
729
|
-
this._openYearScroller();
|
|
730
|
-
}
|
|
731
|
-
break;
|
|
732
|
-
default:
|
|
733
|
-
break;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
_toggleAnimateClass(enable) {
|
|
738
|
-
if (enable) {
|
|
739
|
-
this.classList.add('animate');
|
|
740
|
-
} else {
|
|
741
|
-
this.classList.remove('animate');
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
_toggleYearScroller() {
|
|
746
|
-
if (this._isYearScrollerVisible()) {
|
|
747
|
-
this._closeYearScroller();
|
|
748
|
-
} else {
|
|
749
|
-
this._openYearScroller();
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
_openYearScroller() {
|
|
754
|
-
this._translateX = 0;
|
|
755
|
-
this.setAttribute('years-visible', '');
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
_closeYearScroller() {
|
|
759
|
-
this.removeAttribute('years-visible');
|
|
760
|
-
this._translateX = this._yearScrollerWidth;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
_isYearScrollerVisible() {
|
|
764
|
-
return this._translateX < this._yearScrollerWidth / 2;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
_translateXChanged(x) {
|
|
768
|
-
if (!this._desktopMode) {
|
|
769
|
-
this._monthScroller.style.transform = `translateX(${x - this._yearScrollerWidth}px)`;
|
|
770
|
-
this._yearScroller.style.transform = `translateX(${x}px)`;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
_yearAfterXMonths(months) {
|
|
775
|
-
return dateAfterXMonths(months).getFullYear();
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
_differenceInMonths(date1, date2) {
|
|
779
|
-
const months = (date1.getFullYear() - date2.getFullYear()) * 12;
|
|
780
|
-
return months - date2.getMonth() + date1.getMonth();
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
_clear() {
|
|
784
|
-
this._selectDate('');
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
_close() {
|
|
788
|
-
this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }));
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
_cancel() {
|
|
792
|
-
this.focusedDate = this.selectedDate;
|
|
793
|
-
this._close();
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
_preventDefault(e) {
|
|
797
|
-
e.preventDefault();
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
__toggleDate(date) {
|
|
801
|
-
if (dateEquals(date, this.selectedDate)) {
|
|
802
|
-
this._clear();
|
|
803
|
-
this.focusedDate = date;
|
|
804
|
-
} else {
|
|
805
|
-
this._selectDate(date);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
__onMonthCalendarKeyDown(event) {
|
|
810
|
-
let handled = false;
|
|
811
|
-
|
|
812
|
-
switch (event.key) {
|
|
813
|
-
case 'ArrowDown':
|
|
814
|
-
this._moveFocusByDays(7);
|
|
815
|
-
handled = true;
|
|
816
|
-
break;
|
|
817
|
-
case 'ArrowUp':
|
|
818
|
-
this._moveFocusByDays(-7);
|
|
819
|
-
handled = true;
|
|
820
|
-
break;
|
|
821
|
-
case 'ArrowRight':
|
|
822
|
-
this._moveFocusByDays(this.__isRTL ? -1 : 1);
|
|
823
|
-
handled = true;
|
|
824
|
-
break;
|
|
825
|
-
case 'ArrowLeft':
|
|
826
|
-
this._moveFocusByDays(this.__isRTL ? 1 : -1);
|
|
827
|
-
handled = true;
|
|
828
|
-
break;
|
|
829
|
-
case 'Enter':
|
|
830
|
-
this._selectDate(this.focusedDate);
|
|
831
|
-
this._close();
|
|
832
|
-
handled = true;
|
|
833
|
-
break;
|
|
834
|
-
case ' ':
|
|
835
|
-
this.__toggleDate(this.focusedDate);
|
|
836
|
-
handled = true;
|
|
837
|
-
break;
|
|
838
|
-
case 'Home':
|
|
839
|
-
this._moveFocusInsideMonth(this.focusedDate, 'minDate');
|
|
840
|
-
handled = true;
|
|
841
|
-
break;
|
|
842
|
-
case 'End':
|
|
843
|
-
this._moveFocusInsideMonth(this.focusedDate, 'maxDate');
|
|
844
|
-
handled = true;
|
|
845
|
-
break;
|
|
846
|
-
case 'PageDown':
|
|
847
|
-
this._moveFocusByMonths(event.shiftKey ? 12 : 1);
|
|
848
|
-
handled = true;
|
|
849
|
-
break;
|
|
850
|
-
case 'PageUp':
|
|
851
|
-
this._moveFocusByMonths(event.shiftKey ? -12 : -1);
|
|
852
|
-
handled = true;
|
|
853
|
-
break;
|
|
854
|
-
case 'Tab':
|
|
855
|
-
this._onTabKeyDown(event, 'calendar');
|
|
856
|
-
break;
|
|
857
|
-
default:
|
|
858
|
-
break;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (handled) {
|
|
862
|
-
event.preventDefault();
|
|
863
|
-
event.stopPropagation();
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
_onTabKeyDown(event, section) {
|
|
868
|
-
// Stop propagation to avoid focus-trap
|
|
869
|
-
// listener when used in a modal dialog.
|
|
870
|
-
event.stopPropagation();
|
|
871
|
-
|
|
872
|
-
switch (section) {
|
|
873
|
-
case 'calendar':
|
|
874
|
-
if (event.shiftKey) {
|
|
875
|
-
event.preventDefault();
|
|
876
|
-
|
|
877
|
-
if (this.hasAttribute('fullscreen')) {
|
|
878
|
-
// Trap focus in the overlay
|
|
879
|
-
this.focusCancel();
|
|
880
|
-
} else {
|
|
881
|
-
this.__focusInput();
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
break;
|
|
885
|
-
case 'today':
|
|
886
|
-
if (event.shiftKey) {
|
|
887
|
-
event.preventDefault();
|
|
888
|
-
this.focusDateElement();
|
|
889
|
-
}
|
|
890
|
-
break;
|
|
891
|
-
case 'cancel':
|
|
892
|
-
if (!event.shiftKey) {
|
|
893
|
-
event.preventDefault();
|
|
894
|
-
|
|
895
|
-
if (this.hasAttribute('fullscreen')) {
|
|
896
|
-
// Trap focus in the overlay
|
|
897
|
-
this.focusDateElement();
|
|
898
|
-
} else {
|
|
899
|
-
this.__focusInput();
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
break;
|
|
903
|
-
default:
|
|
904
|
-
break;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
__onTodayButtonKeyDown(event) {
|
|
909
|
-
if (event.key === 'Tab') {
|
|
910
|
-
this._onTabKeyDown(event, 'today');
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
__onCancelButtonKeyDown(event) {
|
|
915
|
-
if (event.key === 'Tab') {
|
|
916
|
-
this._onTabKeyDown(event, 'cancel');
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
__focusInput() {
|
|
921
|
-
this.dispatchEvent(new CustomEvent('focus-input', { bubbles: true, composed: true }));
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
__tryFocusDate() {
|
|
925
|
-
const dateToFocus = this.__pendingDateFocus;
|
|
926
|
-
if (dateToFocus) {
|
|
927
|
-
// Check the date element with tabindex="0"
|
|
928
|
-
const dateElement = this.focusableDateElement;
|
|
929
|
-
|
|
930
|
-
if (dateElement && dateEquals(dateElement.date, this.__pendingDateFocus)) {
|
|
931
|
-
delete this.__pendingDateFocus;
|
|
932
|
-
dateElement.focus();
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
async focusDate(date, keepMonth) {
|
|
938
|
-
const dateToFocus = date || this.selectedDate || this.initialPosition || new Date();
|
|
939
|
-
this.focusedDate = dateToFocus;
|
|
940
|
-
if (!keepMonth) {
|
|
941
|
-
this._focusedMonthDate = dateToFocus.getDate();
|
|
942
|
-
}
|
|
943
|
-
await this.focusDateElement(false);
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
async focusDateElement(reveal = true) {
|
|
947
|
-
this.__pendingDateFocus = this.focusedDate;
|
|
948
|
-
|
|
949
|
-
// Wait for `vaadin-month-calendar` elements to be rendered
|
|
950
|
-
if (!this.calendars.length) {
|
|
951
|
-
await new Promise((resolve) => {
|
|
952
|
-
afterNextRender(this, () => {
|
|
953
|
-
// Force dom-repeat elements to render
|
|
954
|
-
flush();
|
|
955
|
-
resolve();
|
|
956
|
-
});
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// Reveal focused date unless it has been just set,
|
|
961
|
-
// which triggers `revealDate()` in the observer.
|
|
962
|
-
if (reveal) {
|
|
963
|
-
this.revealDate(this.focusedDate);
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
if (this._revealPromise) {
|
|
967
|
-
// Wait for focused date to be scrolled into view.
|
|
968
|
-
await this._revealPromise;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
this.__tryFocusDate();
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
_focusClosestDate(focus) {
|
|
975
|
-
this.focusDate(getClosestDate(focus, [this.minDate, this.maxDate]));
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
_focusAllowedDate(dateToFocus, diff, keepMonth) {
|
|
979
|
-
if (this._dateAllowed(dateToFocus)) {
|
|
980
|
-
this.focusDate(dateToFocus, keepMonth);
|
|
981
|
-
} else if (this._dateAllowed(this.focusedDate)) {
|
|
982
|
-
// Move to min or max date
|
|
983
|
-
if (diff > 0) {
|
|
984
|
-
// Down, Right or Page Down key
|
|
985
|
-
this.focusDate(this.maxDate);
|
|
986
|
-
} else {
|
|
987
|
-
// Up, Left or Page Up key
|
|
988
|
-
this.focusDate(this.minDate);
|
|
989
|
-
}
|
|
990
|
-
} else {
|
|
991
|
-
// Move to closest allowed date
|
|
992
|
-
this._focusClosestDate(this.focusedDate);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
_getDateDiff(months, days) {
|
|
997
|
-
const date = new Date(0, 0);
|
|
998
|
-
date.setFullYear(this.focusedDate.getFullYear());
|
|
999
|
-
date.setMonth(this.focusedDate.getMonth() + months);
|
|
1000
|
-
if (days) {
|
|
1001
|
-
date.setDate(this.focusedDate.getDate() + days);
|
|
1002
|
-
}
|
|
1003
|
-
return date;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
_moveFocusByDays(days) {
|
|
1007
|
-
const dateToFocus = this._getDateDiff(0, days);
|
|
1008
|
-
|
|
1009
|
-
this._focusAllowedDate(dateToFocus, days, false);
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
_moveFocusByMonths(months) {
|
|
1013
|
-
const dateToFocus = this._getDateDiff(months);
|
|
1014
|
-
const targetMonth = dateToFocus.getMonth();
|
|
1015
|
-
|
|
1016
|
-
if (!this._focusedMonthDate) {
|
|
1017
|
-
this._focusedMonthDate = this.focusedDate.getDate();
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
dateToFocus.setDate(this._focusedMonthDate);
|
|
1021
|
-
|
|
1022
|
-
if (dateToFocus.getMonth() !== targetMonth) {
|
|
1023
|
-
dateToFocus.setDate(0);
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
this._focusAllowedDate(dateToFocus, months, true);
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
_moveFocusInsideMonth(focusedDate, property) {
|
|
1030
|
-
const dateToFocus = new Date(0, 0);
|
|
1031
|
-
dateToFocus.setFullYear(focusedDate.getFullYear());
|
|
1032
|
-
|
|
1033
|
-
if (property === 'minDate') {
|
|
1034
|
-
dateToFocus.setMonth(focusedDate.getMonth());
|
|
1035
|
-
dateToFocus.setDate(1);
|
|
1036
|
-
} else {
|
|
1037
|
-
dateToFocus.setMonth(focusedDate.getMonth() + 1);
|
|
1038
|
-
dateToFocus.setDate(0);
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
if (this._dateAllowed(dateToFocus)) {
|
|
1042
|
-
this.focusDate(dateToFocus);
|
|
1043
|
-
} else if (this._dateAllowed(focusedDate)) {
|
|
1044
|
-
// Move to minDate or maxDate
|
|
1045
|
-
this.focusDate(this[property]);
|
|
1046
|
-
} else {
|
|
1047
|
-
// Move to closest allowed date
|
|
1048
|
-
this._focusClosestDate(focusedDate);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
_dateAllowed(date, min = this.minDate, max = this.maxDate) {
|
|
1053
|
-
return (!min || date >= min) && (!max || date <= max);
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
_isTodayAllowed(min, max) {
|
|
1057
|
-
const today = new Date();
|
|
1058
|
-
const todayMidnight = new Date(0, 0);
|
|
1059
|
-
todayMidnight.setFullYear(today.getFullYear());
|
|
1060
|
-
todayMidnight.setMonth(today.getMonth());
|
|
1061
|
-
todayMidnight.setDate(today.getDate());
|
|
1062
|
-
return this._dateAllowed(todayMidnight, min, max);
|
|
63
|
+
this._addListeners();
|
|
64
|
+
this._initControllers();
|
|
1063
65
|
}
|
|
1064
66
|
}
|
|
1065
67
|
|