@vaadin/date-picker 23.0.0-alpha3 → 23.0.0-beta2
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/date-picker",
|
|
3
|
-
"version": "23.0.0-
|
|
3
|
+
"version": "23.0.0-beta2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"main": "vaadin-date-picker.js",
|
|
20
20
|
"module": "vaadin-date-picker.js",
|
|
21
|
+
"type": "module",
|
|
21
22
|
"files": [
|
|
22
23
|
"src",
|
|
23
24
|
"theme",
|
|
@@ -34,21 +35,21 @@
|
|
|
34
35
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
35
36
|
"@polymer/iron-media-query": "^3.0.0",
|
|
36
37
|
"@polymer/polymer": "^3.2.0",
|
|
37
|
-
"@vaadin/button": "23.0.0-
|
|
38
|
-
"@vaadin/component-base": "23.0.0-
|
|
39
|
-
"@vaadin/field-base": "23.0.0-
|
|
40
|
-
"@vaadin/input-container": "23.0.0-
|
|
41
|
-
"@vaadin/vaadin-lumo-styles": "23.0.0-
|
|
42
|
-
"@vaadin/vaadin-material-styles": "23.0.0-
|
|
43
|
-
"@vaadin/vaadin-overlay": "23.0.0-
|
|
44
|
-
"@vaadin/vaadin-themable-mixin": "23.0.0-
|
|
38
|
+
"@vaadin/button": "23.0.0-beta2",
|
|
39
|
+
"@vaadin/component-base": "23.0.0-beta2",
|
|
40
|
+
"@vaadin/field-base": "23.0.0-beta2",
|
|
41
|
+
"@vaadin/input-container": "23.0.0-beta2",
|
|
42
|
+
"@vaadin/vaadin-lumo-styles": "23.0.0-beta2",
|
|
43
|
+
"@vaadin/vaadin-material-styles": "23.0.0-beta2",
|
|
44
|
+
"@vaadin/vaadin-overlay": "23.0.0-beta2",
|
|
45
|
+
"@vaadin/vaadin-themable-mixin": "23.0.0-beta2"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@esm-bundle/chai": "^4.3.4",
|
|
48
|
-
"@vaadin/dialog": "23.0.0-
|
|
49
|
-
"@vaadin/polymer-legacy-adapter": "23.0.0-
|
|
49
|
+
"@vaadin/dialog": "23.0.0-beta2",
|
|
50
|
+
"@vaadin/polymer-legacy-adapter": "23.0.0-beta2",
|
|
50
51
|
"@vaadin/testing-helpers": "^0.3.2",
|
|
51
52
|
"sinon": "^9.2.0"
|
|
52
53
|
},
|
|
53
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "a276f7a0fd00e5459b87267468e0dd0d4fb6f7f3"
|
|
54
55
|
}
|
|
@@ -319,11 +319,6 @@ export const DatePickerMixin = (subclass) =>
|
|
|
319
319
|
value: document.createElement('div').style.webkitOverflowScrolling === ''
|
|
320
320
|
},
|
|
321
321
|
|
|
322
|
-
/** @private */
|
|
323
|
-
_ignoreAnnounce: {
|
|
324
|
-
value: true
|
|
325
|
-
},
|
|
326
|
-
|
|
327
322
|
/** @private */
|
|
328
323
|
_focusOverlayOnOpen: Boolean,
|
|
329
324
|
|
|
@@ -335,8 +330,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
335
330
|
static get observers() {
|
|
336
331
|
return [
|
|
337
332
|
'_selectedDateChanged(_selectedDate, i18n.formatDate)',
|
|
338
|
-
'_focusedDateChanged(_focusedDate, i18n.formatDate)'
|
|
339
|
-
'_announceFocusedDate(_focusedDate, opened, _ignoreAnnounce)'
|
|
333
|
+
'_focusedDateChanged(_focusedDate, i18n.formatDate)'
|
|
340
334
|
];
|
|
341
335
|
}
|
|
342
336
|
|
|
@@ -582,7 +576,9 @@ export const DatePickerMixin = (subclass) =>
|
|
|
582
576
|
_inputElementChanged(input) {
|
|
583
577
|
super._inputElementChanged(input);
|
|
584
578
|
if (input) {
|
|
579
|
+
input.autocomplete = 'off';
|
|
585
580
|
input.setAttribute('role', 'combobox');
|
|
581
|
+
input.setAttribute('aria-haspopup', 'dialog');
|
|
586
582
|
input.setAttribute('aria-expanded', !!this.opened);
|
|
587
583
|
this._applyInputValue(this._selectedDate);
|
|
588
584
|
}
|
|
@@ -709,7 +705,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
709
705
|
}
|
|
710
706
|
|
|
711
707
|
if (this._focusOverlayOnOpen) {
|
|
712
|
-
this._overlayContent.
|
|
708
|
+
this._overlayContent.focusDateElement();
|
|
713
709
|
this._focusOverlayOnOpen = false;
|
|
714
710
|
} else {
|
|
715
711
|
this._focus();
|
|
@@ -718,8 +714,6 @@ export const DatePickerMixin = (subclass) =>
|
|
|
718
714
|
if (this._noInput && this.focusElement) {
|
|
719
715
|
this.focusElement.blur();
|
|
720
716
|
}
|
|
721
|
-
|
|
722
|
-
this._ignoreAnnounce = false;
|
|
723
717
|
}
|
|
724
718
|
|
|
725
719
|
// A hack needed for iOS to prevent dropdown from being clipped in an
|
|
@@ -764,8 +758,6 @@ export const DatePickerMixin = (subclass) =>
|
|
|
764
758
|
|
|
765
759
|
/** @protected */
|
|
766
760
|
_onOverlayClosed() {
|
|
767
|
-
this._ignoreAnnounce = true;
|
|
768
|
-
|
|
769
761
|
window.removeEventListener('scroll', this._boundOnScroll, true);
|
|
770
762
|
|
|
771
763
|
if (this._touchPrevented) {
|
|
@@ -890,15 +882,14 @@ export const DatePickerMixin = (subclass) =>
|
|
|
890
882
|
case 'ArrowUp':
|
|
891
883
|
// prevent scrolling the page with arrows
|
|
892
884
|
e.preventDefault();
|
|
893
|
-
|
|
894
885
|
if (this.opened) {
|
|
895
|
-
|
|
896
|
-
this.
|
|
886
|
+
// The overlay can be opened with ctrl + option + shift in VoiceOver
|
|
887
|
+
// and without this logic, it won't be possible to focus the dialog opened this way.
|
|
888
|
+
this._overlayContent.focusDateElement();
|
|
897
889
|
} else {
|
|
898
890
|
this._focusOverlayOnOpen = true;
|
|
899
891
|
this.open();
|
|
900
892
|
}
|
|
901
|
-
|
|
902
893
|
break;
|
|
903
894
|
case 'Enter': {
|
|
904
895
|
const parsedDate = this._getParsedDate();
|
|
@@ -944,8 +935,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
944
935
|
if (e.shiftKey) {
|
|
945
936
|
this._overlayContent.focusCancel();
|
|
946
937
|
} else {
|
|
947
|
-
this._overlayContent.
|
|
948
|
-
this._overlayContent.revealDate(this._focusedDate);
|
|
938
|
+
this._overlayContent.focusDate(this._focusedDate);
|
|
949
939
|
}
|
|
950
940
|
}
|
|
951
941
|
break;
|
|
@@ -993,13 +983,6 @@ export const DatePickerMixin = (subclass) =>
|
|
|
993
983
|
}
|
|
994
984
|
}
|
|
995
985
|
|
|
996
|
-
/** @private */
|
|
997
|
-
_announceFocusedDate(_focusedDate, opened, _ignoreAnnounce) {
|
|
998
|
-
if (opened && !_ignoreAnnounce) {
|
|
999
|
-
this._overlayContent.announceFocusedDate();
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
986
|
/** @private */
|
|
1004
987
|
get _overlayContent() {
|
|
1005
988
|
return this.$.overlay.content.querySelector('#overlay-content');
|
|
@@ -8,13 +8,12 @@ import '@vaadin/button/src/vaadin-button.js';
|
|
|
8
8
|
import './vaadin-month-calendar.js';
|
|
9
9
|
import './vaadin-infinite-scroller.js';
|
|
10
10
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
11
|
-
import { announce } from '@vaadin/component-base/src/a11y-announcer.js';
|
|
12
11
|
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
13
12
|
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
14
13
|
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
15
14
|
import { addListener, setTouchAction } from '@vaadin/component-base/src/gestures.js';
|
|
16
15
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
17
|
-
import { dateEquals, extractDateParts, getClosestDate
|
|
16
|
+
import { dateEquals, extractDateParts, getClosestDate } from './vaadin-date-picker-helper.js';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* @extends HTMLElement
|
|
@@ -150,17 +149,8 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
150
149
|
z-index: 1;
|
|
151
150
|
padding: 8px;
|
|
152
151
|
}
|
|
153
|
-
|
|
154
|
-
#announcer {
|
|
155
|
-
display: inline-block;
|
|
156
|
-
position: fixed;
|
|
157
|
-
clip: rect(0, 0, 0, 0);
|
|
158
|
-
clip-path: inset(100%);
|
|
159
|
-
}
|
|
160
152
|
</style>
|
|
161
153
|
|
|
162
|
-
<div id="announcer" role="alert" aria-live="polite">[[i18n.calendar]]</div>
|
|
163
|
-
|
|
164
154
|
<div part="overlay-header" on-touchend="_preventDefault" desktop$="[[_desktopMode]]" aria-hidden="true">
|
|
165
155
|
<div part="label">[[_formatDisplayed(selectedDate, i18n.formatDate, label)]]</div>
|
|
166
156
|
<div part="clear-button" showclear$="[[_showClear(selectedDate)]]"></div>
|
|
@@ -190,9 +180,9 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
190
180
|
show-week-numbers="[[showWeekNumbers]]"
|
|
191
181
|
min-date="[[minDate]]"
|
|
192
182
|
max-date="[[maxDate]]"
|
|
193
|
-
focused$="[[_focused]]"
|
|
194
183
|
part="month"
|
|
195
184
|
theme$="[[theme]]"
|
|
185
|
+
on-keydown="__onMonthCalendarKeyDown"
|
|
196
186
|
>
|
|
197
187
|
</vaadin-month-calendar>
|
|
198
188
|
</template>
|
|
@@ -204,11 +194,11 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
204
194
|
buffer-size="12"
|
|
205
195
|
active="[[initialPosition]]"
|
|
206
196
|
part="years"
|
|
197
|
+
aria-hidden="true"
|
|
207
198
|
>
|
|
208
199
|
<template>
|
|
209
200
|
<div
|
|
210
201
|
part="year-number"
|
|
211
|
-
role="button"
|
|
212
202
|
current$="[[_isCurrentYear(index)]]"
|
|
213
203
|
selected$="[[_isSelectedYear(index, selectedDate)]]"
|
|
214
204
|
>
|
|
@@ -225,10 +215,13 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
225
215
|
part="today-button"
|
|
226
216
|
theme="tertiary"
|
|
227
217
|
disabled="[[!_isTodayAllowed(minDate, maxDate)]]"
|
|
218
|
+
on-keydown="__onTodayButtonKeyDown"
|
|
228
219
|
>
|
|
229
220
|
[[i18n.today]]
|
|
230
221
|
</vaadin-button>
|
|
231
|
-
<vaadin-button id="cancelButton" part="cancel-button" theme="tertiary"
|
|
222
|
+
<vaadin-button id="cancelButton" part="cancel-button" theme="tertiary" on-keydown="__onCancelButtonKeyDown">
|
|
223
|
+
[[i18n.cancel]]
|
|
224
|
+
</vaadin-button>
|
|
232
225
|
</div>
|
|
233
226
|
<iron-media-query query="(min-width: 375px)" query-matches="{{_desktopMode}}"></iron-media-query>
|
|
234
227
|
`;
|
|
@@ -305,8 +298,6 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
305
298
|
*/
|
|
306
299
|
maxDate: Date,
|
|
307
300
|
|
|
308
|
-
_focused: Boolean,
|
|
309
|
-
|
|
310
301
|
/**
|
|
311
302
|
* Input label
|
|
312
303
|
*/
|
|
@@ -318,13 +309,15 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
318
309
|
return this.getAttribute('dir') === 'rtl';
|
|
319
310
|
}
|
|
320
311
|
|
|
312
|
+
get focusableDateElement() {
|
|
313
|
+
return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')]
|
|
314
|
+
.map((calendar) => calendar.focusableDateElement)
|
|
315
|
+
.find(Boolean);
|
|
316
|
+
}
|
|
317
|
+
|
|
321
318
|
ready() {
|
|
322
319
|
super.ready();
|
|
323
|
-
this.setAttribute('tabindex', 0);
|
|
324
|
-
this.addEventListener('keydown', this._onKeydown.bind(this));
|
|
325
320
|
addListener(this, 'tap', this._stopPropagation);
|
|
326
|
-
this.addEventListener('focus', this._onOverlayFocus.bind(this));
|
|
327
|
-
this.addEventListener('blur', this._onOverlayBlur.bind(this));
|
|
328
321
|
addListener(this.$.scrollers, 'track', this._track.bind(this));
|
|
329
322
|
addListener(this.shadowRoot.querySelector('[part="clear-button"]'), 'tap', this._clear.bind(this));
|
|
330
323
|
addListener(this.shadowRoot.querySelector('[part="today-button"]'), 'tap', this._onTodayTap.bind(this));
|
|
@@ -352,25 +345,6 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
352
345
|
setTouchAction(this.$.scrollers, 'pan-y');
|
|
353
346
|
}
|
|
354
347
|
|
|
355
|
-
announceFocusedDate() {
|
|
356
|
-
const focusedDate = this._currentlyFocusedDate();
|
|
357
|
-
let messages = [];
|
|
358
|
-
if (dateEquals(focusedDate, new Date())) {
|
|
359
|
-
messages.push(this.i18n.today);
|
|
360
|
-
}
|
|
361
|
-
messages = messages.concat([
|
|
362
|
-
this.i18n.weekdays[focusedDate.getDay()],
|
|
363
|
-
focusedDate.getDate(),
|
|
364
|
-
this.i18n.monthNames[focusedDate.getMonth()],
|
|
365
|
-
focusedDate.getFullYear()
|
|
366
|
-
]);
|
|
367
|
-
if (this.showWeekNumbers && this.i18n.firstDayOfWeek === 1) {
|
|
368
|
-
messages.push(this.i18n.week);
|
|
369
|
-
messages.push(getISOWeekNumber(focusedDate));
|
|
370
|
-
}
|
|
371
|
-
announce(messages.join(' '));
|
|
372
|
-
}
|
|
373
|
-
|
|
374
348
|
/**
|
|
375
349
|
* Focuses the cancel button
|
|
376
350
|
*/
|
|
@@ -422,14 +396,6 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
422
396
|
}
|
|
423
397
|
}
|
|
424
398
|
|
|
425
|
-
_onOverlayFocus() {
|
|
426
|
-
this._focused = true;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
_onOverlayBlur() {
|
|
430
|
-
this._focused = false;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
399
|
_initialPositionChanged(initialPosition) {
|
|
434
400
|
this.scrollToDate(initialPosition);
|
|
435
401
|
}
|
|
@@ -521,6 +487,7 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
521
487
|
this.$.monthScroller.position = targetPosition;
|
|
522
488
|
this._targetPosition = undefined;
|
|
523
489
|
this._repositionYearScroller();
|
|
490
|
+
this.__tryFocusDate();
|
|
524
491
|
return;
|
|
525
492
|
}
|
|
526
493
|
|
|
@@ -562,6 +529,7 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
562
529
|
|
|
563
530
|
this.$.monthScroller.position = this._targetPosition;
|
|
564
531
|
this._targetPosition = undefined;
|
|
532
|
+
this.__tryFocusDate();
|
|
565
533
|
}
|
|
566
534
|
|
|
567
535
|
setTimeout(this._repositionYearScroller.bind(this), 1);
|
|
@@ -702,150 +670,168 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
702
670
|
e.preventDefault();
|
|
703
671
|
}
|
|
704
672
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const isCancel = e.composedPath().indexOf(this.$.cancelButton) >= 0;
|
|
712
|
-
const isScroller = !isToday && !isCancel;
|
|
713
|
-
|
|
714
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
|
715
|
-
const navigationKeys = [
|
|
716
|
-
' ',
|
|
717
|
-
'ArrowDown',
|
|
718
|
-
'ArrowUp',
|
|
719
|
-
'ArrowRight',
|
|
720
|
-
'ArrowLeft',
|
|
721
|
-
'Enter',
|
|
722
|
-
'End',
|
|
723
|
-
'Escape',
|
|
724
|
-
'Home',
|
|
725
|
-
'PageUp',
|
|
726
|
-
'PageDown',
|
|
727
|
-
'Tab'
|
|
728
|
-
];
|
|
729
|
-
|
|
730
|
-
const eventKey = e.key;
|
|
731
|
-
if (eventKey === 'Tab') {
|
|
732
|
-
// We handle tabs here and don't want to bubble up.
|
|
733
|
-
e.stopPropagation();
|
|
734
|
-
|
|
735
|
-
const isFullscreen = this.hasAttribute('fullscreen');
|
|
736
|
-
const isShift = e.shiftKey;
|
|
737
|
-
|
|
738
|
-
if (isFullscreen) {
|
|
739
|
-
e.preventDefault();
|
|
740
|
-
} else if ((isShift && isScroller) || (!isShift && isCancel)) {
|
|
741
|
-
// Return focus back to the input field
|
|
742
|
-
e.preventDefault();
|
|
743
|
-
this.dispatchEvent(new CustomEvent('focus-input', { bubbles: true, composed: true }));
|
|
744
|
-
} else if (isShift && isToday) {
|
|
745
|
-
// Browser returns focus back to the scrollable area. We need to set
|
|
746
|
-
// the focused flag, and move the scroll to focused date.
|
|
747
|
-
this._focused = true;
|
|
748
|
-
setTimeout(() => this.revealDate(this.focusedDate), 1);
|
|
749
|
-
} else {
|
|
750
|
-
// Browser moves the focus out of the scroller, hence focused flag must
|
|
751
|
-
// set to false.
|
|
752
|
-
this._focused = false;
|
|
753
|
-
}
|
|
754
|
-
} else if (navigationKeys.includes(eventKey)) {
|
|
755
|
-
e.preventDefault();
|
|
756
|
-
e.stopPropagation();
|
|
757
|
-
switch (eventKey) {
|
|
758
|
-
case 'ArrowDown':
|
|
759
|
-
this._moveFocusByDays(7);
|
|
760
|
-
this.focus();
|
|
761
|
-
break;
|
|
762
|
-
case 'ArrowUp':
|
|
763
|
-
this._moveFocusByDays(-7);
|
|
764
|
-
this.focus();
|
|
765
|
-
break;
|
|
766
|
-
case 'ArrowRight':
|
|
767
|
-
if (isScroller) {
|
|
768
|
-
this._moveFocusByDays(this.__isRTL ? -1 : 1);
|
|
769
|
-
}
|
|
770
|
-
break;
|
|
771
|
-
case 'ArrowLeft':
|
|
772
|
-
if (isScroller) {
|
|
773
|
-
this._moveFocusByDays(this.__isRTL ? 1 : -1);
|
|
774
|
-
}
|
|
775
|
-
break;
|
|
776
|
-
case 'Enter':
|
|
777
|
-
if (isScroller || isCancel) {
|
|
778
|
-
this._close();
|
|
779
|
-
} else if (isToday) {
|
|
780
|
-
this._onTodayTap();
|
|
781
|
-
}
|
|
782
|
-
break;
|
|
783
|
-
case ' ':
|
|
784
|
-
if (isCancel) {
|
|
785
|
-
this._close();
|
|
786
|
-
} else if (isToday) {
|
|
787
|
-
this._onTodayTap();
|
|
788
|
-
} else {
|
|
789
|
-
var focusedDate = this.focusedDate;
|
|
790
|
-
if (dateEquals(focusedDate, this.selectedDate)) {
|
|
791
|
-
this.selectedDate = '';
|
|
792
|
-
this.focusedDate = focusedDate;
|
|
793
|
-
} else {
|
|
794
|
-
this.selectedDate = focusedDate;
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
break;
|
|
798
|
-
case 'Home':
|
|
799
|
-
this._moveFocusInsideMonth(focus, 'minDate');
|
|
800
|
-
break;
|
|
801
|
-
case 'End':
|
|
802
|
-
this._moveFocusInsideMonth(focus, 'maxDate');
|
|
803
|
-
break;
|
|
804
|
-
case 'PageDown':
|
|
805
|
-
this._moveFocusByMonths(e.shiftKey ? 12 : 1);
|
|
806
|
-
break;
|
|
807
|
-
case 'PageUp':
|
|
808
|
-
this._moveFocusByMonths(e.shiftKey ? -12 : -1);
|
|
809
|
-
break;
|
|
810
|
-
case 'Escape':
|
|
811
|
-
this._cancel();
|
|
812
|
-
break;
|
|
813
|
-
default:
|
|
814
|
-
break;
|
|
815
|
-
}
|
|
673
|
+
__toggleDate(date) {
|
|
674
|
+
if (dateEquals(date, this.selectedDate)) {
|
|
675
|
+
this.selectedDate = '';
|
|
676
|
+
this.focusedDate = date;
|
|
677
|
+
} else {
|
|
678
|
+
this.selectedDate = date;
|
|
816
679
|
}
|
|
817
680
|
}
|
|
818
681
|
|
|
819
|
-
|
|
820
|
-
|
|
682
|
+
__onMonthCalendarKeyDown(event) {
|
|
683
|
+
let handled = false;
|
|
684
|
+
|
|
685
|
+
switch (event.key) {
|
|
686
|
+
case 'ArrowDown':
|
|
687
|
+
this._moveFocusByDays(7);
|
|
688
|
+
handled = true;
|
|
689
|
+
break;
|
|
690
|
+
case 'ArrowUp':
|
|
691
|
+
this._moveFocusByDays(-7);
|
|
692
|
+
handled = true;
|
|
693
|
+
break;
|
|
694
|
+
case 'ArrowRight':
|
|
695
|
+
this._moveFocusByDays(this.__isRTL ? -1 : 1);
|
|
696
|
+
handled = true;
|
|
697
|
+
break;
|
|
698
|
+
case 'ArrowLeft':
|
|
699
|
+
this._moveFocusByDays(this.__isRTL ? 1 : -1);
|
|
700
|
+
handled = true;
|
|
701
|
+
break;
|
|
702
|
+
case 'Enter':
|
|
703
|
+
this.selectedDate = this.focusedDate;
|
|
704
|
+
this._close();
|
|
705
|
+
handled = true;
|
|
706
|
+
break;
|
|
707
|
+
case ' ':
|
|
708
|
+
this.__toggleDate(this.focusedDate);
|
|
709
|
+
handled = true;
|
|
710
|
+
break;
|
|
711
|
+
case 'Home':
|
|
712
|
+
this._moveFocusInsideMonth(this.focusedDate, 'minDate');
|
|
713
|
+
handled = true;
|
|
714
|
+
break;
|
|
715
|
+
case 'End':
|
|
716
|
+
this._moveFocusInsideMonth(this.focusedDate, 'maxDate');
|
|
717
|
+
handled = true;
|
|
718
|
+
break;
|
|
719
|
+
case 'PageDown':
|
|
720
|
+
this._moveFocusByMonths(event.shiftKey ? 12 : 1);
|
|
721
|
+
handled = true;
|
|
722
|
+
break;
|
|
723
|
+
case 'PageUp':
|
|
724
|
+
this._moveFocusByMonths(event.shiftKey ? -12 : -1);
|
|
725
|
+
handled = true;
|
|
726
|
+
break;
|
|
727
|
+
case 'Escape':
|
|
728
|
+
this._cancel();
|
|
729
|
+
handled = true;
|
|
730
|
+
break;
|
|
731
|
+
default:
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (handled) {
|
|
736
|
+
event.preventDefault();
|
|
737
|
+
event.stopPropagation();
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
__onTodayButtonKeyDown(event) {
|
|
742
|
+
if (this.hasAttribute('fullscreen')) {
|
|
743
|
+
event.stopPropagation();
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (event.key === 'Tab' && event.shiftKey) {
|
|
748
|
+
event.stopPropagation();
|
|
749
|
+
|
|
750
|
+
// Browser returns focus back to the calendar.
|
|
751
|
+
// We need to move the scroll to focused date.
|
|
752
|
+
setTimeout(() => this.revealDate(this.focusedDate), 1);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (event.key === 'Escape') {
|
|
756
|
+
this._cancel();
|
|
757
|
+
event.preventDefault();
|
|
758
|
+
event.stopPropagation();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
__onCancelButtonKeyDown(event) {
|
|
763
|
+
if (this.hasAttribute('fullscreen')) {
|
|
764
|
+
event.stopPropagation();
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (event.key === 'Tab' && !event.shiftKey) {
|
|
769
|
+
// Return focus back to the input field
|
|
770
|
+
event.preventDefault();
|
|
771
|
+
event.stopPropagation();
|
|
772
|
+
this.dispatchEvent(new CustomEvent('focus-input', { bubbles: true, composed: true }));
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (event.key === 'Escape') {
|
|
776
|
+
this._cancel();
|
|
777
|
+
event.preventDefault();
|
|
778
|
+
event.stopPropagation();
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
__tryFocusDate() {
|
|
783
|
+
const dateToFocus = this.__pendingDateFocus;
|
|
784
|
+
if (dateToFocus) {
|
|
785
|
+
// Check the date element with tabindex="0"
|
|
786
|
+
const dateElement = this.focusableDateElement;
|
|
787
|
+
|
|
788
|
+
if (dateElement && dateEquals(dateElement.date, this.__pendingDateFocus)) {
|
|
789
|
+
delete this.__pendingDateFocus;
|
|
790
|
+
dateElement.focus();
|
|
791
|
+
}
|
|
792
|
+
}
|
|
821
793
|
}
|
|
822
794
|
|
|
823
|
-
|
|
795
|
+
async focusDate(date, keepMonth) {
|
|
796
|
+
const dateToFocus = date || this.selectedDate || this.initialPosition || new Date();
|
|
824
797
|
this.focusedDate = dateToFocus;
|
|
825
|
-
|
|
798
|
+
if (!keepMonth) {
|
|
799
|
+
this._focusedMonthDate = dateToFocus.getDate();
|
|
800
|
+
}
|
|
801
|
+
await this.focusDateElement();
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
async focusDateElement() {
|
|
805
|
+
this.__pendingDateFocus = this.focusedDate;
|
|
806
|
+
|
|
807
|
+
await new Promise((resolve) => {
|
|
808
|
+
requestAnimationFrame(resolve);
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
this.__tryFocusDate();
|
|
826
812
|
}
|
|
827
813
|
|
|
828
814
|
_focusClosestDate(focus) {
|
|
829
|
-
this.
|
|
815
|
+
this.focusDate(getClosestDate(focus, [this.minDate, this.maxDate]));
|
|
830
816
|
}
|
|
831
817
|
|
|
832
818
|
_moveFocusByDays(days) {
|
|
833
|
-
var focus = this.
|
|
819
|
+
var focus = this.focusedDate;
|
|
834
820
|
var dateToFocus = new Date(0, 0);
|
|
835
821
|
dateToFocus.setFullYear(focus.getFullYear());
|
|
836
822
|
dateToFocus.setMonth(focus.getMonth());
|
|
837
823
|
dateToFocus.setDate(focus.getDate() + days);
|
|
838
824
|
|
|
839
825
|
if (this._dateAllowed(dateToFocus, this.minDate, this.maxDate)) {
|
|
840
|
-
this.
|
|
826
|
+
this.focusDate(dateToFocus);
|
|
841
827
|
} else if (this._dateAllowed(focus, this.minDate, this.maxDate)) {
|
|
842
828
|
// Move to min or max date
|
|
843
829
|
if (days > 0) {
|
|
844
830
|
// down or right
|
|
845
|
-
this.
|
|
831
|
+
this.focusDate(this.maxDate);
|
|
846
832
|
} else {
|
|
847
833
|
// up or left
|
|
848
|
-
this.
|
|
834
|
+
this.focusDate(this.minDate);
|
|
849
835
|
}
|
|
850
836
|
} else {
|
|
851
837
|
// Move to closest allowed date
|
|
@@ -854,7 +840,7 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
854
840
|
}
|
|
855
841
|
|
|
856
842
|
_moveFocusByMonths(months) {
|
|
857
|
-
var focus = this.
|
|
843
|
+
var focus = this.focusedDate;
|
|
858
844
|
var dateToFocus = new Date(0, 0);
|
|
859
845
|
dateToFocus.setFullYear(focus.getFullYear());
|
|
860
846
|
dateToFocus.setMonth(focus.getMonth() + months);
|
|
@@ -867,15 +853,15 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
867
853
|
}
|
|
868
854
|
|
|
869
855
|
if (this._dateAllowed(dateToFocus, this.minDate, this.maxDate)) {
|
|
870
|
-
this.
|
|
856
|
+
this.focusDate(dateToFocus, true);
|
|
871
857
|
} else if (this._dateAllowed(focus, this.minDate, this.maxDate)) {
|
|
872
858
|
// Move to min or max date
|
|
873
859
|
if (months > 0) {
|
|
874
860
|
// pagedown
|
|
875
|
-
this.
|
|
861
|
+
this.focusDate(this.maxDate);
|
|
876
862
|
} else {
|
|
877
863
|
// pageup
|
|
878
|
-
this.
|
|
864
|
+
this.focusDate(this.minDate);
|
|
879
865
|
}
|
|
880
866
|
} else {
|
|
881
867
|
// Move to closest allowed date
|
|
@@ -896,10 +882,10 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
896
882
|
}
|
|
897
883
|
|
|
898
884
|
if (this._dateAllowed(dateToFocus, this.minDate, this.maxDate)) {
|
|
899
|
-
this.
|
|
885
|
+
this.focusDate(dateToFocus);
|
|
900
886
|
} else if (this._dateAllowed(focusedDate, this.minDate, this.maxDate)) {
|
|
901
887
|
// Move to minDate or maxDate
|
|
902
|
-
this.
|
|
888
|
+
this.focusDate(this[property]);
|
|
903
889
|
} else {
|
|
904
890
|
// Move to closest allowed date
|
|
905
891
|
this._focusClosestDate(focusedDate);
|
|
@@ -13,6 +13,8 @@ registerStyles('vaadin-date-picker-overlay', datePickerOverlayStyles, {
|
|
|
13
13
|
moduleId: 'vaadin-date-picker-overlay-styles'
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
let memoizedTemplate;
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* An element used internally by `<vaadin-date-picker>`. Not intended to be used separately.
|
|
18
20
|
*
|
|
@@ -23,6 +25,15 @@ class DatePickerOverlay extends DisableUpgradeMixin(PositionMixin(OverlayElement
|
|
|
23
25
|
static get is() {
|
|
24
26
|
return 'vaadin-date-picker-overlay';
|
|
25
27
|
}
|
|
28
|
+
|
|
29
|
+
static get template() {
|
|
30
|
+
if (!memoizedTemplate) {
|
|
31
|
+
memoizedTemplate = super.template.cloneNode(true);
|
|
32
|
+
memoizedTemplate.content.querySelector('[part~="overlay"]').removeAttribute('tabindex');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return memoizedTemplate;
|
|
36
|
+
}
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
customElements.define(DatePickerOverlay.is, DatePickerOverlay);
|
|
@@ -9,7 +9,6 @@ import './vaadin-date-picker-overlay.js';
|
|
|
9
9
|
import './vaadin-date-picker-overlay-content.js';
|
|
10
10
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
11
11
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
12
|
-
import { addListener } from '@vaadin/component-base/src/gestures.js';
|
|
13
12
|
import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
14
13
|
import { InputController } from '@vaadin/field-base/src/input-controller.js';
|
|
15
14
|
import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-controller.js';
|
|
@@ -144,8 +143,8 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
|
|
|
144
143
|
>
|
|
145
144
|
<slot name="prefix" slot="prefix"></slot>
|
|
146
145
|
<slot name="input"></slot>
|
|
147
|
-
<div id="clearButton" part="clear-button" slot="suffix"></div>
|
|
148
|
-
<div part="toggle-button" slot="suffix"
|
|
146
|
+
<div id="clearButton" part="clear-button" slot="suffix" aria-hidden="true"></div>
|
|
147
|
+
<div part="toggle-button" slot="suffix" aria-hidden="true" on-click="_toggle"></div>
|
|
149
148
|
</vaadin-input-container>
|
|
150
149
|
|
|
151
150
|
<div part="helper-text">
|
|
@@ -213,7 +212,9 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
|
|
|
213
212
|
})
|
|
214
213
|
);
|
|
215
214
|
this.addController(new LabelledInputController(this.inputElement, this._labelController));
|
|
216
|
-
|
|
215
|
+
|
|
216
|
+
const toggleButton = this.shadowRoot.querySelector('[part="toggle-button"]');
|
|
217
|
+
toggleButton.addEventListener('mousedown', (e) => e.preventDefault());
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
/** @private */
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import '@polymer/polymer/lib/elements/dom-repeat.js';
|
|
7
7
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
8
|
+
import { FocusMixin } from '@vaadin/component-base/src/focus-mixin.js';
|
|
8
9
|
import { addListener } from '@vaadin/component-base/src/gestures.js';
|
|
9
10
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
10
11
|
import { dateAllowed, dateEquals, getISOWeekNumber } from './vaadin-date-picker-helper.js';
|
|
@@ -13,7 +14,7 @@ import { dateAllowed, dateEquals, getISOWeekNumber } from './vaadin-date-picker-
|
|
|
13
14
|
* @extends HTMLElement
|
|
14
15
|
* @private
|
|
15
16
|
*/
|
|
16
|
-
class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
17
|
+
class MonthCalendar extends FocusMixin(ThemableMixin(PolymerElement)) {
|
|
17
18
|
static get template() {
|
|
18
19
|
return html`
|
|
19
20
|
<style>
|
|
@@ -21,15 +22,23 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
21
22
|
display: block;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
#monthGrid {
|
|
26
|
+
display: block;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#monthGrid thead,
|
|
30
|
+
#monthGrid tbody {
|
|
31
|
+
display: block;
|
|
32
|
+
width: 100%;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
[part='weekdays'] {
|
|
26
36
|
display: flex;
|
|
27
|
-
flex-wrap: wrap;
|
|
28
37
|
flex-grow: 1;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
#days-container,
|
|
32
|
-
#weekdays-container {
|
|
40
|
+
#days-container tr,
|
|
41
|
+
#weekdays-container tr {
|
|
33
42
|
display: flex;
|
|
34
43
|
}
|
|
35
44
|
|
|
@@ -40,6 +49,11 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
40
49
|
flex-shrink: 0;
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
[part='date'] {
|
|
53
|
+
outline: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
[part='week-number'][hidden],
|
|
43
57
|
[part='week-numbers'][hidden],
|
|
44
58
|
[part='weekday'][hidden] {
|
|
45
59
|
display: none;
|
|
@@ -47,8 +61,11 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
47
61
|
|
|
48
62
|
[part='weekday'],
|
|
49
63
|
[part='date'] {
|
|
64
|
+
display: block;
|
|
50
65
|
/* Would use calc(100% / 7) but it doesn't work nice on IE */
|
|
51
66
|
width: 14.285714286%;
|
|
67
|
+
padding: 0;
|
|
68
|
+
font-weight: normal;
|
|
52
69
|
}
|
|
53
70
|
|
|
54
71
|
[part='weekday']:empty,
|
|
@@ -58,42 +75,59 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
58
75
|
}
|
|
59
76
|
</style>
|
|
60
77
|
|
|
61
|
-
<div part="month-header"
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
<div part="month-header" id="month-header" aria-hidden="true">[[_getTitle(month, i18n.monthNames)]]</div>
|
|
79
|
+
<table
|
|
80
|
+
id="monthGrid"
|
|
81
|
+
role="grid"
|
|
82
|
+
aria-labelledby="month-header"
|
|
83
|
+
on-touchend="_preventDefault"
|
|
84
|
+
on-touchstart="_onMonthGridTouchStart"
|
|
85
|
+
>
|
|
86
|
+
<thead id="weekdays-container">
|
|
87
|
+
<tr role="row" part="weekdays">
|
|
88
|
+
<th
|
|
89
|
+
part="weekday"
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
hidden$="[[!_showWeekSeparator(showWeekNumbers, i18n.firstDayOfWeek)]]"
|
|
92
|
+
></th>
|
|
66
93
|
<template
|
|
67
94
|
is="dom-repeat"
|
|
68
95
|
items="[[_getWeekDayNames(i18n.weekdays, i18n.weekdaysShort, showWeekNumbers, i18n.firstDayOfWeek)]]"
|
|
69
96
|
>
|
|
70
|
-
<
|
|
71
|
-
</template>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
<div id="days-container">
|
|
75
|
-
<div part="week-numbers" hidden$="[[!_showWeekSeparator(showWeekNumbers, i18n.firstDayOfWeek)]]">
|
|
76
|
-
<template is="dom-repeat" items="[[_getWeekNumbers(_days)]]">
|
|
77
|
-
<div part="week-number" role="heading" aria-label$="[[i18n.week]] [[item]]">[[item]]</div>
|
|
78
|
-
</template>
|
|
79
|
-
</div>
|
|
80
|
-
<div id="days">
|
|
81
|
-
<template is="dom-repeat" items="[[_days]]">
|
|
82
|
-
<!-- prettier-ignore -->
|
|
83
|
-
<div
|
|
84
|
-
part="date"
|
|
85
|
-
today$="[[_isToday(item)]]"
|
|
86
|
-
selected$="[[_dateEquals(item, selectedDate)]]"
|
|
87
|
-
focused$="[[_dateEquals(item, focusedDate)]]"
|
|
88
|
-
date="[[item]]"
|
|
89
|
-
disabled$="[[!_dateAllowed(item, minDate, maxDate)]]"
|
|
90
|
-
role$="[[_getRole(item)]]"
|
|
91
|
-
aria-label$="[[_getAriaLabel(item)]]"
|
|
92
|
-
aria-disabled$="[[_getAriaDisabled(item, minDate, maxDate)]]">[[_getDate(item)]]</div>
|
|
97
|
+
<th role="columnheader" part="weekday" scope="col" abbr$="[[item.weekDay]]">[[item.weekDayShort]]</th>
|
|
93
98
|
</template>
|
|
94
|
-
</
|
|
95
|
-
</
|
|
96
|
-
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody id="days-container">
|
|
102
|
+
<template is="dom-repeat" items="[[_weeks]]" as="week">
|
|
103
|
+
<tr role="row">
|
|
104
|
+
<td
|
|
105
|
+
part="week-number"
|
|
106
|
+
aria-hidden="true"
|
|
107
|
+
hidden$="[[!_showWeekSeparator(showWeekNumbers, i18n.firstDayOfWeek)]]"
|
|
108
|
+
>
|
|
109
|
+
[[__getWeekNumber(week)]]
|
|
110
|
+
</td>
|
|
111
|
+
<template is="dom-repeat" items="[[week]]">
|
|
112
|
+
<td
|
|
113
|
+
role="gridcell"
|
|
114
|
+
part="date"
|
|
115
|
+
date="[[item]]"
|
|
116
|
+
today$="[[_isToday(item)]]"
|
|
117
|
+
focused$="[[__isDayFocused(item, focusedDate)]]"
|
|
118
|
+
tabindex$="[[__getDayTabindex(item, focusedDate)]]"
|
|
119
|
+
selected$="[[__isDaySelected(item, selectedDate)]]"
|
|
120
|
+
disabled$="[[__isDayDisabled(item, minDate, maxDate)]]"
|
|
121
|
+
aria-selected$="[[__getDayAriaSelected(item, selectedDate)]]"
|
|
122
|
+
aria-disabled$="[[__getDayAriaDisabled(item, minDate, maxDate)]]"
|
|
123
|
+
aria-label$="[[__getDayAriaLabel(item)]]"
|
|
124
|
+
>[[_getDate(item)]]</td
|
|
125
|
+
>
|
|
126
|
+
</template>
|
|
127
|
+
</tr>
|
|
128
|
+
</template>
|
|
129
|
+
</tbody>
|
|
130
|
+
</table>
|
|
97
131
|
`;
|
|
98
132
|
}
|
|
99
133
|
|
|
@@ -162,6 +196,11 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
162
196
|
computed: '_getDays(month, i18n.firstDayOfWeek, minDate, maxDate)'
|
|
163
197
|
},
|
|
164
198
|
|
|
199
|
+
_weeks: {
|
|
200
|
+
type: Array,
|
|
201
|
+
computed: '_getWeeks(_days)'
|
|
202
|
+
},
|
|
203
|
+
|
|
165
204
|
disabled: {
|
|
166
205
|
type: Boolean,
|
|
167
206
|
reflectToAttribute: true,
|
|
@@ -171,7 +210,10 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
171
210
|
}
|
|
172
211
|
|
|
173
212
|
static get observers() {
|
|
174
|
-
return [
|
|
213
|
+
return [
|
|
214
|
+
'_showWeekNumbersChanged(showWeekNumbers, i18n.firstDayOfWeek)',
|
|
215
|
+
'__focusedDateChanged(focusedDate, _days)'
|
|
216
|
+
];
|
|
175
217
|
}
|
|
176
218
|
|
|
177
219
|
/** @protected */
|
|
@@ -180,12 +222,10 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
180
222
|
addListener(this.$.monthGrid, 'tap', this._handleTap.bind(this));
|
|
181
223
|
}
|
|
182
224
|
|
|
183
|
-
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
_dateAllowed(date, min, max) {
|
|
188
|
-
return dateAllowed(date, min, max);
|
|
225
|
+
get focusableDateElement() {
|
|
226
|
+
return [...this.shadowRoot.querySelectorAll('[part=date]')].find((datePart) => {
|
|
227
|
+
return dateEquals(datePart.date, this.focusedDate);
|
|
228
|
+
});
|
|
189
229
|
}
|
|
190
230
|
|
|
191
231
|
/* Returns true if all the dates in the month are out of the allowed range */
|
|
@@ -212,7 +252,7 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
212
252
|
return false;
|
|
213
253
|
}
|
|
214
254
|
|
|
215
|
-
return !
|
|
255
|
+
return !dateAllowed(firstDate, minDate, maxDate) && !dateAllowed(lastDate, minDate, maxDate);
|
|
216
256
|
}
|
|
217
257
|
|
|
218
258
|
_getTitle(month, monthNames) {
|
|
@@ -260,6 +300,14 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
260
300
|
return weekDayNames;
|
|
261
301
|
}
|
|
262
302
|
|
|
303
|
+
__focusedDateChanged(focusedDate, days) {
|
|
304
|
+
if (days.some((date) => dateEquals(date, focusedDate))) {
|
|
305
|
+
this.removeAttribute('aria-hidden');
|
|
306
|
+
} else {
|
|
307
|
+
this.setAttribute('aria-hidden', 'true');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
263
311
|
_getDate(date) {
|
|
264
312
|
return date ? date.getDate() : '';
|
|
265
313
|
}
|
|
@@ -278,7 +326,7 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
278
326
|
}
|
|
279
327
|
|
|
280
328
|
_isToday(date) {
|
|
281
|
-
return
|
|
329
|
+
return dateEquals(new Date(), date);
|
|
282
330
|
}
|
|
283
331
|
|
|
284
332
|
_getDays(month, firstDayOfWeek) {
|
|
@@ -308,25 +356,14 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
308
356
|
return days;
|
|
309
357
|
}
|
|
310
358
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return !acc && d ? d : acc;
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return getISOWeekNumber(date);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
_getWeekNumbers(dates) {
|
|
327
|
-
return dates
|
|
328
|
-
.map((date) => this._getWeekNumber(date, dates))
|
|
329
|
-
.filter((week, index, arr) => arr.indexOf(week) === index);
|
|
359
|
+
_getWeeks(days) {
|
|
360
|
+
return days.reduce((acc, day, i) => {
|
|
361
|
+
if (i % 7 === 0) {
|
|
362
|
+
acc.push([]);
|
|
363
|
+
}
|
|
364
|
+
acc[acc.length - 1].push(day);
|
|
365
|
+
return acc;
|
|
366
|
+
}, []);
|
|
330
367
|
}
|
|
331
368
|
|
|
332
369
|
_handleTap(e) {
|
|
@@ -340,11 +377,43 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
340
377
|
e.preventDefault();
|
|
341
378
|
}
|
|
342
379
|
|
|
343
|
-
|
|
344
|
-
|
|
380
|
+
__getWeekNumber(days) {
|
|
381
|
+
const date = days.reduce((acc, d) => {
|
|
382
|
+
return !acc && d ? d : acc;
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
return getISOWeekNumber(date);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
__isDayFocused(date, focusedDate) {
|
|
389
|
+
return dateEquals(date, focusedDate);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
__isDaySelected(date, selectedDate) {
|
|
393
|
+
return dateEquals(date, selectedDate);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
__getDayAriaSelected(date, selectedDate) {
|
|
397
|
+
if (this.__isDaySelected(date, selectedDate)) {
|
|
398
|
+
return 'true';
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
__isDayDisabled(date, minDate, maxDate) {
|
|
403
|
+
return !dateAllowed(date, minDate, maxDate);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
__getDayAriaDisabled(date, min, max) {
|
|
407
|
+
if (date === undefined || min === undefined || max === undefined) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (this.__isDayDisabled(date, min, max)) {
|
|
412
|
+
return 'true';
|
|
413
|
+
}
|
|
345
414
|
}
|
|
346
415
|
|
|
347
|
-
|
|
416
|
+
__getDayAriaLabel(date) {
|
|
348
417
|
if (!date) {
|
|
349
418
|
return '';
|
|
350
419
|
}
|
|
@@ -365,11 +434,18 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
|
|
|
365
434
|
return ariaLabel;
|
|
366
435
|
}
|
|
367
436
|
|
|
368
|
-
|
|
369
|
-
if (date
|
|
370
|
-
return;
|
|
437
|
+
__getDayTabindex(date, focusedDate) {
|
|
438
|
+
if (this.__isDayFocused(date, focusedDate)) {
|
|
439
|
+
return '0';
|
|
371
440
|
}
|
|
372
|
-
|
|
441
|
+
|
|
442
|
+
return '-1';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
__getWeekNumbers(dates) {
|
|
446
|
+
return dates
|
|
447
|
+
.map((date) => this.__getWeekNumber(date, dates))
|
|
448
|
+
.filter((week, index, arr) => arr.indexOf(week) === index);
|
|
373
449
|
}
|
|
374
450
|
}
|
|
375
451
|
|