@vaadin/date-picker 24.2.0-alpha1 → 24.2.0-alpha11

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-alpha1",
3
+ "version": "24.2.0-alpha11",
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-alpha1",
40
- "@vaadin/button": "24.2.0-alpha1",
41
- "@vaadin/component-base": "24.2.0-alpha1",
42
- "@vaadin/field-base": "24.2.0-alpha1",
43
- "@vaadin/input-container": "24.2.0-alpha1",
44
- "@vaadin/overlay": "24.2.0-alpha1",
45
- "@vaadin/vaadin-lumo-styles": "24.2.0-alpha1",
46
- "@vaadin/vaadin-material-styles": "24.2.0-alpha1",
47
- "@vaadin/vaadin-themable-mixin": "24.2.0-alpha1"
43
+ "@vaadin/a11y-base": "24.2.0-alpha11",
44
+ "@vaadin/button": "24.2.0-alpha11",
45
+ "@vaadin/component-base": "24.2.0-alpha11",
46
+ "@vaadin/field-base": "24.2.0-alpha11",
47
+ "@vaadin/input-container": "24.2.0-alpha11",
48
+ "@vaadin/overlay": "24.2.0-alpha11",
49
+ "@vaadin/vaadin-lumo-styles": "24.2.0-alpha11",
50
+ "@vaadin/vaadin-material-styles": "24.2.0-alpha11",
51
+ "@vaadin/vaadin-themable-mixin": "24.2.0-alpha11"
48
52
  },
49
53
  "devDependencies": {
50
54
  "@esm-bundle/chai": "^4.3.4",
51
- "@vaadin/testing-helpers": "^0.4.2",
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": "0dbb118320203ab6c0c07450a3e718815367589f"
62
+ "gitHead": "a958207d5f6a09ca0e2dcf9f62194b3f92c8766a"
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
  */
@@ -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,14 +447,12 @@ export const DatePickerMixin = (subclass) =>
431
447
  super._onBlur(event);
432
448
 
433
449
  if (!this.opened) {
434
- if (this.autoOpenDisabled) {
435
- this._selectParsedOrFocusedDate();
436
- }
437
-
438
- this.validate();
450
+ this._selectParsedOrFocusedDate();
439
451
 
440
- if (this._inputElementValue === '' && this.value !== '') {
441
- this.value = '';
452
+ // Do not validate when focusout is caused by document
453
+ // losing focus, which happens on browser tab switch.
454
+ if (document.hasFocus()) {
455
+ this.validate();
442
456
  }
443
457
  }
444
458
  }
@@ -473,25 +487,6 @@ export const DatePickerMixin = (subclass) =>
473
487
  this.opened = false;
474
488
  }
475
489
 
476
- /**
477
- * Override Polymer lifecycle callback to dispatch `change` event if needed.
478
- * This is necessary to ensure `change` is fired after `value-changed`.
479
- *
480
- * @param {!Object} currentProps Current accessor values
481
- * @param {?Object} changedProps Properties changed since the last call
482
- * @param {?Object} oldProps Previous values for each changed property
483
- * @protected
484
- * @override
485
- */
486
- _propertiesChanged(currentProps, changedProps, oldProps) {
487
- super._propertiesChanged(currentProps, changedProps, oldProps);
488
-
489
- if ('value' in changedProps && this.__dispatchChange) {
490
- this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
491
- this.__dispatchChange = false;
492
- }
493
- }
494
-
495
490
  /**
496
491
  * Opens the dropdown.
497
492
  */
@@ -557,6 +552,34 @@ export const DatePickerMixin = (subclass) =>
557
552
  });
558
553
  }
559
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
+
560
583
  /**
561
584
  * Returns true if the current input value satisfies all constraints (if any)
562
585
  *
@@ -566,9 +589,7 @@ export const DatePickerMixin = (subclass) =>
566
589
  */
