@vaadin/date-picker 24.2.0-dev.f254716fe → 24.3.0-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/date-picker",
3
- "version": "24.2.0-dev.f254716fe",
3
+ "version": "24.3.0-alpha1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,6 +21,10 @@
21
21
  "type": "module",
22
22
  "files": [
23
23
  "src",
24
+ "!src/vaadin-lit-date-picker-overlay-content.js",
25
+ "!src/vaadin-lit-date-picker-overlay.js",
26
+ "!src/vaadin-lit-month-calendar.js",
27
+ "!src/vaadin-lit-date-picker.js",
24
28
  "theme",
25
29
  "vaadin-*.d.ts",
26
30
  "vaadin-*.js",
@@ -36,24 +40,24 @@
36
40
  "dependencies": {
37
41
  "@open-wc/dedupe-mixin": "^1.3.0",
38
42
  "@polymer/polymer": "^3.2.0",
39
- "@vaadin/a11y-base": "24.2.0-dev.f254716fe",
40
- "@vaadin/button": "24.2.0-dev.f254716fe",
41
- "@vaadin/component-base": "24.2.0-dev.f254716fe",
42
- "@vaadin/field-base": "24.2.0-dev.f254716fe",
43
- "@vaadin/input-container": "24.2.0-dev.f254716fe",
44
- "@vaadin/overlay": "24.2.0-dev.f254716fe",
45
- "@vaadin/vaadin-lumo-styles": "24.2.0-dev.f254716fe",
46
- "@vaadin/vaadin-material-styles": "24.2.0-dev.f254716fe",
47
- "@vaadin/vaadin-themable-mixin": "24.2.0-dev.f254716fe"
43
+ "@vaadin/a11y-base": "24.3.0-alpha1",
44
+ "@vaadin/button": "24.3.0-alpha1",
45
+ "@vaadin/component-base": "24.3.0-alpha1",
46
+ "@vaadin/field-base": "24.3.0-alpha1",
47
+ "@vaadin/input-container": "24.3.0-alpha1",
48
+ "@vaadin/overlay": "24.3.0-alpha1",
49
+ "@vaadin/vaadin-lumo-styles": "24.3.0-alpha1",
50
+ "@vaadin/vaadin-material-styles": "24.3.0-alpha1",
51
+ "@vaadin/vaadin-themable-mixin": "24.3.0-alpha1"
48
52
  },
49
53
  "devDependencies": {
50
54
  "@esm-bundle/chai": "^4.3.4",
51
- "@vaadin/testing-helpers": "^0.4.3",
55
+ "@vaadin/testing-helpers": "^0.5.0",
52
56
  "sinon": "^13.0.2"
53
57
  },
54
58
  "web-types": [
55
59
  "web-types.json",
56
60
  "web-types.lit.json"
57
61
  ],
58
- "gitHead": "da54950b9f8c14c6451ede0d426e16a489c7fb9b"
62
+ "gitHead": "9ca6f3ca220a777e8eea181a1f5717e39a732240"
59
63
  }
@@ -25,6 +25,11 @@ export type DatePickerLightOpenedChangedEvent = CustomEvent<{ value: boolean }>;
25
25
  */
26
26
  export type DatePickerLightInvalidChangedEvent = CustomEvent<{ value: boolean }>;
27
27
 
28
+ /**
29
+ * Fired when the `dirty` property changes.
30
+ */
31
+ export type DatePickerLightDirtyChangedEvent = CustomEvent<{ value: boolean }>;
32
+
28
33
  /**
29
34
  * Fired when the `value` property changes.
30
35
  */
