@vaadin/date-picker 23.2.0-alpha3 → 23.2.0-alpha6

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.2.0-alpha3",
3
+ "version": "23.2.0-alpha6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -23,7 +23,9 @@
23
23
  "src",
24
24
  "theme",
25
25
  "vaadin-*.d.ts",
26
- "vaadin-*.js"
26
+ "vaadin-*.js",
27
+ "web-types.json",
28
+ "web-types.lit.json"
27
29
  ],
28
30
  "keywords": [
29
31
  "Vaadin",
@@ -34,19 +36,23 @@
34
36
  "dependencies": {
35
37
  "@open-wc/dedupe-mixin": "^1.3.0",
36
38
  "@polymer/polymer": "^3.2.0",
37
- "@vaadin/button": "23.2.0-alpha3",
38
- "@vaadin/component-base": "23.2.0-alpha3",
39
- "@vaadin/field-base": "23.2.0-alpha3",
40
- "@vaadin/input-container": "23.2.0-alpha3",
41
- "@vaadin/vaadin-lumo-styles": "23.2.0-alpha3",
42
- "@vaadin/vaadin-material-styles": "23.2.0-alpha3",
43
- "@vaadin/vaadin-overlay": "23.2.0-alpha3",
44
- "@vaadin/vaadin-themable-mixin": "23.2.0-alpha3"
39
+ "@vaadin/button": "23.2.0-alpha6",
40
+ "@vaadin/component-base": "23.2.0-alpha6",
41
+ "@vaadin/field-base": "23.2.0-alpha6",
42
+ "@vaadin/input-container": "23.2.0-alpha6",
43
+ "@vaadin/vaadin-lumo-styles": "23.2.0-alpha6",
44
+ "@vaadin/vaadin-material-styles": "23.2.0-alpha6",
45
+ "@vaadin/vaadin-overlay": "23.2.0-alpha6",
46
+ "@vaadin/vaadin-themable-mixin": "23.2.0-alpha6"
45
47
  },
46
48
  "devDependencies": {
47
49
  "@esm-bundle/chai": "^4.3.4",
48
50
  "@vaadin/testing-helpers": "^0.3.2",
49
51
  "sinon": "^13.0.2"
50
52
  },
51
- "gitHead": "06e5875be93ca50da2846dafc65a8531010c0576"
53
+ "web-types": [
54
+ "web-types.json",
55
+ "web-types.lit.json"
56
+ ],
57
+ "gitHead": "61f1fb56953434e97d34a8819640064301dd3d8a"
52
58
  }
@@ -99,13 +99,13 @@ declare class DatePickerLight extends ThemableMixin(DatePickerMixin(ValidateMixi
99
99
  addEventListener<K extends keyof DatePickerLightEventMap>(
100
100
  type: K,
101
101
  listener: (this: DatePickerLight, ev: DatePickerLightEventMap[K]) => void,
102
- options?: boolean | AddEventListenerOptions,
102
+ options?: AddEventListenerOptions | boolean,
103
103
  ): void;
104
104
 
105
105
  removeEventListener<K extends keyof DatePickerLightEventMap>(
106
106
  type: K,
107
107
  listener: (this: DatePickerLight, ev: DatePickerLightEventMap[K]) => void,
108
- options?: boolean | EventListenerOptions,
108
+ options?: EventListenerOptions | boolean,
109
109
  ): void;
110
110
  }
111
111
 
@@ -33,13 +33,13 @@ export interface DatePickerI18n {
33
33
 
34
34
  export declare function DatePickerMixin<T extends Constructor<HTMLElement>>(
35
35
  base: T,
36
- ): T &
37
- Constructor<DatePickerMixinClass> &
36
+ ): Constructor<DatePickerMixinClass> &
38
37
  Constructor<DelegateFocusMixinClass> &
39
38
  Constructor<DisabledMixinClass> &
40
39
  Constructor<FocusMixinClass> &
41
40
  Constructor<InputMixinClass> &
42
- Constructor<KeyboardMixinClass>;
41
+ Constructor<KeyboardMixinClass> &
42
+ T;
43
43
 
44
44
  export declare class DatePickerMixinClass {
45
45
  /**
@@ -46,7 +46,6 @@ export const DatePickerMixin = (subclass) =>
46
46
  */
47
47
  value: {
48
48
  type: String,
49
- observer: '_valueChanged',
50
49
  notify: true,
51
50
  value: '',
52
51
  },
@@ -102,13 +101,6 @@ export const DatePickerMixin = (subclass) =>
102
101
  value: '(max-width: 420px), (max-height: 420px)',
103
102
  },
104
103
 
105
- /**
106
- * An array of ancestor elements whose -webkit-overflow-scrolling is forced from value
107
- * 'touch' to value 'auto' in order to prevent them from clipping the dropdown. iOS only.
108
- * @private
109
- */
110
- _touchPrevented: Array,
111
-
112
104
  /**
113
105
  * The object used to localize this component.
114
106
  * To change the default localization, replace the entire
@@ -264,7 +256,6 @@ export const DatePickerMixin = (subclass) =>
264
256
  */
265
257
  min: {
266
258
  type: String,
267
- observer: '_minChanged',
268
259
  },
269
260
 
270
261
  /**
@@ -278,28 +269,28 @@ export const DatePickerMixin = (subclass) =>
278
269
  */
279
270
  max: {
280
271
  type: String,
281
- observer: '_maxChanged',
282
272
  },
283
273
 
284
274
  /**
285
275
  * The earliest date that can be selected. All earlier dates will be disabled.
286
- * @type {Date | string}
276
+ * @type {Date | undefined}
287
277
  * @protected
288
278
  */
289
279
  _minDate: {
290
280
  type: Date,
291
- // Null does not work here because minimizer passes undefined to overlay (#351)
292
- value: '',
281
+ observer: '__minDateChanged',
282
+ computed: '__computeMinOrMaxDate(min)',
293
283
  },
294
284
 
295
285
  /**
296
286
  * The latest date that can be selected. All later dates will be disabled.
297
- * @type {Date | string}
287
+ * @type {Date | undefined}
298
288
  * @protected
299
289
  */
300
290
  _maxDate: {
301
291
  type: Date,
302
- value: '',
292
+ observer: '__maxDateChanged',
293
+ computed: '__computeMinOrMaxDate(max)',
303
294
  },
304
295
 
305
296
  /** @private */
@@ -314,12 +305,6 @@ export const DatePickerMixin = (subclass) =>
314
305
  value: isIOS,
315
306
  },