567
590
  checkValidity() {
568
591
  const inputValue = this._inputElementValue;
569
- const inputValid =
570
- !inputValue ||
571
- (!!this._selectedDate && inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
592
+ const inputValid = !inputValue || (!!this._selectedDate && inputValue === this.__formatDate(this._selectedDate));
572
593
  const minMaxValid = !this._selectedDate || dateAllowed(this._selectedDate, this._minDate, this._maxDate);
573
594
 
574
595
  let inputValidity = true;
@@ -629,6 +650,12 @@ export const DatePickerMixin = (subclass) =>
629
650
  this._shouldKeepFocusRing = focused && this._keyboardActive;
630
651
  }
631
652
 
653
+ /** @private */
654
+ __dispatchChange() {
655
+ this.validate();
656
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
657
+ }
658
+
632
659
  /**
633
660
  * Select date on user interaction and set the flag
634
661
  * to fire change event if necessary.
@@ -637,14 +664,14 @@ export const DatePickerMixin = (subclass) =>
637
664
  * @protected
638
665
  */
639
666
  _selectDate(dateToSelect) {
640
- const value = this._formatISO(dateToSelect);
641
-
642
- // Only set flag if the value will change.
643
- if (this.value !== value) {
644
- this.__dispatchChange = true;
645
- }
667
+ const prevValue = this.value;
646
668
 
647
669
  this._selectedDate = dateToSelect;
670
+
671
+ if (prevValue !== this.value) {
672
+ this.dirty = true;
673
+ this.__dispatchChange();
674
+ }
648
675
  }
649
676
 
650
677
  /** @private */
@@ -720,28 +747,24 @@ export const DatePickerMixin = (subclass) =>
720
747
  }
721
748
 
722
749
  /** @private */
