@vaadin/date-picker 23.0.0-beta1 → 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
|
},
|
|
@@ -35,21 +35,21 @@
|
|
|
35
35
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
36
36
|
"@polymer/iron-media-query": "^3.0.0",
|
|
37
37
|
"@polymer/polymer": "^3.2.0",
|
|
38
|
-
"@vaadin/button": "23.0.0-
|
|
39
|
-
"@vaadin/component-base": "23.0.0-
|
|
40
|
-
"@vaadin/field-base": "23.0.0-
|
|
41
|
-
"@vaadin/input-container": "23.0.0-
|
|
42
|
-
"@vaadin/vaadin-lumo-styles": "23.0.0-
|
|
43
|
-
"@vaadin/vaadin-material-styles": "23.0.0-
|
|
44
|
-
"@vaadin/vaadin-overlay": "23.0.0-
|
|
45
|
-
"@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"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@esm-bundle/chai": "^4.3.4",
|
|
49
|
-
"@vaadin/dialog": "23.0.0-
|
|
50
|
-
"@vaadin/polymer-legacy-adapter": "23.0.0-
|
|
49
|
+
"@vaadin/dialog": "23.0.0-beta2",
|
|
50
|
+
"@vaadin/polymer-legacy-adapter": "23.0.0-beta2",
|
|
51
51
|
"@vaadin/testing-helpers": "^0.3.2",
|
|
52
52
|
"sinon": "^9.2.0"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "a276f7a0fd00e5459b87267468e0dd0d4fb6f7f3"
|
|
55
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
|
|
|
@@ -584,6 +578,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
584
578
|
if (input) {
|
|
585
579
|
input.autocomplete = 'off';
|
|
586
580
|
input.setAttribute('role', 'combobox');
|
|
581
|
+
input.setAttribute('aria-haspopup', 'dialog');
|
|
587
582
|
input.setAttribute('aria-expanded', !!this.opened);
|
|
588
583
|
this._applyInputValue(this._selectedDate);
|
|
589
584
|
}
|
|
@@ -710,7 +705,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
710
705
|
}
|
|
711
706
|
|
|
712
707
|
if (this._focusOverlayOnOpen) {
|
|
713
|
-
this._overlayContent.
|
|
708
|
+
this._overlayContent.focusDateElement();
|
|
714
709
|
this._focusOverlayOnOpen = false;
|
|
715
710
|
} else {
|
|
716
711
|
this._focus();
|
|
@@ -719,8 +714,6 @@ export const DatePickerMixin = (subclass) =>
|
|
|
719
714
|
if (this._noInput && this.focusElement) {
|
|
720
715
|
this.focusElement.blur();
|
|
721
716
|
}
|
|
722
|
-
|
|
723
|
-
this._ignoreAnnounce = false;
|
|
724
717
|
}
|
|
725
718
|
|
|
726
719
|
// A hack needed for iOS to prevent dropdown from being clipped in an
|
|
@@ -765,8 +758,6 @@ export const DatePickerMixin = (subclass) =>
|
|
|
765
758
|
|
|
766
759
|
/** @protected */
|
|
767
760
|
_onOverlayClosed() {
|
|
768
|
-
this._ignoreAnnounce = true;
|
|
769
|
-
|
|
770
761
|
window.removeEventListener('scroll', this._boundOnScroll, true);
|
|
771
762
|
|
|
772
763
|
if (this._touchPrevented) {
|
|
@@ -891,15 +882,14 @@ export const DatePickerMixin = (subclass) =>
|
|
|
891
882
|
case 'ArrowUp':
|
|
892
883
|
// prevent scrolling the page with arrows
|
|
893
884
|
e.preventDefault();
|
|
894
|
-
|
|
895
885
|
if (this.opened) {
|
|
896
|
-
|
|
897
|
-
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();
|
|
898
889
|
} else {
|
|
899
890
|
this._focusOverlayOnOpen = true;
|
|
900
891
|
this.open();
|
|
901
892
|
}
|
|
902
|
-
|
|
903
893
|
break;
|
|
904
894
|
case 'Enter': {
|
|
905
895
|
const parsedDate = this._getParsedDate();
|
|
@@ -945,8 +935,7 @@ export const DatePickerMixin = (subclass) =>
|
|
|
945
935
|
if (e.shiftKey) {
|
|
946
936
|
this._overlayContent.focusCancel();
|
|
947
937
|
} else {
|
|
948
|
-
this._overlayContent.
|
|
949
|
-
this._overlayContent.revealDate(this._focusedDate);
|
|
938
|
+
this._overlayContent.focusDate(this._focusedDate);
|
|
950
939
|
}
|
|
951
940
|
}
|
|
952
941
|
break;
|
|
@@ -994,13 +983,6 @@ export const DatePickerMixin = (subclass) =>
|
|
|
994
983
|
}
|
|
995
984
|
}
|
|
996
985
|
|
|
997
|
-
/** @private */
|
|
998
|
-
_announceFocusedDate(_focusedDate, opened, _ignoreAnnounce) {
|
|
999
|
-
if (opened && !_ignoreAnnounce) {
|
|
1000
|
-
this._overlayContent.announceFocusedDate();
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
986
|
/** @private */
|
|
1005
987
|
get _overlayContent() {
|
|
1006
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>
|
|
@@ -209,7 +199,6 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
209
199
|
<template>
|
|
210
200
|
<div
|
|
211
201
|
part="year-number"
|
|
212
|
-
role="button"
|
|
213
202
|
current$="[[_isCurrentYear(index)]]"
|
|
214
203
|
selected$="[[_isSelectedYear(index, selectedDate)]]"
|
|
215
204
|
>
|
|
@@ -226,10 +215,13 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
226
215
|
part="today-button"
|
|
227
216
|
theme="tertiary"
|
|
228
217
|
disabled="[[!_isTodayAllowed(minDate, maxDate)]]"
|
|
218
|
+
on-keydown="__onTodayButtonKeyDown"
|
|
229
219
|
>
|
|
230
220
|
[[i18n.today]]
|
|
231
221
|
</vaadin-button>
|
|
232
|
-
<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>
|
|
233
225
|
</div>
|
|
234
226
|
<iron-media-query query="(min-width: 375px)" query-matches="{{_desktopMode}}"></iron-media-query>
|
|
235
227
|
`;
|
|
@@ -306,8 +298,6 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
306
298
|
*/
|
|
307
299
|
maxDate: Date,
|
|
308
300
|
|
|
309
|
-
_focused: Boolean,
|
|
310
|
-
|
|
311
301
|
/**
|
|
312
302
|
* Input label
|
|
313
303
|
*/
|
|
@@ -319,13 +309,15 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
319
309
|
return this.getAttribute('dir') === 'rtl';
|
|
320
310
|
}
|
|
321
311
|
|
|
312
|
+
get focusableDateElement() {
|
|
313
|
+
return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')]
|
|
314
|
+
.map((calendar) => calendar.focusableDateElement)
|
|
315
|
+
.find(Boolean);
|
|
316
|
+
}
|
|
317
|
+
|
|
322
318
|
ready() {
|
|
323
319
|
super.ready();
|
|
324
|
-
this.setAttribute('tabindex', 0);
|
|
325
|
-
this.addEventListener('keydown', this._onKeydown.bind(this));
|
|
326
320
|
addListener(this, 'tap', this._stopPropagation);
|
|
327
|
-
this.addEventListener('focus', this._onOverlayFocus.bind(this));
|
|
328
|
-
this.addEventListener('blur', this._onOverlayBlur.bind(this));
|
|
329
321
|
addListener(this.$.scrollers, 'track', this._track.bind(this));
|
|
330
322
|
addListener(this.shadowRoot.querySelector('[part="clear-button"]'), 'tap', this._clear.bind(this));
|
|
331
323
|
addListener(this.shadowRoot.querySelector('[part="today-button"]'), 'tap', this._onTodayTap.bind(this));
|
|
@@ -353,25 +345,6 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
353
345
|
setTouchAction(this.$.scrollers, 'pan-y');
|
|
354
346
|
}
|
|
355
347
|
|
|
356
|
-
announceFocusedDate() {
|
|
357
|
-
const focusedDate = this._currentlyFocusedDate();
|
|
358
|
-
let messages = [];
|
|
359
|
-
if (dateEquals(focusedDate, new Date())) {
|
|
360
|
-
messages.push(this.i18n.today);
|
|
361
|
-
}
|
|
362
|
-
messages = messages.concat([
|
|
363
|
-
this.i18n.weekdays[focusedDate.getDay()],
|
|
364
|
-
focusedDate.getDate(),
|
|
365
|
-
this.i18n.monthNames[focusedDate.getMonth()],
|
|
366
|
-
focusedDate.getFullYear()
|
|
367
|
-
]);
|
|
368
|
-
if (this.showWeekNumbers && this.i18n.firstDayOfWeek === 1) {
|
|
369
|
-
messages.push(this.i18n.week);
|
|
370
|
-
messages.push(getISOWeekNumber(focusedDate));
|
|
371
|
-
}
|
|
372
|
-
announce(messages.join(' '));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
348
|
/**
|
|
376
349
|
* Focuses the cancel button
|
|
377
350
|
*/
|
|
@@ -423,14 +396,6 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
423
396
|
}
|
|
424
397
|
}
|
|
425
398
|
|
|
426
|
-
_onOverlayFocus() {
|
|
427
|
-
this._focused = true;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
_onOverlayBlur() {
|
|
431
|
-
this._focused = false;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
399
|
_initialPositionChanged(initialPosition) {
|
|
435
400
|
this.scrollToDate(initialPosition);
|
|
436
401
|
}
|
|
@@ -522,6 +487,7 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
522
487
|
this.$.monthScroller.position = targetPosition;
|
|
523
488
|
this._targetPosition = undefined;
|
|
524
489
|
this._repositionYearScroller();
|
|
490
|
+
this.__tryFocusDate();
|
|
525
491
|
return;
|
|
526
492
|
}
|
|
527
493
|
|
|
@@ -563,6 +529,7 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
563
529
|
|
|
564
530
|
this.$.monthScroller.position = this._targetPosition;
|
|
565
531
|
this._targetPosition = undefined;
|
|
532
|
+
this.__tryFocusDate();
|
|
566
533
|
}
|
|
567
534
|
|
|
568
535
|
setTimeout(this._repositionYearScroller.bind(this), 1);
|
|
@@ -703,150 +670,168 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
703
670
|
e.preventDefault();
|
|
704
671
|
}
|
|
705
672
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const isCancel = e.composedPath().indexOf(this.$.cancelButton) >= 0;
|
|
713
|
-
const isScroller = !isToday && !isCancel;
|
|
714
|
-
|
|
715
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
|
716
|
-
const navigationKeys = [
|
|
717
|
-
' ',
|
|
718
|
-
'ArrowDown',
|
|
719
|
-
'ArrowUp',
|
|
720
|
-
'ArrowRight',
|
|
721
|
-
'ArrowLeft',
|
|
722
|
-
'Enter',
|
|
723
|
-
'End',
|
|
724
|
-
'Escape',
|
|
725
|
-
'Home',
|
|
726
|
-
'PageUp',
|
|
727
|
-
'PageDown',
|
|
728
|
-
'Tab'
|
|
729
|
-
];
|
|
730
|
-
|
|
731
|
-
const eventKey = e.key;
|
|
732
|
-
if (eventKey === 'Tab') {
|
|
733
|
-
// We handle tabs here and don't want to bubble up.
|
|
734
|
-
e.stopPropagation();
|
|
735
|
-
|
|
736
|
-
const isFullscreen = this.hasAttribute('fullscreen');
|
|
737
|
-
const isShift = e.shiftKey;
|
|
738
|
-
|
|
739
|
-
if (isFullscreen) {
|
|
740
|
-
e.preventDefault();
|
|
741
|
-
} else if ((isShift && isScroller) || (!isShift && isCancel)) {
|
|
742
|
-
// Return focus back to the input field
|
|
743
|
-
e.preventDefault();
|
|
744
|
-
this.dispatchEvent(new CustomEvent('focus-input', { bubbles: true, composed: true }));
|
|
745
|
-
} else if (isShift && isToday) {
|
|
746
|
-
// Browser returns focus back to the scrollable area. We need to set
|
|
747
|
-
// the focused flag, and move the scroll to focused date.
|
|
748
|
-
this._focused = true;
|
|
749
|
-
setTimeout(() => this.revealDate(this.focusedDate), 1);
|
|
750
|
-
} else {
|
|
751
|
-
// Browser moves the focus out of the scroller, hence focused flag must
|
|
752
|
-
// set to false.
|
|
753
|
-
this._focused = false;
|
|
754
|
-
}
|
|
755
|
-
} else if (navigationKeys.includes(eventKey)) {
|
|
756
|
-
e.preventDefault();
|
|
757
|
-
e.stopPropagation();
|
|
758
|
-
switch (eventKey) {
|
|
759
|
-
case 'ArrowDown':
|
|
760
|
-
this._moveFocusByDays(7);
|
|
761
|
-
this.focus();
|
|
762
|
-
break;
|
|
763
|
-
case 'ArrowUp':
|
|
764
|
-
this._moveFocusByDays(-7);
|
|
765
|
-
this.focus();
|
|
766
|
-
break;
|
|
767
|
-
case 'ArrowRight':
|
|
768
|
-
if (isScroller) {
|
|
769
|
-
this._moveFocusByDays(this.__isRTL ? -1 : 1);
|
|
770
|
-
}
|
|
771
|
-
break;
|
|
772
|
-
case 'ArrowLeft':
|
|
773
|
-
if (isScroller) {
|
|
774
|
-
this._moveFocusByDays(this.__isRTL ? 1 : -1);
|
|
775
|
-
}
|
|
776
|
-
break;
|
|
777
|
-
case 'Enter':
|
|
778
|
-
if (isScroller || isCancel) {
|
|
779
|
-
this._close();
|
|
780
|
-
} else if (isToday) {
|
|
781
|
-
this._onTodayTap();
|
|
782
|
-
}
|
|
783
|
-
break;
|
|
784
|
-
case ' ':
|
|
785
|
-
if (isCancel) {
|
|
786
|
-
this._close();
|
|
787
|
-
} else if (isToday) {
|
|
788
|
-
this._onTodayTap();
|
|
789
|
-
} else {
|
|
790
|
-
var focusedDate = this.focusedDate;
|
|
791
|
-
if (dateEquals(focusedDate, this.selectedDate)) {
|
|
792
|
-
this.selectedDate = '';
|
|
793
|
-
this.focusedDate = focusedDate;
|
|
794
|
-
} else {
|
|
795
|
-
this.selectedDate = focusedDate;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
break;
|
|
799
|
-
case 'Home':
|
|
800
|
-
this._moveFocusInsideMonth(focus, 'minDate');
|
|
801
|
-
break;
|
|
802
|
-
case 'End':
|
|
803
|
-
this._moveFocusInsideMonth(focus, 'maxDate');
|
|
804
|
-
break;
|
|
805
|
-
case 'PageDown':
|
|
806
|
-
this._moveFocusByMonths(e.shiftKey ? 12 : 1);
|
|
807
|
-
break;
|
|
808
|
-
case 'PageUp':
|
|
809
|
-
this._moveFocusByMonths(e.shiftKey ? -12 : -1);
|
|
810
|
-
break;
|
|
811
|
-
case 'Escape':
|
|
812
|
-
this._cancel();
|
|
813
|
-
break;
|
|
814
|
-
default:
|
|
815
|
-
break;
|
|
816
|
-
}
|
|
673
|
+
__toggleDate(date) {
|
|
674
|
+
if (dateEquals(date, this.selectedDate)) {
|
|
675
|
+
this.selectedDate = '';
|
|
676
|
+
this.focusedDate = date;
|
|
677
|
+
} else {
|
|
678
|
+
this.selectedDate = date;
|
|
817
679
|
}
|
|
818
680
|
}
|
|
819
681
|
|
|
820
|
-
|
|
821
|
-
|
|
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
|
+
}
|
|
822
793
|
}
|
|
823
794
|
|
|
824
|
-
|
|
795
|
+
async focusDate(date, keepMonth) {
|
|
796
|
+
const dateToFocus = date || this.selectedDate || this.initialPosition || new Date();
|
|
825
797
|
this.focusedDate = dateToFocus;
|
|
826
|
-
|
|
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();
|
|
827
812
|
}
|
|
828
813
|
|
|
829
814
|
_focusClosestDate(focus) {
|
|
830
|
-
this.
|
|
815
|
+
this.focusDate(getClosestDate(focus, [this.minDate, this.maxDate]));
|
|
831
816
|
}
|
|
832
817
|
|
|
833
818
|
_moveFocusByDays(days) {
|
|
834
|
-
var focus = this.
|
|
819
|
+
var focus = this.focusedDate;
|
|
835
820
|
var dateToFocus = new Date(0, 0);
|
|
836
821
|
dateToFocus.setFullYear(focus.getFullYear());
|
|
837
822
|
dateToFocus.setMonth(focus.getMonth());
|
|
838
823
|
dateToFocus.setDate(focus.getDate() + days);
|
|
839
824
|
|
|
840
825
|
if (this._dateAllowed(dateToFocus, this.minDate, this.maxDate)) {
|
|
841
|
-
this.
|
|
826
|
+
this.focusDate(dateToFocus);
|
|
842
827
|
} else if (this._dateAllowed(focus, this.minDate, this.maxDate)) {
|
|
843
828
|
// Move to min or max date
|
|
844
829
|
if (days > 0) {
|
|
845
830
|
// down or right
|
|
846
|
-
this.
|
|
831
|
+
this.focusDate(this.maxDate);
|
|
847
832
|
} else {
|
|
848
833
|
// up or left
|
|
849
|
-
this.
|
|
834
|
+
this.focusDate(this.minDate);
|
|
850
835
|
}
|
|
851
836
|
} else {
|
|
852
837
|
// Move to closest allowed date
|
|
@@ -855,7 +840,7 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
855
840
|
}
|
|
856
841
|
|
|
857
842
|
_moveFocusByMonths(months) {
|
|
858
|
-
var focus = this.
|
|
843
|
+
var focus = this.focusedDate;
|
|
859
844
|
var dateToFocus = new Date(0, 0);
|
|
860
845
|
dateToFocus.setFullYear(focus.getFullYear());
|
|
861
846
|
dateToFocus.setMonth(focus.getMonth() + months);
|
|
@@ -868,15 +853,15 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
868
853
|
}
|
|
869
854
|
|
|
870
855
|
if (this._dateAllowed(dateToFocus, this.minDate, this.maxDate)) {
|
|
871
|
-
this.
|
|
856
|
+
this.focusDate(dateToFocus, true);
|
|
872
857
|
} else if (this._dateAllowed(focus, this.minDate, this.maxDate)) {
|
|
873
858
|
// Move to min or max date
|
|
874
859
|
if (months > 0) {
|
|
875
860
|
// pagedown
|
|
876
|
-
this.
|
|
861
|
+
this.focusDate(this.maxDate);
|
|
877
862
|
} else {
|
|
878
863
|
// pageup
|
|
879
|
-
this.
|
|
864
|
+
this.focusDate(this.minDate);
|
|
880
865
|
}
|
|
881
866
|
} else {
|
|
882
867
|
// Move to closest allowed date
|
|
@@ -897,10 +882,10 @@ class DatePickerOverlayContent extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
897
882
|
}
|
|
898
883
|
|
|
899
884
|
if (this._dateAllowed(dateToFocus, this.minDate, this.maxDate)) {
|
|
900
|
-
this.
|
|
885
|
+
this.focusDate(dateToFocus);
|
|
901
886
|
} else if (this._dateAllowed(focusedDate, this.minDate, this.maxDate)) {
|
|
902
887
|
// Move to minDate or maxDate
|
|
903
|
-
this.
|
|
888
|
+
this.focusDate(this[property]);
|
|
904
889
|
} else {
|
|
905
890
|
// Move to closest allowed date
|
|
906
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
|
|