316
307
 
317
- /** @private */
318
- _webkitOverflowScroll: {
319
- type: Boolean,
320
- value: document.createElement('div').style.webkitOverflowScrolling === '',
321
- },
322
-
323
308
  /** @private */
324
309
  _focusOverlayOnOpen: Boolean,
325
310
 
@@ -689,41 +674,56 @@ export const DatePickerMixin = (subclass) =>
689
674
  }
690
675
  }
691
676
 
692
- /** @private */
693
- _handleDateChange(property, value, oldValue) {
694
- if (!value) {
695
- this[property] = '';
696
- return;
697
- }
677
+ /**
678
+ * Override the value observer from `InputMixin` to implement custom
679
+ * handling of the `value` property. The date-picker doesn't forward
680
+ * the value directly to the input like the default implementation of `InputMixin`.
681
+ * Instead, it parses the value into a date, puts it in `_selectedDate` which
682
+ * is then displayed in the input with respect to the specified date format.
683
+ *
684
+ * @param {string | undefined} value
685
+ * @param {string | undefined} oldValue
686
+ * @protected
687
+ * @override
688
+ */
689
+ _valueChanged(value, oldValue) {
690
+ const newDate = this._parseDate(value);
698
691
 
699
- const date = this._parseDate(value);
700
- if (!date) {
692
+ if (value && !newDate) {
693
+ // The new value cannot be parsed, revert the old value.
701
694
  this.value = oldValue;
702
695
  return;
703
696
  }
704
- if (!dateEquals(this[property], date)) {
705
- this[property] = date;
706
- if (this.value) {
707
- this.validate();
697
+
698
+ if (value) {
699
+ if (!dateEquals(this._selectedDate, newDate)) {
700
+ // Update the date instance only if the date has actually changed.
701
+ this._selectedDate = newDate;
702
+
703
+ if (oldValue !== undefined) {
704
+ // Validate only if `value` changes after initialization.
705
+ this.validate();
706
+ }
708
707
  }
708
+ } else {
709
+ this._selectedDate = null;
709
710
  }
710
- }
711
-
712
- /** @private */
713
- _valueChanged(value, oldValue) {
714
- this._handleDateChange('_selectedDate', value, oldValue);
715
711
 
716
712
  this._toggleHasValue(!!value);
717
713
  }
718
714
 
719
715
  /** @private */
720
- _minChanged(value, oldValue) {
721
- this._handleDateChange('_minDate', value, oldValue);
716
+ __minDateChanged() {
717
+ if (this.value) {
718
+ this.validate();
719
+ }
722
720
  }
723
721
 
724
722
  /** @private */
725
- _maxChanged(value, oldValue) {
726
- this._handleDateChange('_maxDate', value, oldValue);
723
+ __maxDateChanged() {
724
+ if (this.value) {
725
+ this.validate();
726
+ }
727
727
  }
728
728
 
729
729
  /** @protected */
@@ -749,10 +749,6 @@ export const DatePickerMixin = (subclass) =>
749
749
 
750
750
  window.addEventListener('scroll', this._boundOnScroll, true);
751
751
 
752
- if (this._webkitOverflowScroll) {
753
- this._touchPrevented = this._preventWebkitOverflowScrollingTouch(this.parentElement);
754
- }
755
-
756
752
  if (this._focusOverlayOnOpen) {
757
753
  this._overlayContent.focusDateElement();
758
754
  this._focusOverlayOnOpen = false;
@@ -766,25 +762,6 @@ export const DatePickerMixin = (subclass) =>
766
762
  }
767
763
  }
768
764
 
769
- // A hack needed for iOS to prevent dropdown from being clipped in an
770
- // ancestor container with -webkit-overflow-scrolling: touch;
771
- /** @private */
772
- _preventWebkitOverflowScrollingTouch(element) {
773
- const result = [];
774
- while (element) {
775
- if (window.getComputedStyle(element).webkitOverflowScrolling === 'touch') {
776
- const oldInlineValue = element.style.webkitOverflowScrolling;
777
- element.style.webkitOverflowScrolling = 'auto';
778
- result.push({
779
- element,
780
- oldInlineValue,
781
- });
782
- }
783
- element = element.parentElement;
784
- }
785
- return result;
786
- }
787
-
788
765
  /** @private */
789
766
  _selectParsedOrFocusedDate() {
790
767
  // Select the parsed input or focused date
@@ -811,13 +788,6 @@ export const DatePickerMixin = (subclass) =>
811
788
  _onOverlayClosed() {
812
789
  window.removeEventListener('scroll', this._boundOnScroll, true);
813
790
 
814
- if (this._touchPrevented) {
815
- this._touchPrevented.forEach((prevented) => {
816
- prevented.element.style.webkitOverflowScrolling = prevented.oldInlineValue;
817
- });
818
- this._touchPrevented = [];
819
- }
820
-
821
791
  // No need to select date on close if it was confirmed by the user.
822
792
  if (this.__userConfirmedDate) {
823
793
  this.__userConfirmedDate = false;
@@ -980,7 +950,7 @@ export const DatePickerMixin = (subclass) =>
980
950
  if (e.shiftKey) {
981
951
  this._overlayContent.focusCancel();
982
952
  } else {
983
- this._overlayContent.focusDate(this._focusedDate);
953
+ this._overlayContent.focusDateElement();
984
954
  }
985
955
  }
986
956
  break;
@@ -1093,6 +1063,11 @@ export const DatePickerMixin = (subclass) =>
1093
1063
  return this.$.overlay.content.querySelector('#overlay-content');
1094
1064
  }
1095
1065
 
1066
+ /** @private */
1067
+ __computeMinOrMaxDate(dateString) {
1068
+ return this._parseDate(dateString);
1069
+ }
1070
+
1096
1071
  /**
1097
1072
  * Fired when the user commits a value change.
1098
1073
  *
@@ -318,10 +318,12 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
318
318
  return this.getAttribute('dir') === 'rtl';
319
319
  }
320
320
 
321
+ get calendars() {
322
+ return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')];
323
+ }
324
+
321
325
  get focusableDateElement() {
322
- return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')]
323
- .map((calendar) => calendar.focusableDateElement)
324
- .find(Boolean);
326
+ return this.calendars.map((calendar) => calendar.focusableDateElement).find(Boolean);
325
327
  }
326
328
 
327
329
  ready() {
@@ -526,6 +528,11 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
526
528
 
527
529
  this._targetPosition = targetPosition;
528
530
 
531
+ let revealResolve;
532
+ this._revealPromise = new Promise((resolve) => {
533
+ revealResolve = resolve;
534
+ });
535
+
529
536
  // http://gizma.com/easing/
530
537
  const easingFunction = (t, b, c, d) => {
531
538
  t /= d / 2;
@@ -566,7 +573,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
566
573
 
567
574
  this.$.monthScroller.position = this._targetPosition;
568
575
  this._targetPosition = undefined;
569
- this.__tryFocusDate();
576
+
577
+ revealResolve();
578
+ this._revealPromise = undefined;
570
579
  }
571
580
 
572
581
  setTimeout(this._repositionYearScroller.bind(this), 1);
@@ -780,23 +789,32 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
780
789
  switch (section) {
781
790
  case 'calendar':
782
791
  if (event.shiftKey) {
783
- // Return focus back to the input field.
784
792
  event.preventDefault();
785
- this.__focusInput();
793
+
794
+ if (this.hasAttribute('fullscreen')) {
795
+ // Trap focus in the overlay
796
+ this.$.cancelButton.focus();
797
+ } else {
798
+ this.__focusInput();
799
+ }
786
800
  }
787
801
  break;
788
802
  case 'today':
789
803
  if (event.shiftKey) {
790
- // Browser returns focus back to the calendar.
791
- // We need to move the scroll to focused date.
792
- setTimeout(() => this.revealDate(this.focusedDate), 1);
804
+ event.preventDefault();
805
+ this.focusDateElement();
793
806
  }
794
807
  break;
795
808
  case 'cancel':
796
809
  if (!event.shiftKey) {
797
- // Return focus back to the input field.
798
810
  event.preventDefault();
799
- this.__focusInput();
811
+
812
+ if (this.hasAttribute('fullscreen')) {
813
+ // Trap focus in the overlay
814
+ this.focusDateElement();
815
+ } else {
816
+ this.__focusInput();
817
+ }
800
818
  }
801
819
  break;
802
820
  default:
@@ -805,28 +823,12 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
805
823
  }
806
824
 
807
825
  __onTodayButtonKeyDown(event) {
808
- if (this.hasAttribute('fullscreen')) {
809
- // Do not prevent closing on Esc
810
- if (event.key !== 'Escape') {
811
- event.stopPropagation();
812
- }
813
- return;
814
- }
815
-
816
826
  if (event.key === 'Tab') {
817
827
  this._onTabKeyDown(event, 'today');
818
828
  }
819
829
  }
820
830
 
821
831
  __onCancelButtonKeyDown(event) {
822
- if (this.hasAttribute('fullscreen')) {
823
- // Do not prevent closing on Esc
824
- if (event.key !== 'Escape') {
825
- event.stopPropagation();
826
- }
827
- return;
828
- }
829
-
830
832
  if (event.key === 'Tab') {
831
833
  this._onTabKeyDown(event, 'cancel');
832
834
  }
@@ -855,15 +857,29 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
855
857
  if (!keepMonth) {
856
858
  this._focusedMonthDate = dateToFocus.getDate();
857
859
  }
858
- await this.focusDateElement();
860
+ await this.focusDateElement(false);
859
861
  }
860
862
 
861
- async focusDateElement() {
863
+ async focusDateElement(reveal = true) {
862
864
  this.__pendingDateFocus = this.focusedDate;
863
865
 
864
- await new Promise((resolve) => {
865
- requestAnimationFrame(resolve);
866
- });
866
+ // Wait for `vaadin-month-calendar` elements to be rendered
867
+ if (!this.calendars.length) {
868
+ await new Promise((resolve) => {
869
+ setTimeout(resolve);
870
+ });
871
+ }
872
+
873
+ // Reveal focused date unless it has been just set,
874
+ // which triggers `revealDate()` in the observer.
875
+ if (reveal) {
876
+ this.revealDate(this.focusedDate);
877
+ }
878
+
879
+ if (this._revealPromise) {
880
+ // Wait for focused date to be scrolled into view.
881
+ await this._revealPromise;
882
+ }
867
883
 
868
884
  this.__tryFocusDate();
869
885
  }
@@ -146,13 +146,13 @@ declare class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin
146
146
  addEventListener<K extends keyof DatePickerEventMap>(
147
147
  type: K,
148
148
  listener: (this: DatePicker, ev: DatePickerEventMap[K]) => void,
149
- options?: boolean | AddEventListenerOptions,
149
+ options?: AddEventListenerOptions | boolean,
150
150
  ): void;
151
151
 
152
152
  removeEventListener<K extends keyof DatePickerEventMap>(
153
153
  type: K,
154
154
  listener: (this: DatePicker, ev: DatePickerEventMap[K]) => void,
155
- options?: boolean | EventListenerOptions,
155
+ options?: EventListenerOptions | boolean,
156
156
  ): void;
157
157
  }
158
158
 
@@ -151,9 +151,9 @@ registerStyles(
151
151
  { moduleId: 'lumo-month-calendar' },
152
152
  );
153
153
 
154
- const $_documentContainer = document.createElement('template');
154
+ const template = document.createElement('template');
155
155
 
156
- $_documentContainer.innerHTML = `
156
+ template.innerHTML = `
157
157
  <style>
158
158
  @keyframes vaadin-date-picker-month-calendar-focus-date {
159
159
  50% {
@@ -163,4 +163,4 @@ $_documentContainer.innerHTML = `
163
163
  </style>
164
164
  `;
165
165
 
166
- document.head.appendChild($_documentContainer.content);
166
+ document.head.appendChild(template.content);