723
- _selectedDateChanged(selectedDate, formatDate) {
724
- if (selectedDate === undefined || formatDate === undefined) {
750
+ _selectedDateChanged(selectedDate, i18n) {
751
+ if (selectedDate === undefined || i18n === undefined) {
725
752
  return;
726
753
  }
727
- const value = this._formatISO(selectedDate);
728
754
 
729
755
  if (!this.__keepInputValue) {
730
756
  this._applyInputValue(selectedDate);
731
757
  }
732
758
 
733
- if (value !== this.value) {
734
- this.validate();
735
- this.value = value;
736
- }
759
+ this.value = this._formatISO(selectedDate);
737
760
  this._ignoreFocusedDateChange = true;
738
761
  this._focusedDate = selectedDate;
739
762
  this._ignoreFocusedDateChange = false;
740
763
  }
741
764
 
742
765
  /** @private */
743
- _focusedDateChanged(focusedDate, formatDate) {
744
- if (focusedDate === undefined || formatDate === undefined) {
766
+ _focusedDateChanged(focusedDate, i18n) {
767
+ if (focusedDate === undefined || i18n === undefined) {
745
768
  return;
746
769
  }
747
770
  if (!this._ignoreFocusedDateChange && !this._noInput) {
@@ -791,15 +814,13 @@ export const DatePickerMixin = (subclass) =>
791
814
  // eslint-disable-next-line max-params
792
815
  __updateOverlayContent(overlayContent, i18n, label, minDate, maxDate, focusedDate, selectedDate, showWeekNumbers) {
793
816
  if (overlayContent) {
794
- overlayContent.setProperties({
795
- i18n,
796
- label,
797
- minDate,
798
- maxDate,
799
- focusedDate,
800
- selectedDate,
801
- showWeekNumbers,
802
- });
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;
803
824
  }
804
825
  }
805
826
 
@@ -830,6 +851,7 @@ export const DatePickerMixin = (subclass) =>
830
851
  /** @protected */
831
852
  _onOverlayOpened() {
832
853
  const content = this._overlayContent;
854
+ content.reset();
833
855
 
834
856
  // Detect which date to show
835
857
  const initialPosition = this._getInitialPosition();
@@ -881,9 +903,9 @@ export const DatePickerMixin = (subclass) =>
881
903
  this._ignoreFocusedDateChange = true;
882
904
  if (this.i18n.parseDate) {
883
905
  const inputValue = this._inputElementValue || '';
884
- const parsedDate = this._getParsedDate(inputValue);
906
+ const parsedDate = this.__parseDate(inputValue);
885
907
 
886
- if (this._isValidDate(parsedDate)) {
908
+ if (parsedDate) {
887
909
  this._selectDate(parsedDate);
888
910
  } else {
889
911
  this.__keepInputValue = true;
@@ -918,8 +940,9 @@ export const DatePickerMixin = (subclass) =>
918
940
  this._nativeInput.selectionStart = this._nativeInput.selectionEnd;
919
941
  }
920
942
  // No need to revalidate the value after `_selectedDateChanged`
921
- // Needed in case the value was not changed: open and close dropdown.
922
- 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) {
923
946
  this.validate();
924
947
  }
925
948
  }
@@ -946,12 +969,7 @@ export const DatePickerMixin = (subclass) =>
946
969
 
947
970
  /** @private */
948
971
  _applyInputValue(date) {
949
- this._inputElementValue = date ? this._getFormattedDate(this.i18n.formatDate, date) : '';
950
- }
951
-
952
- /** @private */
953
- _getFormattedDate(formatDate, date) {
954
- return formatDate(extractDateParts(date));
972
+ this._inputElementValue = date ? this.__formatDate(date) : '';
955
973
  }
956
974
 
957
975
  /** @private */
@@ -961,23 +979,14 @@ export const DatePickerMixin = (subclass) =>
961
979
  }
962
980
  }
963
981
 
964
- /** @private */
965
- _isValidDate(d) {
966
- return d && !isNaN(d.getTime());
967
- }
968
-
969
982
  /**
970
983
  * Override an event listener from `InputConstraintsMixin`
971
- * 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
+ *
972
987
  * @protected
973
988
  */
974
989
  _onChange(event) {
975
- // For change event on the native <input> blur, after the input is cleared,
976
- // we schedule change event to be dispatched on date-picker blur.
977
- if (this._inputElementValue === '') {
978
- this.__dispatchChange = true;
979
- }
980
-
981
990
  event.stopPropagation();
982
991
  }
983
992
 
@@ -1011,10 +1020,10 @@ export const DatePickerMixin = (subclass) =>
1011
1020
  */
1012
1021
  _onClearButtonClick(event) {
1013
1022
  event.preventDefault();
1023
+ this.dirty = true;
1014
1024
  this._inputElementValue = '';
1015
1025
  this.value = '';
1016
- this.validate();
1017
- this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
1026
+ this.__dispatchChange();
1018
1027
  }
1019
1028
 
1020
1029
  /**
@@ -1125,13 +1134,6 @@ export const DatePickerMixin = (subclass) =>
1125
1134
  }
1126
1135
  }
1127
1136
 
1128
- /** @private */
1129
- _getParsedDate(inputValue = this._inputElementValue) {
1130
- const dateObject = this.i18n.parseDate && this.i18n.parseDate(inputValue);
1131
- const parsedDate = dateObject && parseDate(`${dateObject.year}-${dateObject.month + 1}-${dateObject.day}`);
1132
- return parsedDate;
1133
- }
1134
-
1135
1137
  /** @protected */
1136
1138
  _isClearButton(event) {
1137
1139
  return event.composedPath()[0] === this.clearElement;
@@ -1142,18 +1144,13 @@ export const DatePickerMixin = (subclass) =>
1142
1144
  * @protected
1143
1145
  */
1144
1146
  _onInput() {
1145
- if (!this.opened && this.inputElement.value && !this.autoOpenDisabled) {
1147
+ if (!this.opened && this._inputElementValue && !this.autoOpenDisabled) {
1146
1148
  this.open();
1147
1149
  }
1148
- this._userInputValueChanged();
1149
- }
1150
1150
 
1151
- /** @private */
1152
- _userInputValueChanged() {
1153
1151
  if (this._inputElementValue) {
1154
- const parsedDate = this._getParsedDate();
1155
-
1156
- if (this._isValidDate(parsedDate)) {
1152
+ const parsedDate = this.__parseDate(this._inputElementValue);
1153
+ if (parsedDate) {
1157
1154
  this._ignoreFocusedDateChange = true;
1158
1155
  if (!dateEquals(parsedDate, this._focusedDate)) {
1159
1156
  this._focusedDate = parsedDate;
@@ -3,11 +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 { html } from '@polymer/polymer/lib/utils/html-tag.js';
7
6
  import { dateAfterXMonths } from './vaadin-date-picker-helper.js';
8
7
  import { InfiniteScroller } from './vaadin-infinite-scroller.js';
9
8
 
10
- const stylesTemplate = html`
9
+ const stylesTemplate = document.createElement('template');
10
+ stylesTemplate.innerHTML = `
11
11
  <style>
12
12
  :host {
13
13
  --vaadin-infinite-scroller-item-height: 270px;
@@ -21,8 +21,6 @@ const stylesTemplate = html`
21
21
  </style>
22
22
  `;
23
23
 
24
- let memoizedTemplate;
25
-
26
24
  /**
27
25
  * An element used internally by `<vaadin-date-picker>`. Not intended to be used separately.
28
26
  *
@@ -34,22 +32,11 @@ class DatePickerMonthScroller extends InfiniteScroller {
34
32
  return 'vaadin-date-picker-month-scroller';
35
33
  }
36
34
 
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
- }
35
+ constructor() {
36
+ super();
45
37
 
46
- static get properties() {
47
- return {
48
- bufferSize: {
49
- type: Number,
50
- value: 3,
51
- },
52
- };
38
+ this.bufferSize = 3;
39
+ this.shadowRoot.appendChild(stylesTemplate.content.cloneNode(true));
53
40
  }
54
41
 
55
42
  /**