@@ -40,6 +45,8 @@ export interface DatePickerLightCustomEventMap {
40
45
 
41
46
  'invalid-changed': DatePickerLightInvalidChangedEvent;
42
47
 
48
+ 'dirty-changed': DatePickerLightDirtyChangedEvent;
49
+
43
50
  'value-changed': DatePickerLightValueChangedEvent;
44
51
 
45
52
  validated: DatePickerLightValidatedEvent;
@@ -80,6 +87,7 @@ export interface DatePickerLightEventMap extends HTMLElementEventMap, DatePicker
80
87
  *
81
88
  * @fires {Event} change - Fired when the user commits a value change.
82
89
  * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
90
+ * @fires {CustomEvent} dirty-changed - Fired when the `dirty` property changes.
83
91
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
84
92
  * @fires {CustomEvent} validated - Fired whenever the field is validated.
85
93
  */
@@ -3,10 +3,11 @@
3
3
  * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import './vaadin-date-picker-overlay.js';
7
6
  import './vaadin-date-picker-overlay-content.js';
7
+ import './vaadin-date-picker-overlay.js';
8
8
  import { dashToCamelCase } from '@polymer/polymer/lib/utils/case-map.js';
9
9
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
10
+ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
10
11
  import { ValidateMixin } from '@vaadin/field-base/src/validate-mixin.js';
11
12
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
12
13
  import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
@@ -45,6 +46,7 @@ import { DatePickerMixin } from './vaadin-date-picker-mixin.js';
45
46
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
46
47
  * @fires {CustomEvent} validated - Fired whenever the field is validated.
47
48
  *
49
+ * @customElement
48
50
  * @extends HTMLElement
49
51
  * @mixes ThemableMixin
50
52
  * @mixes DatePickerMixin
@@ -126,6 +128,6 @@ class DatePickerLight extends ThemableMixin(DatePickerMixin(ValidateMixin(Polyme
126
128
  }
127
129
  }
128
130
 
129
- customElements.define(DatePickerLight.is, DatePickerLight);
131
+ defineCustomElement(DatePickerLight);
130
132
 
131
133
  export { DatePickerLight };
@@ -42,14 +42,18 @@ export const DatePickerMixin = (subclass) =>
42
42
  * @protected
43
43
  */
44
44
  _selectedDate: {
45
- type: Date,
45
+ type: Object,
46
+ sync: true,
46
47
  },
47
48
 
48
49
  /**
49
50
  * @type {Date | undefined}
50
51
  * @protected
51
52
  */
52
- _focusedDate: Date,
53
+ _focusedDate: {
54
+ type: Object,
55
+ sync: true,
56
+ },
53
57
 
54
58
  /**
55
59
  * Selected date.
@@ -64,6 +68,7 @@ export const DatePickerMixin = (subclass) =>
64
68
  type: String,
65
69
  notify: true,
66
70
  value: '',
71
+ sync: true,
67
72
  },
68
73
 
69
74
  /**
@@ -82,6 +87,7 @@ export const DatePickerMixin = (subclass) =>
82
87
  reflectToAttribute: true,
83
88
  notify: true,
84
89
  observer: '_openedChanged',
90
+ sync: true,
85
91
  },
86
92
 
87
93
  /**
@@ -99,6 +105,7 @@ export const DatePickerMixin = (subclass) =>
99
105
  showWeekNumbers: {
100
106
  type: Boolean,
101
107
  value: false,
108
+ sync: true,
102
109
  },
103
110
 
104
111
  /**
@@ -108,6 +115,7 @@ export const DatePickerMixin = (subclass) =>
108
115
  _fullscreen: {
109
116
  type: Boolean,
110
117
  value: false,
118
+ sync: true,
111
119
  },
112
120
 
113
121
  /**
@@ -206,6 +214,7 @@ export const DatePickerMixin = (subclass) =>
206
214
  */
207
215
  i18n: {
208
216
  type: Object,
217
+ sync: true,
209
218
  value: () => {
210
219
  return {
211
220
  monthNames: [
@@ -276,6 +285,7 @@ export const DatePickerMixin = (subclass) =>
276
285
  */
277
286
  min: {
278
287
  type: String,
288
+ sync: true,
279
289
  },
280
290
 
281
291
  /**
@@ -289,6 +299,7 @@ export const DatePickerMixin = (subclass) =>
289
299
  */
290
300
  max: {
291
301
  type: String,
302
+ sync: true,
292
303
  },
293
304
 
294
305
  /**
@@ -299,6 +310,7 @@ export const DatePickerMixin = (subclass) =>
299
310
  _minDate: {
300
311
  type: Date,
301
312
  computed: '__computeMinOrMaxDate(min)',
313
+ sync: true,
302
314
  },
303
315
 
304
316
  /**
@@ -309,6 +321,7 @@ export const DatePickerMixin = (subclass) =>
309
321
  _maxDate: {
310
322
  type: Date,
311
323
  computed: '__computeMinOrMaxDate(max)',
324
+ sync: true,
312
325
  },
313
326
 
314
327
  /** @private */
@@ -327,7 +340,10 @@ export const DatePickerMixin = (subclass) =>
327
340
  _focusOverlayOnOpen: Boolean,
328
341
 
329
342
  /** @private */
330
- _overlayContent: Object,
343
+ _overlayContent: {
344
+ type: Object,
345
+ sync: true,
346
+ },
331
347
 
332
348
  /**
333
349
  * In date-picker, unlike other components extending `InputMixin`,
@@ -347,8 +363,8 @@ export const DatePickerMixin = (subclass) =>
347
363
 
348
364
  static get observers() {
349
365
  return [
350
- '_selectedDateChanged(_selectedDate, i18n.formatDate)',
351
- '_focusedDateChanged(_focusedDate, i18n.formatDate)',
366
+ '_selectedDateChanged(_selectedDate, i18n)',
367
+ '_focusedDateChanged(_focusedDate, i18n)',
352
368
  '__updateOverlayContent(_overlayContent, i18n, label, _minDate, _maxDate, _focusedDate, _selectedDate, showWeekNumbers)',
353
369
  '__updateOverlayContentTheme(_overlayContent, _theme)',
354
370
  '__updateOverlayContentFullScreen(_overlayContent, _fullscreen)',
@@ -431,19 +447,13 @@ export const DatePickerMixin = (subclass) =>
431
447
  super._onBlur(event);
432
448
 
433
449
  if (!this.opened) {
434
- if (this.autoOpenDisabled) {
435
- this._selectParsedOrFocusedDate();
436
- }
450
+ this._selectParsedOrFocusedDate();
437
451
 
438
452
  // Do not validate when focusout is caused by document
439
453
  // losing focus, which happens on browser tab switch.
440
454
  if (document.hasFocus()) {
441
455
  this.validate();
442
456
  }
443
-
444
- if (this._inputElementValue === '' && this.value !== '') {
445
- this.value = '';
446
- }
447
457
  }
448
458
  }
449
459
 
@@ -477,25 +487,6 @@ export const DatePickerMixin = (subclass) =>
477
487
  this.opened = false;
478
488
  }
479
489
 
480
- /**
481
- * Override Polymer lifecycle callback to dispatch `change` event if needed.
482
- * This is necessary to ensure `change` is fired after `value-changed`.
483
- *
484
- * @param {!Object} currentProps Current accessor values
485
- * @param {?Object} changedProps Properties changed since the last call
486
- * @param {?Object} oldProps Previous values for each changed property
487
- * @protected
488
- * @override
489
- */
490
- _propertiesChanged(currentProps, changedProps, oldProps) {
491
- super._propertiesChanged(currentProps, changedProps, oldProps);
492
-
493
- if ('value' in changedProps && this.__dispatchChange) {
494
- this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
495
- this.__dispatchChange = false;
496
- }
497
- }
498
-
499
490
  /**
500
491
  * Opens the dropdown.
501
492
  */
@@ -561,6 +552,34 @@ export const DatePickerMixin = (subclass) =>
561
552
  });
562
553
  }
563
554
 
555
+ /**
556
+ * @param {string} dateString
557
+ * @private
558
+ */
559
+ __parseDate(dateString) {
560
+ if (!this.i18n.parseDate) {
561
+ return;
562
+ }
563
+
564
+ let dateObject = this.i18n.parseDate(dateString);
565
+ if (dateObject) {
566
+ dateObject = parseDate(`${dateObject.year}-${dateObject.month + 1}-${dateObject.day}`);
567
+ }
568
+ if (dateObject && !isNaN(dateObject.getTime())) {
569
+ return dateObject;
570
+ }
571
+ }
572
+
573
+ /**
574
+ * @param {Date} dateObject
575
+ * @private
576
+ */
577
+ __formatDate(dateObject) {
578
+ if (this.i18n.formatDate) {
579
+ return this.i18n.formatDate(extractDateParts(dateObject));
580
+ }
581
+ }
582
+
564
583
  /**
565
584
  * Returns true if the current input value satisfies all constraints (if any)
566
585
  *
@@ -570,9 +589,7 @@ export const DatePickerMixin = (subclass) =>
570
589
  */
571
590
  checkValidity() {
572
591
  const inputValue = this._inputElementValue;
573
- const inputValid =
574
- !inputValue ||
575
- (!!this._selectedDate && inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
592
+ const inputValid = !inputValue || (!!this._selectedDate && inputValue === this.__formatDate(this._selectedDate));
576
593
  const minMaxValid = !this._selectedDate || dateAllowed(this._selectedDate, this._minDate, this._maxDate);
577
594
 
578
595
  let inputValidity = true;
@@ -633,6 +650,12 @@ export const DatePickerMixin = (subclass) =>
633
650
  this._shouldKeepFocusRing = focused && this._keyboardActive;
634
651
  }
635
652
 
653
+ /** @private */
654
+ __dispatchChange() {
655
+ this.validate();
656
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
657
+ }
658
+
636
659
  /**
637
660
  * Select date on user interaction and set the flag
638
661
  * to fire change event if necessary.
@@ -641,14 +664,14 @@ export const DatePickerMixin = (subclass) =>
641
664
  * @protected
642
665
  */
643
666
  _selectDate(dateToSelect) {
644
- const value = this._formatISO(dateToSelect);
645
-
646
- // Only set flag if the value will change.
647
- if (this.value !== value) {
648
- this.__dispatchChange = true;
649
- }
667
+ const prevValue = this.value;
650
668
 
651
669
  this._selectedDate = dateToSelect;
670
+
671
+ if (prevValue !== this.value) {
672
+ this.dirty = true;
673
+ this.__dispatchChange();
674
+ }
652
675
  }
653
676
 
654
677
  /** @private */
@@ -724,28 +747,24 @@ export const DatePickerMixin = (subclass) =>
724
747
  }
725
748
 
726
749
  /** @private */
727
- _selectedDateChanged(selectedDate, formatDate) {
728
- if (selectedDate === undefined || formatDate === undefined) {
750
+ _selectedDateChanged(selectedDate, i18n) {
751
+ if (selectedDate === undefined || i18n === undefined) {
729
752
  return;
730
753
  }
731
- const value = this._formatISO(selectedDate);
732
754
 
733
755
  if (!this.__keepInputValue) {
734
756
  this._applyInputValue(selectedDate);
735
757
  }
736
758
 
737
- if (value !== this.value) {
738
- this.validate();
739
- this.value = value;
740
- }
759
+ this.value = this._formatISO(selectedDate);
741
760
  this._ignoreFocusedDateChange = true;
742
761
  this._focusedDate = selectedDate;
743
762
  this._ignoreFocusedDateChange = false;
744
763
  }
745
764
 
746
765
  /** @private */
747
- _focusedDateChanged(focusedDate, formatDate) {
748
- if (focusedDate === undefined || formatDate === undefined) {
766
+ _focusedDateChanged(focusedDate, i18n) {
767
+ if (focusedDate === undefined || i18n === undefined) {
749
768
  return;
750
769
  }
751
770
  if (!this._ignoreFocusedDateChange && !this._noInput) {
@@ -795,15 +814,13 @@ export const DatePickerMixin = (subclass) =>
795
814
  // eslint-disable-next-line max-params
796
815
  __updateOverlayContent(overlayContent, i18n, label, minDate, maxDate, focusedDate, selectedDate, showWeekNumbers) {
797
816
  if (overlayContent) {
798
- overlayContent.setProperties({
799
- i18n,
800
- label,
801
- minDate,
802
- maxDate,
803
- focusedDate,
804
- selectedDate,
805
- showWeekNumbers,
806
- });
817
+ overlayContent.i18n = i18n;
818
+ overlayContent.label = label;
819
+ overlayContent.minDate = minDate;
820
+ overlayContent.maxDate = maxDate;
821
+ overlayContent.focusedDate = focusedDate;
822
+ overlayContent.selectedDate = selectedDate;
823
+ overlayContent.showWeekNumbers = showWeekNumbers;
807
824
  }
808
825
  }
809
826
 
@@ -834,6 +851,7 @@ export const DatePickerMixin = (subclass) =>
834
851
  /** @protected */
835
852
  _onOverlayOpened() {
836
853
  const content = this._overlayContent;
854
+ content.reset();
837
855
 
838
856
  // Detect which date to show
839
857
  const initialPosition = this._getInitialPosition();
@@ -885,9 +903,9 @@ export const DatePickerMixin = (subclass) =>
885
903
  this._ignoreFocusedDateChange = true;
886
904
  if (this.i18n.parseDate) {
887
905
  const inputValue = this._inputElementValue || '';
888
- const parsedDate = this._getParsedDate(inputValue);
906
+ const parsedDate = this.__parseDate(inputValue);
889
907
 
890
- if (this._isValidDate(parsedDate)) {
908
+ if (parsedDate) {
891
909
  this._selectDate(parsedDate);
892
910
  } else {
893
911
  this.__keepInputValue = true;
@@ -922,8 +940,9 @@ export const DatePickerMixin = (subclass) =>
922
940
  this._nativeInput.selectionStart = this._nativeInput.selectionEnd;
923
941
  }
924
942
  // No need to revalidate the value after `_selectedDateChanged`
925
- // Needed in case the value was not changed: open and close dropdown.
926
- if (!this.value) {
943
+ // Needed in case the value was not changed: open and close dropdown,
944
+ // especially on outside click. On Esc key press, do not validate.
945
+ if (!this.value && !this._keyboardActive) {
927
946
  this.validate();
928
947
  }
929
948
  }
@@ -950,12 +969,7 @@ export const DatePickerMixin = (subclass) =>
950
969
 
951
970
  /** @private */
952
971
  _applyInputValue(date) {
953
- this._inputElementValue = date ? this._getFormattedDate(this.i18n.formatDate, date) : '';
954
- }
955
-
956
- /** @private */
957
- _getFormattedDate(formatDate, date) {
958
- return formatDate(extractDateParts(date));
972
+ this._inputElementValue = date ? this.__formatDate(date) : '';
959
973
  }
960
974
 
961
975
  /** @private */
@@ -965,23 +979,14 @@ export const DatePickerMixin = (subclass) =>
965
979
  }
966
980
  }
967
981
 
968
- /** @private */
969
- _isValidDate(d) {
970
- return d && !isNaN(d.getTime());
971
- }
972
-
973
982
  /**
974
983
  * Override an event listener from `InputConstraintsMixin`
975
- * to have date-picker fully control when to fire a change event.
984
+ * to have date-picker fully control when to fire a change event
985
+ * and trigger validation.
986
+ *
976
987
  * @protected
977
988
  */
978
989
  _onChange(event) {
979
- // For change event on the native <input> blur, after the input is cleared,
980
- // we schedule change event to be dispatched on date-picker blur.
981
- if (this._inputElementValue === '') {
982
- this.__dispatchChange = true;
983
- }
984
-
985
990
  event.stopPropagation();
986
991
  }
987
992
 
@@ -1015,10 +1020,10 @@ export const DatePickerMixin = (subclass) =>
1015
1020
  */
1016
1021
  _onClearButtonClick(event) {
1017
1022
  event.preventDefault();
1023
+ this.dirty = true;
1018
1024
  this._inputElementValue = '';
1019
1025
  this.value = '';
1020
- this.validate();
1021
- this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
1026
+ this.__dispatchChange();
1022
1027
  }
1023
1028
 
1024
1029
  /**
@@ -1129,13 +1134,6 @@ export const DatePickerMixin = (subclass) =>
1129
1134
  }
1130
1135
  }
1131
1136
 
1132
- /** @private */
1133
- _getParsedDate(inputValue = this._inputElementValue) {
1134
- const dateObject = this.i18n.parseDate && this.i18n.parseDate(inputValue);
1135
- const parsedDate = dateObject && parseDate(`${dateObject.year}-${dateObject.month + 1}-${dateObject.day}`);
1136
- return parsedDate;
1137
- }
1138
-
1139
1137
  /** @protected */
1140
1138
  _isClearButton(event) {
1141
1139
  return event.composedPath()[0] === this.clearElement;
@@ -1146,18 +1144,13 @@ export const DatePickerMixin = (subclass) =>
1146
1144
  * @protected
1147
1145
  */
1148
1146
  _onInput() {
1149
- if (!this.opened && this.inputElement.value && !this.autoOpenDisabled) {
1147
+ if (!this.opened && this._inputElementValue && !this.autoOpenDisabled) {
1150
1148
  this.open();
1151
1149
  }
1152
- this._userInputValueChanged();
1153
- }
1154
1150
 
1155
- /** @private */
1156
- _userInputValueChanged() {
1157
1151
  if (this._inputElementValue) {
1158
- const parsedDate = this._getParsedDate();
1159
-
1160
- if (this._isValidDate(parsedDate)) {
1152
+ const parsedDate = this.__parseDate(this._inputElementValue);
1153
+ if (parsedDate) {
1161
1154
  this._ignoreFocusedDateChange = true;
1162
1155
  if (!dateEquals(parsedDate, this._focusedDate)) {
1163
1156
  this._focusedDate = parsedDate;
@@ -3,11 +3,12 @@
3
3
  * Copyright (c) 2016 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { html } from '@polymer/polymer/lib/utils/html-tag.js';
6
+ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
7
7
  import { dateAfterXMonths } from './vaadin-date-picker-helper.js';
8
8
  import { InfiniteScroller } from './vaadin-infinite-scroller.js';
9
9
 
10
- const stylesTemplate = html`
10
+ const stylesTemplate = document.createElement('template');
11
+ stylesTemplate.innerHTML = `
11
12
  <style>
12
13
  :host {
13
14
  --vaadin-infinite-scroller-item-height: 270px;
@@ -21,11 +22,10 @@ const stylesTemplate = html`
21
22
  </style>
22
23
  `;
23
24
 
24
- let memoizedTemplate;
25
-
26
25
  /**
27
26
  * An element used internally by `<vaadin-date-picker>`. Not intended to be used separately.
28
27
  *
28
+ * @customElement
29
29
  * @extends InfiniteScroller
30
30
  * @private
31
31
  */
@@ -34,22 +34,11 @@ class DatePickerMonthScroller extends InfiniteScroller {
34
34
  return 'vaadin-date-picker-month-scroller';
35
35
  }
36
36
 
37
- static get template() {
38
- if (!memoizedTemplate) {
39
- memoizedTemplate = super.template.cloneNode(true);
40
- memoizedTemplate.content.appendChild(stylesTemplate.content.cloneNode(true));
41
- }
42
-
43
- return memoizedTemplate;
44
- }
37
+ constructor() {
38
+ super();
45
39
 
46
- static get properties() {
47
- return {
48
- bufferSize: {
49
- type: Number,
50
- value: 3,
51
- },
52
- };
40
+ this.bufferSize = 3;
41
+ this.shadowRoot.appendChild(stylesTemplate.content.cloneNode(true));
53
42
  }
54
43
 
55
44
  /**
@@ -71,4 +60,4 @@ class DatePickerMonthScroller extends InfiniteScroller {
71
60
  }
72
61
  }
73
62
 
74
- customElements.define(DatePickerMonthScroller.is, DatePickerMonthScroller);
63
+ defineCustomElement(DatePickerMonthScroller);