@everymatrix/helper-filters 0.1.2 → 0.1.4

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.
@@ -14,7 +14,8 @@ const TRANSLATIONS = {
14
14
  filterDateRangePlaceholder: 'Date Range',
15
15
  filterModalButton: 'Search',
16
16
  filterFromCalendar: 'From',
17
- filterToCalendar: 'To'
17
+ filterToCalendar: 'To',
18
+ filterOrDate: 'or search by date'
18
19
  },
19
20
  ro: {
20
21
  filterOpen: 'Filtrare',
@@ -24,7 +25,8 @@ const TRANSLATIONS = {
24
25
  filterTicketPlaceholder: 'Cauta ID bilet',
25
26
  filterDrawPlaceholder: 'Cauta ID draw',
26
27
  filterDateRangePlaceholder: 'Perioada',
27
- filterModalButton: 'Cauta'
28
+ filterModalButton: 'Cauta',
29
+ filterOrDate: 'sau cauta dupa data'
28
30
  },
29
31
  };
30
32
  const translate$1 = (key, customLang) => {
@@ -39,7 +41,7 @@ const translate$1 = (key, customLang) => {
39
41
  */
40
42
  class Lumo extends HTMLElement {
41
43
  static get version() {
42
- return '23.1.5';
44
+ return '23.2.0';
43
45
  }
44
46
  }
45
47
 
@@ -95,7 +97,7 @@ const ThemePropertyMixin = (superClass) =>
95
97
  * **NOTE:** Extending the mixin only provides the property for binding,
96
98
  * and does not make the propagation alone.
97
99
  *
98
- * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/ds/customization/styling-components/#sub-components).
100
+ * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components/#sub-components).
99
101
  * page for more information.
100
102
  *
101
103
  * @deprecated The `theme` property is not supposed for public use and will be dropped in Vaadin 24.
@@ -120,7 +122,7 @@ const ThemePropertyMixin = (superClass) =>
120
122
  * **NOTE:** Extending the mixin only provides the property for binding,
121
123
  * and does not make the propagation alone.
122
124
  *
123
- * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/ds/customization/styling-components/#sub-components).
125
+ * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components/#sub-components).
124
126
  * page for more information.
125
127
  *
126
128
  * @protected
@@ -225,9 +227,9 @@ function matchesThemeFor(themeFor, tagName) {
225
227
  */
226
228
  function getIncludePriority(moduleName = '') {
227
229
  let includePriority = 0;
228
- if (moduleName.indexOf('lumo-') === 0 || moduleName.indexOf('material-') === 0) {
230
+ if (moduleName.startsWith('lumo-') || moduleName.startsWith('material-')) {
229
231
  includePriority = 1;
230
- } else if (moduleName.indexOf('vaadin-') === 0) {
232
+ } else if (moduleName.startsWith('vaadin-')) {
231
233
  includePriority = 2;
232
234
  }
233
235
  return includePriority;
@@ -10112,6 +10114,38 @@ const ControllerMixin = dedupingMixin(
10112
10114
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
10113
10115
  */
10114
10116
 
10117
+ // We consider the keyboard to be active if the window has received a keydown
10118
+ // event since the last mousedown event.
10119
+ let keyboardActive = false;
10120
+
10121
+ // Listen for top-level keydown and mousedown events.
10122
+ // Use capture phase so we detect events even if they're handled.
10123
+ window.addEventListener(
10124
+ 'keydown',
10125
+ () => {
10126
+ keyboardActive = true;
10127
+ },
10128
+ { capture: true },
10129
+ );
10130
+
10131
+ window.addEventListener(
10132
+ 'mousedown',
10133
+ () => {
10134
+ keyboardActive = false;
10135
+ },
10136
+ { capture: true },
10137
+ );
10138
+
10139
+ /**
10140
+ * Returns true if the window has received a keydown
10141
+ * event since the last mousedown event.
10142
+ *
10143
+ * @return {boolean}
10144
+ */
10145
+ function isKeyboardActive() {
10146
+ return keyboardActive;
10147
+ }
10148
+
10115
10149
  /**
10116
10150
  * Returns true if the element is hidden directly with `display: none` or `visibility: hidden`,
10117
10151
  * false otherwise.
@@ -10552,7 +10586,7 @@ class FocusTrapController {
10552
10586
  * ---|---|---
10553
10587
  * `--vaadin-overlay-viewport-bottom` | Bottom offset of the visible viewport area | `0` or detected offset
10554
10588
  *
10555
- * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
10589
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
10556
10590
  *
10557
10591
  * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
10558
10592
  * @fires {CustomEvent} vaadin-overlay-open - Fired after the overlay is opened.
@@ -12822,7 +12856,7 @@ const registered = new Set();
12822
12856
  const ElementMixin = (superClass) =>
12823
12857
  class VaadinElementMixin extends DirMixin(superClass) {
12824
12858
  static get version() {
12825
- return '23.1.5';
12859
+ return '23.2.0';
12826
12860
  }
12827
12861
 
12828
12862
  /** @protected */
@@ -13120,7 +13154,7 @@ function _handleNative(ev) {
13120
13154
  }
13121
13155
  if (!ev[HANDLED_OBJ]) {
13122
13156
  ev[HANDLED_OBJ] = {};
13123
- if (type.slice(0, 5) === 'touch') {
13157
+ if (type.startsWith('touch')) {
13124
13158
  const t = ev.changedTouches[0];
13125
13159
  if (type === 'touchstart') {
13126
13160
  // Only handle the first finger
@@ -13979,28 +14013,6 @@ const ActiveMixin = (superclass) =>
13979
14013
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
13980
14014
  */
13981
14015
 
13982
- // We consider the keyboard to be active if the window has received a keydown
13983
- // event since the last mousedown event.
13984
- let keyboardActive = false;
13985
-
13986
- // Listen for top-level keydown and mousedown events.
13987
- // Use capture phase so we detect events even if they're handled.
13988
- window.addEventListener(
13989
- 'keydown',
13990
- () => {
13991
- keyboardActive = true;
13992
- },
13993
- { capture: true },
13994
- );
13995
-
13996
- window.addEventListener(
13997
- 'mousedown',
13998
- () => {
13999
- keyboardActive = false;
14000
- },
14001
- { capture: true },
14002
- );
14003
-
14004
14016
  /**
14005
14017
  * A mixin to handle `focused` and `focus-ring` attributes based on focus.
14006
14018
  *
@@ -14014,7 +14026,7 @@ const FocusMixin = dedupingMixin(
14014
14026
  * @return {boolean}
14015
14027
  */
14016
14028
  get _keyboardActive() {
14017
- return keyboardActive;
14029
+ return isKeyboardActive();
14018
14030
  }
14019
14031
 
14020
14032
  /** @protected */
@@ -14278,7 +14290,7 @@ const ButtonMixin = (superClass) =>
14278
14290
  * `focus-ring` | Set when the button is focused using the keyboard.
14279
14291
  * `focused` | Set when the button is focused.
14280
14292
  *
14281
- * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
14293
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
14282
14294
  *
14283
14295
  * @extends HTMLElement
14284
14296
  * @mixes ButtonMixin
@@ -14360,7 +14372,6 @@ registerStyles(
14360
14372
  i$1`
14361
14373
  :host {
14362
14374
  position: relative;
14363
- background-color: transparent;
14364
14375
  /* Background for the year scroller, placed here as we are using a mask image on the actual years part */
14365
14376
  background-image: linear-gradient(var(--lumo-shade-5pct), var(--lumo-shade-5pct));
14366
14377
  background-size: 57px 100%;
@@ -14468,17 +14479,10 @@ registerStyles(
14468
14479
 
14469
14480
  [part='toolbar'] {
14470
14481
  padding: var(--lumo-space-s);
14471
- box-shadow: 0 -1px 0 0 var(--lumo-contrast-10pct);
14472
14482
  border-bottom-left-radius: var(--lumo-border-radius-l);
14473
14483
  margin-right: 57px;
14474
14484
  }
14475
14485
 
14476
- @supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
14477
- [part='toolbar'] {
14478
- box-shadow: none;
14479
- }
14480
- }
14481
-
14482
14486
  /* Today and Cancel buttons */
14483
14487
 
14484
14488
  [part='toolbar'] [part\$='button'] {
@@ -14511,8 +14515,6 @@ registerStyles(
14511
14515
  /* Very narrow screen (year scroller initially hidden) */
14512
14516
 
14513
14517
  [part='years-toggle-button'] {
14514
- position: relative;
14515
- right: auto;
14516
14518
  display: flex;
14517
14519
  align-items: center;
14518
14520
  height: var(--lumo-size-s);
@@ -14530,10 +14532,6 @@ registerStyles(
14530
14532
  color: var(--lumo-primary-contrast-color);
14531
14533
  }
14532
14534
 
14533
- [part='years-toggle-button']::before {
14534
- content: none;
14535
- }
14536
-
14537
14535
  /* TODO magic number (same as used for iron-media-query in vaadin-date-picker-overlay-content) */
14538
14536
  @media screen and (max-width: 374px) {
14539
14537
  :host {
@@ -14709,9 +14707,9 @@ registerStyles(
14709
14707
  { moduleId: 'lumo-month-calendar' },
14710
14708
  );
14711
14709
 
14712
- const $_documentContainer$1 = document.createElement('template');
14710
+ const template$1 = document.createElement('template');
14713
14711
 
14714
- $_documentContainer$1.innerHTML = `
14712
+ template$1.innerHTML = `
14715
14713
  <style>
14716
14714
  @keyframes vaadin-date-picker-month-calendar-focus-date {
14717
14715
  50% {
@@ -14721,7 +14719,7 @@ $_documentContainer$1.innerHTML = `
14721
14719
  </style>
14722
14720
  `;
14723
14721
 
14724
- document.head.appendChild($_documentContainer$1.content);
14722
+ document.head.appendChild(template$1.content);
14725
14723
 
14726
14724
  /**
14727
14725
  * @license
@@ -14729,9 +14727,9 @@ document.head.appendChild($_documentContainer$1.content);
14729
14727
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
14730
14728
  */
14731
14729
 
14732
- const $_documentContainer = document.createElement('template');
14730
+ const template = document.createElement('template');
14733
14731
 
14734
- $_documentContainer.innerHTML = `
14732
+ template.innerHTML = `
14735
14733
  <style>
14736
14734
  @font-face {
14737
14735
  font-family: 'lumo-icons';
@@ -14787,7 +14785,7 @@ $_documentContainer.innerHTML = `
14787
14785
  </style>
14788
14786
  `;
14789
14787
 
14790
- document.head.appendChild($_documentContainer.content);
14788
+ document.head.appendChild(template.content);
14791
14789
 
14792
14790
  /**
14793
14791
  * @license
@@ -15354,6 +15352,57 @@ function getAncestorRootNodes(node) {
15354
15352
  return result;
15355
15353
  }
15356
15354
 
15355
+ /**
15356
+ * @param {string} value
15357
+ * @return {Set<string>}
15358
+ */
15359
+ function deserializeAttributeValue(value) {
15360
+ if (!value) {
15361
+ return new Set();
15362
+ }
15363
+
15364
+ return new Set(value.split(' '));
15365
+ }
15366
+
15367
+ /**
15368
+ * @param {Set<string>} values
15369
+ * @return {string}
15370
+ */
15371
+ function serializeAttributeValue(values) {
15372
+ return [...values].join(' ');
15373
+ }
15374
+
15375
+ /**
15376
+ * Adds a value to an attribute containing space-delimited values.
15377
+ *
15378
+ * @param {HTMLElement} element
15379
+ * @param {string} attr
15380
+ * @param {string} value
15381
+ */
15382
+ function addValueToAttribute(element, attr, value) {
15383
+ const values = deserializeAttributeValue(element.getAttribute(attr));
15384
+ values.add(value);
15385
+ element.setAttribute(attr, serializeAttributeValue(values));
15386
+ }
15387
+
15388
+ /**
15389
+ * Removes a value from an attribute containing space-delimited values.
15390
+ * If the value is the last one, the whole attribute is removed.
15391
+ *
15392
+ * @param {HTMLElement} element
15393
+ * @param {string} attr
15394
+ * @param {string} value
15395
+ */
15396
+ function removeValueFromAttribute(element, attr, value) {
15397
+ const values = deserializeAttributeValue(element.getAttribute(attr));
15398
+ values.delete(value);
15399
+ if (values.size === 0) {
15400
+ element.removeAttribute(attr);
15401
+ return;
15402
+ }
15403
+ element.setAttribute(attr, serializeAttributeValue(values));
15404
+ }
15405
+
15357
15406
  /**
15358
15407
  * @license
15359
15408
  * Copyright (c) 2017 - 2022 Vaadin Ltd.
@@ -17078,7 +17127,9 @@ class MonthCalendar extends FocusMixin(ThemableMixin(PolymerElement)) {
17078
17127
 
17079
17128
  _onMonthGridTouchStart() {
17080
17129
  this._notTapping = false;
17081
- setTimeout(() => (this._notTapping = true), 300);
17130
+ setTimeout(() => {
17131
+ this._notTapping = true;
17132
+ }, 300);
17082
17133
  }
17083
17134
 
17084
17135
  _dateAdd(date, delta) {
@@ -17413,7 +17464,7 @@ class InfiniteScroller extends PolymerElement {
17413
17464
  // Once the first set of items start fading in, stamp the rest
17414
17465
  this._buffers.forEach((buffer) => {
17415
17466
  [].forEach.call(buffer.children, (insertionPoint) => this._ensureStampedInstance(insertionPoint._itemWrapper));
17416
- }, this);
17467
+ });
17417
17468
 
17418
17469
  if (!this._buffers[0].translateY) {
17419
17470
  this._reset();
@@ -17576,7 +17627,7 @@ class InfiniteScroller extends PolymerElement {
17576
17627
  }
17577
17628
  }, 1); // Wait for first reset
17578
17629
  }
17579
- }, this);
17630
+ });
17580
17631
 
17581
17632
  setTimeout(() => {
17582
17633
  afterNextRender(this, this._finishInit.bind(this));
@@ -17614,7 +17665,7 @@ class InfiniteScroller extends PolymerElement {
17614
17665
  });
17615
17666
  buffer.updated = true;
17616
17667
  }
17617
- }, this);
17668
+ });
17618
17669
  }
17619
17670
 
17620
17671
  _isVisible(element, container) {
@@ -17713,7 +17764,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17713
17764
  height: 100%;
17714
17765
  width: 100%;
17715
17766
  outline: none;
17716
- background: #fff;
17717
17767
  }
17718
17768
 
17719
17769
  [part='overlay-header'] {
@@ -17731,22 +17781,14 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17731
17781
  flex-grow: 1;
17732
17782
  }
17733
17783
 
17734
- [part='clear-button']:not([showclear]) {
17735
- display: none;
17784
+ [hidden] {
17785
+ display: none !important;
17736
17786
  }
17737
17787
 
17738
17788
  [part='years-toggle-button'] {
17739
17789
  display: flex;
17740
17790
  }
17741
17791
 
17742
- [part='years-toggle-button'][desktop] {
17743
- display: none;
17744
- }
17745
-
17746
- :host(:not([years-visible])) [part='years-toggle-button']::before {
17747
- transform: rotate(180deg);
17748
- }
17749
-
17750
17792
  #scrollers {
17751
17793
  display: flex;
17752
17794
  height: 100%;
@@ -17820,27 +17862,14 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17820
17862
  z-index: 2;
17821
17863
  flex-shrink: 0;
17822
17864
  }
17823
-
17824
- [part~='overlay-header']:not([desktop]) {
17825
- padding-bottom: 40px;
17826
- }
17827
-
17828
- [part~='years-toggle-button'] {
17829
- position: absolute;
17830
- top: auto;
17831
- right: 8px;
17832
- bottom: 0;
17833
- z-index: 1;
17834
- padding: 8px;
17835
- }
17836
17865
  </style>
17837
17866
 
17838
17867
  <div part="overlay-header" on-touchend="_preventDefault" desktop$="[[_desktopMode]]" aria-hidden="true">
17839
17868
  <div part="label">[[_formatDisplayed(selectedDate, i18n.formatDate, label)]]</div>
17840
- <div part="clear-button" showclear$="[[_showClear(selectedDate)]]"></div>
17869
+ <div part="clear-button" hidden$="[[!selectedDate]]"></div>
17841
17870
  <div part="toggle-button"></div>
17842
17871
 
17843
- <div part="years-toggle-button" desktop$="[[_desktopMode]]" aria-hidden="true">
17872
+ <div part="years-toggle-button" hidden$="[[_desktopMode]]" aria-hidden="true">
17844
17873
  [[_yearAfterXMonths(_visibleMonthIndex)]]
17845
17874
  </div>
17846
17875
  </div>
@@ -17926,6 +17955,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17926
17955
  */
17927
17956
  selectedDate: {
17928
17957
  type: Date,
17958
+ value: null,
17929
17959
  },
17930
17960
 
17931
17961
  /**
@@ -18001,10 +18031,12 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18001
18031
  return this.getAttribute('dir') === 'rtl';
18002
18032
  }
18003
18033
 
18034
+ get calendars() {
18035
+ return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')];
18036
+ }
18037
+
18004
18038
  get focusableDateElement() {
18005
- return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')]
18006
- .map((calendar) => calendar.focusableDateElement)
18007
- .find(Boolean);
18039
+ return this.calendars.map((calendar) => calendar.focusableDateElement).find(Boolean);
18008
18040
  }
18009
18041
 
18010
18042
  ready() {
@@ -18012,7 +18044,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18012
18044
 
18013
18045
  this.setAttribute('role', 'dialog');
18014
18046
 
18015
- addListener(this, 'tap', this._stopPropagation);
18016
18047
  addListener(this.$.scrollers, 'track', this._track.bind(this));
18017
18048
  addListener(this.shadowRoot.querySelector('[part="clear-button"]'), 'tap', this._clear.bind(this));
18018
18049
  addListener(this.shadowRoot.querySelector('[part="today-button"]'), 'tap', this._onTodayTap.bind(this));
@@ -18134,7 +18165,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18134
18165
 
18135
18166
  _onYearScrollTouchStart() {
18136
18167
  this._notTapping = false;
18137
- setTimeout(() => (this._notTapping = true), 300);
18168
+ setTimeout(() => {
18169
+ this._notTapping = true;
18170
+ }, 300);
18138
18171
 
18139
18172
  this._repositionMonthScroller();
18140
18173
  }
@@ -18145,7 +18178,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18145
18178
 
18146
18179
  _doIgnoreTaps() {
18147
18180
  this._ignoreTaps = true;
18148
- this._debouncer = Debouncer$1.debounce(this._debouncer, timeOut.after(300), () => (this._ignoreTaps = false));
18181
+ this._debouncer = Debouncer$1.debounce(this._debouncer, timeOut.after(300), () => {
18182
+ this._ignoreTaps = false;
18183
+ });
18149
18184
  }
18150
18185
 
18151
18186
  _formatDisplayed(date, formatDate, label) {
@@ -18176,10 +18211,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18176
18211
  this.scrollToDate(new Date(), true);
18177
18212
  }
18178
18213
 
18179
- _showClear(selectedDate) {
18180
- return !!selectedDate;
18181
- }
18182
-
18183
18214
  _onYearTap(e) {
18184
18215
  if (!this._ignoreTaps && !this._notTapping) {
18185
18216
  const scrollDelta =
@@ -18205,6 +18236,11 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18205
18236
 
18206
18237
  this._targetPosition = targetPosition;
18207
18238
 
18239
+ let revealResolve;
18240
+ this._revealPromise = new Promise((resolve) => {
18241
+ revealResolve = resolve;
18242
+ });
18243
+
18208
18244
  // http://gizma.com/easing/
18209
18245
  const easingFunction = (t, b, c, d) => {
18210
18246
  t /= d / 2;
@@ -18245,7 +18281,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18245
18281
 
18246
18282
  this.$.monthScroller.position = this._targetPosition;
18247
18283
  this._targetPosition = undefined;
18248
- this.__tryFocusDate();
18284
+
18285
+ revealResolve();
18286
+ this._revealPromise = undefined;
18249
18287
  }
18250
18288
 
18251
18289
  setTimeout(this._repositionYearScroller.bind(this), 1);
@@ -18455,51 +18493,44 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18455
18493
  switch (section) {
18456
18494
  case 'calendar':
18457
18495
  if (event.shiftKey) {
18458
- // Return focus back to the input field.
18459
18496
  event.preventDefault();
18460
- this.__focusInput();
18497
+
18498
+ if (this.hasAttribute('fullscreen')) {
18499
+ // Trap focus in the overlay
18500
+ this.$.cancelButton.focus();
18501
+ } else {
18502
+ this.__focusInput();
18503
+ }
18461
18504
  }
18462
18505
  break;
18463
18506
  case 'today':
18464
18507
  if (event.shiftKey) {
18465
- // Browser returns focus back to the calendar.
18466
- // We need to move the scroll to focused date.
18467
- setTimeout(() => this.revealDate(this.focusedDate), 1);
18508
+ event.preventDefault();
18509
+ this.focusDateElement();
18468
18510
  }
18469
18511
  break;
18470
18512
  case 'cancel':
18471
18513
  if (!event.shiftKey) {
18472
- // Return focus back to the input field.
18473
18514
  event.preventDefault();
18474
- this.__focusInput();
18515
+
18516
+ if (this.hasAttribute('fullscreen')) {
18517
+ // Trap focus in the overlay
18518
+ this.focusDateElement();
18519
+ } else {
18520
+ this.__focusInput();
18521
+ }
18475
18522
  }
18476
18523
  break;
18477
18524
  }
18478
18525
  }
18479
18526
 
18480
18527
  __onTodayButtonKeyDown(event) {
18481
- if (this.hasAttribute('fullscreen')) {
18482
- // Do not prevent closing on Esc
18483
- if (event.key !== 'Escape') {
18484
- event.stopPropagation();
18485
- }
18486
- return;
18487
- }
18488
-
18489
18528
  if (event.key === 'Tab') {
18490
18529
  this._onTabKeyDown(event, 'today');
18491
18530
  }
18492
18531
  }
18493
18532
 
18494
18533
  __onCancelButtonKeyDown(event) {
18495
- if (this.hasAttribute('fullscreen')) {
18496
- // Do not prevent closing on Esc
18497
- if (event.key !== 'Escape') {
18498
- event.stopPropagation();
18499
- }
18500
- return;
18501
- }
18502
-
18503
18534
  if (event.key === 'Tab') {
18504
18535
  this._onTabKeyDown(event, 'cancel');
18505
18536
  }
@@ -18528,15 +18559,29 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18528
18559
  if (!keepMonth) {
18529
18560
  this._focusedMonthDate = dateToFocus.getDate();
18530
18561
  }
18531
- await this.focusDateElement();
18562
+ await this.focusDateElement(false);
18532
18563
  }
18533
18564
 
18534
- async focusDateElement() {
18565
+ async focusDateElement(reveal = true) {
18535
18566
  this.__pendingDateFocus = this.focusedDate;
18536
18567
 
18537
- await new Promise((resolve) => {
18538
- requestAnimationFrame(resolve);
18539
- });
18568
+ // Wait for `vaadin-month-calendar` elements to be rendered
18569
+ if (!this.calendars.length) {
18570
+ await new Promise((resolve) => {
18571
+ setTimeout(resolve);
18572
+ });
18573
+ }
18574
+
18575
+ // Reveal focused date unless it has been just set,
18576
+ // which triggers `revealDate()` in the observer.
18577
+ if (reveal) {
18578
+ this.revealDate(this.focusedDate);
18579
+ }
18580
+
18581
+ if (this._revealPromise) {
18582
+ // Wait for focused date to be scrolled into view.
18583
+ await this._revealPromise;
18584
+ }
18540
18585
 
18541
18586
  this.__tryFocusDate();
18542
18587
  }
@@ -18634,10 +18679,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18634
18679
  todayMidnight.setDate(today.getDate());
18635
18680
  return this._dateAllowed(todayMidnight, min, max);
18636
18681
  }
18637
-
18638
- _stopPropagation(e) {
18639
- e.stopPropagation();
18640
- }
18641
18682
  }
18642
18683
 
18643
18684
  customElements.define(DatePickerOverlayContent.is, DatePickerOverlayContent);
@@ -18868,6 +18909,24 @@ const DelegateFocusMixin = dedupingMixin(
18868
18909
  },
18869
18910
  );
18870
18911
 
18912
+ /**
18913
+ * @license
18914
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
18915
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
18916
+ */
18917
+
18918
+ let uniqueId = 0;
18919
+
18920
+ /**
18921
+ * Returns a unique integer id.
18922
+ *
18923
+ * @return {number}
18924
+ */
18925
+ function generateUniqueId() {
18926
+ // eslint-disable-next-line no-plusplus
18927
+ return uniqueId++;
18928
+ }
18929
+
18871
18930
  /**
18872
18931
  * @license
18873
18932
  * Copyright (c) 2021 - 2022 Vaadin Ltd.
@@ -18888,24 +18947,21 @@ class SlotController extends EventTarget {
18888
18947
  */
18889
18948
  static generateId(slotName, host) {
18890
18949
  const prefix = slotName || 'default';
18891
-
18892
- // Support dash-case slot names e.g. "error-message"
18893
- const field = `${dashToCamelCase(prefix)}Id`;
18894
-
18895
- // Maintain the unique ID counter for a given prefix.
18896
- this[field] = 1 + this[field] || 0;
18897
-
18898
- return `${prefix}-${host.localName}-${this[field]}`;
18950
+ return `${prefix}-${host.localName}-${generateUniqueId()}`;
18899
18951
  }
18900
18952
 
18901
- constructor(host, slotName, slotFactory, slotInitializer) {
18953
+ constructor(host, slotName, slotFactory, slotInitializer, useUniqueId) {
18902
18954
  super();
18903
18955
 
18904
18956
  this.host = host;
18905
18957
  this.slotName = slotName;
18906
18958
  this.slotFactory = slotFactory;
18907
18959
  this.slotInitializer = slotInitializer;
18908
- this.defaultId = SlotController.generateId(slotName, host);
18960
+
18961
+ // Only generate the default ID if requested by the controller.
18962
+ if (useUniqueId) {
18963
+ this.defaultId = SlotController.generateId(slotName, host);
18964
+ }
18909
18965
  }
18910
18966
 
18911
18967
  hostConnected() {
@@ -19060,6 +19116,7 @@ class ErrorController extends SlotController {
19060
19116
 
19061
19117
  this.__updateHasError();
19062
19118
  },
19119
+ true,
19063
19120
  );
19064
19121
  }
19065
19122
 
@@ -19174,63 +19231,6 @@ class ErrorController extends SlotController {
19174
19231
  }
19175
19232
  }
19176
19233
 
19177
- /**
19178
- * @license
19179
- * Copyright (c) 2021 - 2022 Vaadin Ltd.
19180
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
19181
- */
19182
-
19183
- /**
19184
- * @param {string} value
19185
- * @return {Set<string>}
19186
- */
19187
- function deserializeAttributeValue(value) {
19188
- if (!value) {
19189
- return new Set();
19190
- }
19191
-
19192
- return new Set(value.split(' '));
19193
- }
19194
-
19195
- /**
19196
- * @param {Set<string>} values
19197
- * @return {string}
19198
- */
19199
- function serializeAttributeValue(values) {
19200
- return [...values].join(' ');
19201
- }
19202
-
19203
- /**
19204
- * Adds a value to an attribute containing space-delimited values.
19205
- *
19206
- * @param {HTMLElement} element
19207
- * @param {string} attr
19208
- * @param {string} value
19209
- */
19210
- function addValueToAttribute(element, attr, value) {
19211
- const values = deserializeAttributeValue(element.getAttribute(attr));
19212
- values.add(value);
19213
- element.setAttribute(attr, serializeAttributeValue(values));
19214
- }
19215
-
19216
- /**
19217
- * Removes a value from an attribute containing space-delimited values.
19218
- * If the value is the last one, the whole attribute is removed.
19219
- *
19220
- * @param {HTMLElement} element
19221
- * @param {string} attr
19222
- * @param {string} value
19223
- */
19224
- function removeValueFromAttribute(element, attr, value) {
19225
- const values = deserializeAttributeValue(element.getAttribute(attr));
19226
- values.delete(value);
19227
- if (values.size === 0) {
19228
- element.removeAttribute(attr);
19229
- return;
19230
- }
19231
- element.setAttribute(attr, serializeAttributeValue(values));
19232
- }
19233
-
19234
19234
  /**
19235
19235
  * @license
19236
19236
  * Copyright (c) 2021 - 2022 Vaadin Ltd.
@@ -19405,7 +19405,7 @@ class FieldAriaController {
19405
19405
 
19406
19406
  /**
19407
19407
  * @license
19408
- * Copyright (c) 2021 Vaadin Ltd.
19408
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
19409
19409
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
19410
19410
  */
19411
19411
 
@@ -19415,7 +19415,7 @@ class FieldAriaController {
19415
19415
  class HelperController extends SlotController {
19416
19416
  constructor(host) {
19417
19417
  // Do not provide slot factory, as only create helper lazily.
19418
- super(host, 'helper');
19418
+ super(host, 'helper', null, null, true);
19419
19419
  }
19420
19420
 
19421
19421
  get helperId() {
@@ -19612,6 +19612,7 @@ class LabelController extends SlotController {
19612
19612
 
19613
19613
  this.__observeLabel(node);
19614
19614
  },
19615
+ true,
19615
19616
  );
19616
19617
  }
19617
19618
 
@@ -19818,6 +19819,12 @@ const LabelMixin = dedupingMixin(
19818
19819
  super();
19819
19820
 
19820
19821
  this._labelController = new LabelController(this);
19822
+ }
19823
+
19824
+ /** @protected */
19825
+ ready() {
19826
+ super.ready();
19827
+
19821
19828
  this.addController(this._labelController);
19822
19829
  }
19823
19830
 
@@ -19865,12 +19872,17 @@ const ValidateMixin = dedupingMixin(
19865
19872
  }
19866
19873
 
19867
19874
  /**
19868
- * Returns true if field is valid, and sets `invalid` based on the field validity.
19875
+ * Validates the field and sets the `invalid` property based on the result.
19876
+ *
19877
+ * The method fires a `validated` event with the result of the validation.
19869
19878
  *
19870
19879
  * @return {boolean} True if the value is valid.
19871
19880
  */
19872
19881
  validate() {
19873
- return !(this.invalid = !this.checkValidity());
19882
+ const isValid = this.checkValidity();
19883
+ this._setInvalid(!isValid);
19884
+ this.dispatchEvent(new CustomEvent('validated', { detail: { valid: isValid } }));
19885
+ return isValid;
19874
19886
  }
19875
19887
 
19876
19888
  /**
@@ -19881,6 +19893,35 @@ const ValidateMixin = dedupingMixin(
19881
19893
  checkValidity() {
19882
19894
  return !this.required || !!this.value;
19883
19895
  }
19896
+
19897
+ /**
19898
+ * @param {boolean} invalid
19899
+ * @protected
19900
+ */
19901
+ _setInvalid(invalid) {
19902
+ if (this._shouldSetInvalid(invalid)) {
19903
+ this.invalid = invalid;
19904
+ }
19905
+ }
19906
+
19907
+ /**
19908
+ * Override this method to define whether the given `invalid` state should be set.
19909
+ *
19910
+ * @param {boolean} _invalid
19911
+ * @return {boolean}
19912
+ * @protected
19913
+ */
19914
+ _shouldSetInvalid(_invalid) {
19915
+ return true;
19916
+ }
19917
+
19918
+ /**
19919
+ * Fired whenever the field is validated.
19920
+ *
19921
+ * @event validated
19922
+ * @param {Object} detail
19923
+ * @param {boolean} detail.valid the result of the validation.
19924
+ */
19884
19925
  },
19885
19926
  );
19886
19927
 
@@ -19969,10 +20010,6 @@ const FieldMixin = (superclass) =>
19969
20010
  this._helperController = new HelperController(this);
19970
20011
  this._errorController = new ErrorController(this);
19971
20012
 
19972
- this.addController(this._fieldAriaController);
19973
- this.addController(this._helperController);
19974
- this.addController(this._errorController);
19975
-
19976
20013
  this._labelController.addEventListener('label-changed', (event) => {
19977
20014
  const { hasLabel, node } = event.detail;
19978
20015
  this.__labelChanged(hasLabel, node);
@@ -19984,6 +20021,15 @@ const FieldMixin = (superclass) =>
19984
20021
  });
19985
20022
  }
19986
20023
 
20024
+ /** @protected */
20025
+ ready() {
20026
+ super.ready();
20027
+
20028
+ this.addController(this._fieldAriaController);
20029
+ this.addController(this._helperController);
20030
+ this.addController(this._errorController);
20031
+ }
20032
+
19987
20033
  /** @private */
19988
20034
  __helperChanged(hasHelper, helperNode) {
19989
20035
  if (hasHelper) {
@@ -20039,7 +20085,7 @@ const FieldMixin = (superclass) =>
20039
20085
  }
20040
20086
 
20041
20087
  /**
20042
- * @param {boolean} required
20088
+ * @param {boolean} invalid
20043
20089
  * @protected
20044
20090
  */
20045
20091
  _invalidChanged(invalid) {
@@ -20239,13 +20285,23 @@ const InputMixin = dedupingMixin(
20239
20285
  observer: '_valueChanged',
20240
20286
  notify: true,
20241
20287
  },
20288
+
20289
+ /**
20290
+ * When true, the input element has a non-empty value entered by the user.
20291
+ * @protected
20292
+ */
20293
+ _hasInputValue: {
20294
+ type: Boolean,
20295
+ value: false,
20296
+ observer: '_hasInputValueChanged',
20297
+ },
20242
20298
  };
20243
20299
  }
20244
20300
 
20245
20301
  constructor() {
20246
20302
  super();
20247
20303
 
20248
- this._boundOnInput = this._onInput.bind(this);
20304
+ this._boundOnInput = this.__onInput.bind(this);
20249
20305
  this._boundOnChange = this._onChange.bind(this);
20250
20306
  }
20251
20307
 
@@ -20260,6 +20316,7 @@ const InputMixin = dedupingMixin(
20260
20316
  * Add event listeners to the input element instance.
20261
20317
  * Override this method to add custom listeners.
20262
20318
  * @param {!HTMLElement} input
20319
+ * @protected
20263
20320
  */
20264
20321
  _addInputListeners(input) {
20265
20322
  input.addEventListener('input', this._boundOnInput);
@@ -20269,6 +20326,7 @@ const InputMixin = dedupingMixin(
20269
20326
  /**
20270
20327
  * Remove event listeners from the input element instance.
20271
20328
  * @param {!HTMLElement} input
20329
+ * @protected
20272
20330
  */
20273
20331
  _removeInputListeners(input) {
20274
20332
  input.removeEventListener('input', this._boundOnInput);
@@ -20282,7 +20340,6 @@ const InputMixin = dedupingMixin(
20282
20340
  * for example to skip this in certain conditions.
20283
20341
  * @param {string} value
20284
20342
  * @protected
20285
- * @override
20286
20343
  */
20287
20344
  _forwardInputValue(value) {
20288
20345
  // Value might be set before an input element is initialized.
@@ -20299,7 +20356,11 @@ const InputMixin = dedupingMixin(
20299
20356
  }
20300
20357
  }
20301
20358
 
20302
- /** @protected */
20359
+ /**
20360
+ * @param {HTMLElement | undefined} input
20361
+ * @param {HTMLElement | undefined} oldInput
20362
+ * @protected
20363
+ */
20303
20364
  _inputElementChanged(input, oldInput) {
20304
20365
  if (input) {
20305
20366
  this._addInputListeners(input);
@@ -20308,17 +20369,47 @@ const InputMixin = dedupingMixin(
20308
20369
  }
20309
20370
  }
20310
20371
 
20372
+ /**
20373
+ * Observer to notify about the change of private property.
20374
+ *
20375
+ * @private
20376
+ */
20377
+ _hasInputValueChanged(hasValue, oldHasValue) {
20378
+ if (hasValue || oldHasValue) {
20379
+ this.dispatchEvent(new CustomEvent('has-input-value-changed'));
20380
+ }
20381
+ }
20382
+
20383
+ /**
20384
+ * An input event listener used to update `_hasInputValue` property.
20385
+ * Do not override this method.
20386
+ *
20387
+ * @param {Event} event
20388
+ * @private
20389
+ */
20390
+ __onInput(event) {
20391
+ // In the case a custom web component is passed as `inputElement`,
20392
+ // the actual native input element, on which the event occurred,
20393
+ // can be inside shadow trees.
20394
+ const target = event.composedPath()[0];
20395
+ this._hasInputValue = target.value.length > 0;
20396
+ this._onInput(event);
20397
+ }
20398
+
20311
20399
  /**
20312
20400
  * An input event listener used to update the field value.
20313
- * Override this method with an actual implementation.
20314
- * @param {Event} _event
20401
+ *
20402
+ * @param {Event} event
20315
20403
  * @protected
20316
- * @override
20317
20404
  */
20318
20405
  _onInput(event) {
20406
+ // In the case a custom web component is passed as `inputElement`,
20407
+ // the actual native input element, on which the event occurred,
20408
+ // can be inside shadow trees.
20409
+ const target = event.composedPath()[0];
20319
20410
  // Ignore fake input events e.g. used by clear button.
20320
20411
  this.__userInput = event.isTrusted;
20321
- this.value = event.target.value;
20412
+ this.value = target.value;
20322
20413
  this.__userInput = false;
20323
20414
  }
20324
20415
 
@@ -20327,12 +20418,12 @@ const InputMixin = dedupingMixin(
20327
20418
  * Override this method with an actual implementation.
20328
20419
  * @param {Event} _event
20329
20420
  * @protected
20330
- * @override
20331
20421
  */
20332
20422
  _onChange(_event) {}
20333
20423
 
20334
20424
  /**
20335
20425
  * Toggle the has-value attribute based on the value property.
20426
+ *
20336
20427
  * @param {boolean} hasValue
20337
20428
  * @protected
20338
20429
  */
@@ -20345,10 +20436,9 @@ const InputMixin = dedupingMixin(
20345
20436
  * @param {string | undefined} newVal
20346
20437
  * @param {string | undefined} oldVal
20347
20438
  * @protected
20348
- * @override
20349
20439
  */
20350
20440
  _valueChanged(newVal, oldVal) {
20351
- this._toggleHasValue(newVal !== '' && newVal != null);
20441
+ this._toggleHasValue(this._hasValue);
20352
20442
 
20353
20443
  // Setting initial value to empty string, do nothing.
20354
20444
  if (newVal === '' && oldVal === undefined) {
@@ -20363,6 +20453,16 @@ const InputMixin = dedupingMixin(
20363
20453
  // Setting a value programmatically, sync it to input element.
20364
20454
  this._forwardInputValue(newVal);
20365
20455
  }
20456
+
20457
+ /**
20458
+ * Indicates whether the value is different from the default one.
20459
+ * Override if the `value` property has a type other than `string`.
20460
+ *
20461
+ * @protected
20462
+ */
20463
+ get _hasValue() {
20464
+ return this.value != null && this.value !== '';
20465
+ }
20366
20466
  },
20367
20467
  );
20368
20468
 
@@ -20434,26 +20534,32 @@ const InputConstraintsMixin = dedupingMixin(
20434
20534
  _createConstraintsObserver() {
20435
20535
  // This complex observer needs to be added dynamically instead of using `static get observers()`
20436
20536
  // to make it possible to tweak this behavior in classes that apply this mixin.
20437
- this._createMethodObserver(`_constraintsChanged(${this.constructor.constraints.join(', ')})`);
20537
+ this._createMethodObserver(`_constraintsChanged(stateTarget, ${this.constructor.constraints.join(', ')})`);
20438
20538
  }
20439
20539
 
20440
20540
  /**
20441
20541
  * Override this method to implement custom validation constraints.
20542
+ * @param {HTMLElement | undefined} stateTarget
20442
20543
  * @param {unknown[]} constraints
20443
20544
  * @protected
20444
20545
  */
20445
- _constraintsChanged(...constraints) {
20446
- // Prevent marking field as invalid when setting required state
20447
- // or any other constraint before a user has entered the value.
20448
- if (!this.invalid) {
20546
+ _constraintsChanged(stateTarget, ...constraints) {
20547
+ // The input element's validity cannot be determined until
20548
+ // all the necessary constraint attributes aren't set on it.
20549
+ if (!stateTarget) {
20449
20550
  return;
20450
20551
  }
20451
20552
 
20452
- if (this._hasValidConstraints(constraints)) {
20553
+ const hasConstraints = this._hasValidConstraints(constraints);
20554
+ const isLastConstraintRemoved = this.__previousHasConstraints && !hasConstraints;
20555
+
20556
+ if ((this._hasValue || this.invalid) && hasConstraints) {
20453
20557
  this.validate();
20454
- } else {
20455
- this.invalid = false;
20558
+ } else if (isLastConstraintRemoved) {
20559
+ this._setInvalid(false);
20456
20560
  }
20561
+
20562
+ this.__previousHasConstraints = hasConstraints;
20457
20563
  }
20458
20564
 
20459
20565
  /**
@@ -20509,6 +20615,22 @@ const InputControlMixin = (superclass) =>
20509
20615
  ) {
20510
20616
  static get properties() {
20511
20617
  return {
20618
+ /**
20619
+ * A pattern matched against individual characters the user inputs.
20620
+ *
20621
+ * When set, the field will prevent:
20622
+ * - `keydown` events if the entered key doesn't match `/^allowedCharPattern$/`
20623
+ * - `paste` events if the pasted text doesn't match `/^allowedCharPattern*$/`
20624
+ * - `drop` events if the dropped text doesn't match `/^allowedCharPattern*$/`
20625
+ *
20626
+ * For example, to allow entering only numbers and minus signs, use:
20627
+ * `allowedCharPattern = "[\\d-]"`
20628
+ */
20629
+ allowedCharPattern: {
20630
+ type: String,
20631
+ observer: '_allowedCharPatternChanged',
20632
+ },
20633
+
20512
20634
  /**
20513
20635
  * If true, the input text gets fully selected when the field is focused using click or touch / tap.
20514
20636
  */
@@ -20566,6 +20688,14 @@ const InputControlMixin = (superclass) =>
20566
20688
  return [...super.delegateAttrs, 'name', 'type', 'placeholder', 'readonly', 'invalid', 'title'];
20567
20689
  }
20568
20690
 
20691
+ constructor() {
20692
+ super();
20693
+
20694
+ this._boundOnPaste = this._onPaste.bind(this);
20695
+ this._boundOnDrop = this._onDrop.bind(this);
20696
+ this._boundOnBeforeInput = this._onBeforeInput.bind(this);
20697
+ }
20698
+
20569
20699
  /**
20570
20700
  * Any element extending this mixin is required to implement this getter.
20571
20701
  * It returns the reference to the clear button element.
@@ -20658,6 +20788,115 @@ const InputControlMixin = (superclass) =>
20658
20788
  this.inputElement.dispatchEvent(new Event('change', { bubbles: true }));
20659
20789
  }
20660
20790
 
20791
+ /**
20792
+ * Override a method from `InputMixin`.
20793
+ * @param {!HTMLElement} input
20794
+ * @protected
20795
+ * @override
20796
+ */
20797
+ _addInputListeners(input) {
20798
+ super._addInputListeners(input);
20799
+
20800
+ input.addEventListener('paste', this._boundOnPaste);
20801
+ input.addEventListener('drop', this._boundOnDrop);
20802
+ input.addEventListener('beforeinput', this._boundOnBeforeInput);
20803
+ }
20804
+
20805
+ /**
20806
+ * Override a method from `InputMixin`.
20807
+ * @param {!HTMLElement} input
20808
+ * @protected
20809
+ * @override
20810
+ */
20811
+ _removeInputListeners(input) {
20812
+ super._removeInputListeners(input);
20813
+
20814
+ input.removeEventListener('paste', this._boundOnPaste);
20815
+ input.removeEventListener('drop', this._boundOnDrop);
20816
+ input.removeEventListener('beforeinput', this._boundOnBeforeInput);
20817
+ }
20818
+
20819
+ /**
20820
+ * Override an event listener from `KeyboardMixin`.
20821
+ * @param {!KeyboardEvent} event
20822
+ * @protected
20823
+ * @override
20824
+ */
20825
+ _onKeyDown(event) {
20826
+ super._onKeyDown(event);
20827
+
20828
+ if (this.allowedCharPattern && !this.__shouldAcceptKey(event)) {
20829
+ event.preventDefault();
20830
+ this._markInputPrevented();
20831
+ }
20832
+ }
20833
+
20834
+ /** @protected */
20835
+ _markInputPrevented() {
20836
+ // Add input-prevented attribute for 200ms
20837
+ this.setAttribute('input-prevented', '');
20838
+ this._preventInputDebouncer = Debouncer$1.debounce(this._preventInputDebouncer, timeOut.after(200), () => {
20839
+ this.removeAttribute('input-prevented');
20840
+ });
20841
+ }
20842
+
20843
+ /** @private */
20844
+ __shouldAcceptKey(event) {
20845
+ return (
20846
+ event.metaKey ||
20847
+ event.ctrlKey ||
20848
+ !event.key || // Allow typing anything if event.key is not supported
20849
+ event.key.length !== 1 || // Allow "Backspace", "ArrowLeft" etc.
20850
+ this.__allowedCharRegExp.test(event.key)
20851
+ );
20852
+ }
20853
+
20854
+ /** @private */
20855
+ _onPaste(e) {
20856
+ if (this.allowedCharPattern) {
20857
+ const pastedText = e.clipboardData.getData('text');
20858
+ if (!this.__allowedTextRegExp.test(pastedText)) {
20859
+ e.preventDefault();
20860
+ this._markInputPrevented();
20861
+ }
20862
+ }
20863
+ }
20864
+
20865
+ /** @private */
20866
+ _onDrop(e) {
20867
+ if (this.allowedCharPattern) {
20868
+ const draggedText = e.dataTransfer.getData('text');
20869
+ if (!this.__allowedTextRegExp.test(draggedText)) {
20870
+ e.preventDefault();
20871
+ this._markInputPrevented();
20872
+ }
20873
+ }
20874
+ }
20875
+
20876
+ /** @private */
20877
+ _onBeforeInput(e) {
20878
+ // The `beforeinput` event covers all the cases for `allowedCharPattern`: keyboard, pasting and dropping,
20879
+ // but it is still experimental technology so we can't rely on it. It's used here just as an additional check,
20880
+ // because it seems to be the only way to detect and prevent specific keys on mobile devices.
20881
+ // See https://github.com/vaadin/vaadin-text-field/issues/429
20882
+ if (this.allowedCharPattern && e.data && !this.__allowedTextRegExp.test(e.data)) {
20883
+ e.preventDefault();
20884
+ this._markInputPrevented();
20885
+ }
20886
+ }
20887
+
20888
+ /** @private */
20889
+ _allowedCharPatternChanged(charPattern) {
20890
+ if (charPattern) {
20891
+ try {
20892
+ this.__allowedCharRegExp = new RegExp(`^${charPattern}$`);
20893
+ this.__allowedTextRegExp = new RegExp(`^${charPattern}*$`);
20894
+ } catch (e) {
20895
+ console.error(e);
20896
+ }
20897
+ }
20898
+ }
20899
+
20661
20900
  /**
20662
20901
  * Fired when the user commits a value change.
20663
20902
  *
@@ -20696,14 +20935,13 @@ class InputController extends SlotController {
20696
20935
  }
20697
20936
 
20698
20937
  // Ensure every instance has unique ID
20699
- const uniqueId = (InputController._uniqueInputId = 1 + InputController._uniqueInputId || 0);
20700
- host._inputId = `${host.localName}-${uniqueId}`;
20701
- node.id = host._inputId;
20938
+ node.id = this.defaultId;
20702
20939
 
20703
20940
  if (typeof callback === 'function') {
20704
20941
  callback(node);
20705
20942
  }
20706
20943
  },
20944
+ true,
20707
20945
  );
20708
20946
  }
20709
20947
  }
@@ -20885,7 +21123,9 @@ class VirtualKeyboardController {
20885
21123
  * @param {function(new:HTMLElement)} subclass
20886
21124
  */
20887
21125
  const DatePickerMixin = (subclass) =>
20888
- class VaadinDatePickerMixin extends ControllerMixin(DelegateFocusMixin(InputMixin(KeyboardMixin(subclass)))) {
21126
+ class VaadinDatePickerMixin extends ControllerMixin(
21127
+ DelegateFocusMixin(InputConstraintsMixin(KeyboardMixin(subclass))),
21128
+ ) {
20889
21129
  static get properties() {
20890
21130
  return {
20891
21131
  /**
@@ -20914,7 +21154,6 @@ const DatePickerMixin = (subclass) =>
20914
21154
  */
20915
21155
  value: {
20916
21156
  type: String,
20917
- observer: '_valueChanged',
20918
21157
  notify: true,
20919
21158
  value: '',
20920
21159
  },
@@ -20970,13 +21209,6 @@ const DatePickerMixin = (subclass) =>
20970
21209
  value: '(max-width: 420px), (max-height: 420px)',
20971
21210
  },
20972
21211
 
20973
- /**
20974
- * An array of ancestor elements whose -webkit-overflow-scrolling is forced from value
20975
- * 'touch' to value 'auto' in order to prevent them from clipping the dropdown. iOS only.
20976
- * @private
20977
- */
20978
- _touchPrevented: Array,
20979
-
20980
21212
  /**
20981
21213
  * The object used to localize this component.
20982
21214
  * To change the default localization, replace the entire
@@ -21132,7 +21364,6 @@ const DatePickerMixin = (subclass) =>
21132
21364
  */
21133
21365
  min: {
21134
21366
  type: String,
21135
- observer: '_minChanged',
21136
21367
  },
21137
21368
 
21138
21369
  /**
@@ -21146,28 +21377,26 @@ const DatePickerMixin = (subclass) =>
21146
21377
  */
21147
21378
  max: {
21148
21379
  type: String,
21149
- observer: '_maxChanged',
21150
21380
  },
21151
21381
 
21152
21382
  /**
21153
21383
  * The earliest date that can be selected. All earlier dates will be disabled.
21154
- * @type {Date | string}
21384
+ * @type {Date | undefined}
21155
21385
  * @protected
21156
21386
  */
21157
21387
  _minDate: {
21158
21388
  type: Date,
21159
- // Null does not work here because minimizer passes undefined to overlay (#351)
21160
- value: '',
21389
+ computed: '__computeMinOrMaxDate(min)',
21161
21390
  },
21162
21391
 
21163
21392
  /**
21164
21393
  * The latest date that can be selected. All later dates will be disabled.
21165
- * @type {Date | string}
21394
+ * @type {Date | undefined}
21166
21395
  * @protected
21167
21396
  */
21168
21397
  _maxDate: {
21169
21398
  type: Date,
21170
- value: '',
21399
+ computed: '__computeMinOrMaxDate(max)',
21171
21400
  },
21172
21401
 
21173
21402
  /** @private */
@@ -21182,12 +21411,6 @@ const DatePickerMixin = (subclass) =>
21182
21411
  value: isIOS,
21183
21412
  },
21184
21413
 
21185
- /** @private */
21186
- _webkitOverflowScroll: {
21187
- type: Boolean,
21188
- value: document.createElement('div').style.webkitOverflowScrolling === '',
21189
- },
21190
-
21191
21414
  /** @private */
21192
21415
  _focusOverlayOnOpen: Boolean,
21193
21416
 
@@ -21203,6 +21426,10 @@ const DatePickerMixin = (subclass) =>
21203
21426
  ];
21204
21427
  }
21205
21428
 
21429
+ static get constraints() {
21430
+ return [...super.constraints, 'min', 'max'];
21431
+ }
21432
+
21206
21433
  /**
21207
21434
  * Override a getter from `InputControlMixin` to make it optional
21208
21435
  * and to prevent warning when a clear button is missing,
@@ -21269,12 +21496,10 @@ const DatePickerMixin = (subclass) =>
21269
21496
  }
21270
21497
  }
21271
21498
 
21272
- if (this.inputElement.value === '' && this.__dispatchChange) {
21273
- this.validate();
21499
+ this.validate();
21500
+
21501
+ if (this._inputValue === '' && this.value !== '') {
21274
21502
  this.value = '';
21275
- this.__dispatchChange = false;
21276
- } else {
21277
- this.validate();
21278
21503
  }
21279
21504
  }
21280
21505
  }
@@ -21343,14 +21568,19 @@ const DatePickerMixin = (subclass) =>
21343
21568
  this.$.overlay.removeAttribute('disable-upgrade');
21344
21569
  this._overlayInitialized = true;
21345
21570
 
21346
- this.$.overlay.addEventListener('opened-changed', (e) => (this.opened = e.detail.value));
21571
+ this.$.overlay.addEventListener('opened-changed', (e) => {
21572
+ this.opened = e.detail.value;
21573
+ });
21347
21574
 
21348
21575
  this.$.overlay.addEventListener('vaadin-overlay-escape-press', () => {
21349
21576
  this._focusedDate = this._selectedDate;
21350
21577
  this._close();
21351
21578
  });
21352
21579
 
21353
- this._overlayContent.addEventListener('close', this._close.bind(this));
21580
+ this._overlayContent.addEventListener('close', () => {
21581
+ this._close();
21582
+ });
21583
+
21354
21584
  this._overlayContent.addEventListener('focus-input', this._focusAndSelect.bind(this));
21355
21585
 
21356
21586
  // User confirmed selected date by clicking the calendar.
@@ -21359,7 +21589,7 @@ const DatePickerMixin = (subclass) =>
21359
21589
 
21360
21590
  this._selectDate(e.detail.date);
21361
21591
 
21362
- this._close(e);
21592
+ this._close();
21363
21593
  });
21364
21594
 
21365
21595
  // User confirmed selected date by pressing Enter or Today.
@@ -21369,24 +21599,18 @@ const DatePickerMixin = (subclass) =>
21369
21599
  this._selectDate(e.detail.date);
21370
21600
  });
21371
21601
 
21372
- // Keep focus attribute in focusElement for styling
21602
+ // Set focus-ring attribute when moving focus to the overlay
21603
+ // by pressing Tab or arrow key, after opening it on click.
21373
21604
  this._overlayContent.addEventListener('focusin', () => {
21374
- this._setFocused(true);
21605
+ if (this._keyboardActive) {
21606
+ this._setFocused(true);
21607
+ }
21375
21608
  });
21376
21609
 
21377
21610
  this.addEventListener('mousedown', () => this.__bringToFront());
21378
21611
  this.addEventListener('touchstart', () => this.__bringToFront());
21379
21612
  }
21380
21613
 
21381
- /**
21382
- * Returns true if `value` is valid, and sets the `invalid` flag appropriately.
21383
- *
21384
- * @return {boolean} True if the value is valid and sets the `invalid` flag appropriately
21385
- */
21386
- validate() {
21387
- return !(this.invalid = !this.checkValidity());
21388
- }
21389
-
21390
21614
  /**
21391
21615
  * Returns true if the current input value satisfies all constraints (if any)
21392
21616
  *
@@ -21397,7 +21621,7 @@ const DatePickerMixin = (subclass) =>
21397
21621
  checkValidity() {
21398
21622
  const inputValid =
21399
21623
  !this._inputValue ||
21400
- (this._selectedDate && this._inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
21624
+ (!!this._selectedDate && this._inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
21401
21625
  const minMaxValid = !this._selectedDate || dateAllowed(this._selectedDate, this._minDate, this._maxDate);
21402
21626
 
21403
21627
  let inputValidity = true;
@@ -21413,6 +21637,51 @@ const DatePickerMixin = (subclass) =>
21413
21637
  return inputValid && minMaxValid && inputValidity;
21414
21638
  }
21415
21639
 
21640
+ /**
21641
+ * Override method inherited from `FocusMixin`
21642
+ * to not call `_setFocused(true)` when focus
21643
+ * is restored after closing overlay on click,
21644
+ * and to avoid removing `focus-ring` attribute.
21645
+ *
21646
+ * @param {!FocusEvent} _event
21647
+ * @return {boolean}
21648
+ * @protected
21649
+ * @override
21650
+ */
21651
+ _shouldSetFocus(_event) {
21652
+ return !this._shouldKeepFocusRing;
21653
+ }
21654
+
21655
+ /**
21656
+ * Override method inherited from `FocusMixin`
21657
+ * to prevent removing the `focused` attribute:
21658
+ * - when moving focus to the overlay content,
21659
+ * - when closing on date click / outside click.
21660
+ *
21661
+ * @param {!FocusEvent} _event
21662
+ * @return {boolean}
21663
+ * @protected
21664
+ * @override
21665
+ */
21666
+ _shouldRemoveFocus(_event) {
21667
+ return !this.opened;
21668
+ }
21669
+
21670
+ /**
21671
+ * Override method inherited from `FocusMixin`
21672
+ * to store the `focus-ring` state to restore
21673
+ * it later when closing on outside click.
21674
+ *
21675
+ * @param {boolean} focused
21676
+ * @protected
21677
+ * @override
21678
+ */
21679
+ _setFocused(focused) {
21680
+ super._setFocused(focused);
21681
+
21682
+ this._shouldKeepFocusRing = focused && this._keyboardActive;
21683
+ }
21684
+
21416
21685
  /**
21417
21686
  * Select date on user interaction and set the flag
21418
21687
  * to fire change event if necessary.
@@ -21432,10 +21701,7 @@ const DatePickerMixin = (subclass) =>
21432
21701
  }
21433
21702
 
21434
21703
  /** @private */
21435
- _close(e) {
21436
- if (e) {
21437
- e.stopPropagation();
21438
- }
21704
+ _close() {
21439
21705
  this._focus();
21440
21706
  this.close();
21441
21707
  }
@@ -21564,47 +21830,46 @@ const DatePickerMixin = (subclass) =>
21564
21830
  }
21565
21831
  }
21566
21832
 
21567
- /** @private */
21568
- _handleDateChange(property, value, oldValue) {
21569
- if (!value) {
21570
- this[property] = '';
21571
- return;
21572
- }
21833
+ /**
21834
+ * Override the value observer from `InputMixin` to implement custom
21835
+ * handling of the `value` property. The date-picker doesn't forward
21836
+ * the value directly to the input like the default implementation of `InputMixin`.
21837
+ * Instead, it parses the value into a date, puts it in `_selectedDate` which
21838
+ * is then displayed in the input with respect to the specified date format.
21839
+ *
21840
+ * @param {string | undefined} value
21841
+ * @param {string | undefined} oldValue
21842
+ * @protected
21843
+ * @override
21844
+ */
21845
+ _valueChanged(value, oldValue) {
21846
+ const newDate = this._parseDate(value);
21573
21847
 
21574
- const date = this._parseDate(value);
21575
- if (!date) {
21848
+ if (value && !newDate) {
21849
+ // The new value cannot be parsed, revert the old value.
21576
21850
  this.value = oldValue;
21577
21851
  return;
21578
21852
  }
21579
- if (!dateEquals(this[property], date)) {
21580
- this[property] = date;
21581
- if (this.value) {
21582
- this.validate();
21583
- }
21584
- }
21585
- }
21586
21853
 
21587
- /** @private */
21588
- _valueChanged(value, oldValue) {
21589
- this._handleDateChange('_selectedDate', value, oldValue);
21854
+ if (value) {
21855
+ if (!dateEquals(this._selectedDate, newDate)) {
21856
+ // Update the date instance only if the date has actually changed.
21857
+ this._selectedDate = newDate;
21590
21858
 
21591
- this._toggleHasValue(!!value);
21592
- }
21593
-
21594
- /** @private */
21595
- _minChanged(value, oldValue) {
21596
- this._handleDateChange('_minDate', value, oldValue);
21597
- }
21859
+ if (oldValue !== undefined) {
21860
+ // Validate only if `value` changes after initialization.
21861
+ this.validate();
21862
+ }
21863
+ }
21864
+ } else {
21865
+ this._selectedDate = null;
21866
+ }
21598
21867
 
21599
- /** @private */
21600
- _maxChanged(value, oldValue) {
21601
- this._handleDateChange('_maxDate', value, oldValue);
21868
+ this._toggleHasValue(this._hasValue);
21602
21869
  }
21603
21870
 
21604
21871
  /** @protected */
21605
21872
  _onOverlayOpened() {
21606
- this._openedWithFocusRing = this.hasAttribute('focus-ring');
21607
-
21608
21873
  const parsedInitialPosition = this._parseDate(this.initialPosition);
21609
21874
 
21610
21875
  const initialPosition =
@@ -21624,10 +21889,6 @@ const DatePickerMixin = (subclass) =>
21624
21889
 
21625
21890
  window.addEventListener('scroll', this._boundOnScroll, true);
21626
21891
 
21627
- if (this._webkitOverflowScroll) {
21628
- this._touchPrevented = this._preventWebkitOverflowScrollingTouch(this.parentElement);
21629
- }
21630
-
21631
21892
  if (this._focusOverlayOnOpen) {
21632
21893
  this._overlayContent.focusDateElement();
21633
21894
  this._focusOverlayOnOpen = false;
@@ -21641,25 +21902,6 @@ const DatePickerMixin = (subclass) =>
21641
21902
  }
21642
21903
  }
21643
21904
 
21644
- // A hack needed for iOS to prevent dropdown from being clipped in an
21645
- // ancestor container with -webkit-overflow-scrolling: touch;
21646
- /** @private */
21647
- _preventWebkitOverflowScrollingTouch(element) {
21648
- const result = [];
21649
- while (element) {
21650
- if (window.getComputedStyle(element).webkitOverflowScrolling === 'touch') {
21651
- const oldInlineValue = element.style.webkitOverflowScrolling;
21652
- element.style.webkitOverflowScrolling = 'auto';
21653
- result.push({
21654
- element,
21655
- oldInlineValue,
21656
- });
21657
- }
21658
- element = element.parentElement;
21659
- }
21660
- return result;
21661
- }
21662
-
21663
21905
  /** @private */
21664
21906
  _selectParsedOrFocusedDate() {
21665
21907
  // Select the parsed input or focused date
@@ -21686,13 +21928,6 @@ const DatePickerMixin = (subclass) =>
21686
21928
  _onOverlayClosed() {
21687
21929
  window.removeEventListener('scroll', this._boundOnScroll, true);
21688
21930
 
21689
- if (this._touchPrevented) {
21690
- this._touchPrevented.forEach(
21691
- (prevented) => (prevented.element.style.webkitOverflowScrolling = prevented.oldInlineValue),
21692
- );
21693
- this._touchPrevented = [];
21694
- }
21695
-
21696
21931
  // No need to select date on close if it was confirmed by the user.
21697
21932
  if (this.__userConfirmedDate) {
21698
21933
  this.__userConfirmedDate = false;
@@ -21708,11 +21943,6 @@ const DatePickerMixin = (subclass) =>
21708
21943
  if (!this.value) {
21709
21944
  this.validate();
21710
21945
  }
21711
-
21712
- // If the input isn't focused when overlay closes (fullscreen mode), clear focused state
21713
- if (this.getRootNode().activeElement !== this.inputElement) {
21714
- this._setFocused(false);
21715
- }
21716
21946
  }
21717
21947
 
21718
21948
  /** @private */
@@ -21765,10 +21995,7 @@ const DatePickerMixin = (subclass) =>
21765
21995
  _onChange(event) {
21766
21996
  // For change event on the native <input> blur, after the input is cleared,
21767
21997
  // we schedule change event to be dispatched on date-picker blur.
21768
- if (
21769
- this.inputElement.value === '' &&
21770
- !(event.detail && event.detail.sourceEvent && event.detail.sourceEvent.__fromClearButton)
21771
- ) {
21998
+ if (this._inputValue === '') {
21772
21999
  this.__dispatchChange = true;
21773
22000
  }
21774
22001
 
@@ -21855,7 +22082,7 @@ const DatePickerMixin = (subclass) =>
21855
22082
  if (e.shiftKey) {
21856
22083
  this._overlayContent.focusCancel();
21857
22084
  } else {
21858
- this._overlayContent.focusDate(this._focusedDate);
22085
+ this._overlayContent.focusDateElement();
21859
22086
  }
21860
22087
  }
21861
22088
  break;
@@ -21966,6 +22193,11 @@ const DatePickerMixin = (subclass) =>
21966
22193
  return this.$.overlay.content.querySelector('#overlay-content');
21967
22194
  }
21968
22195
 
22196
+ /** @private */
22197
+ __computeMinOrMaxDate(dateString) {
22198
+ return this._parseDate(dateString);
22199
+ }
22200
+
21969
22201
  /**
21970
22202
  * Fired when the user commits a value change.
21971
22203
  *
@@ -22077,12 +22309,13 @@ registerStyles('vaadin-date-picker', [inputFieldShared, datePickerStyles], { mod
22077
22309
  * Note: the `theme` attribute value set on `<vaadin-date-picker>` is
22078
22310
  * propagated to the internal components listed above.
22079
22311
  *
22080
- * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
22312
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
22081
22313
  *
22082
22314
  * @fires {Event} change - Fired when the user commits a value change.
22083
22315
  * @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
22084
22316
  * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
22085
22317
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
22318
+ * @fires {CustomEvent} validated - Fired whenever the field is validated.
22086
22319
  *
22087
22320
  * @extends HTMLElement
22088
22321
  * @mixes ElementMixin
@@ -22136,7 +22369,7 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22136
22369
  fullscreen$="[[_fullscreen]]"
22137
22370
  theme$="[[__getOverlayTheme(_theme, _overlayInitialized)]]"
22138
22371
  on-vaadin-overlay-open="_onOverlayOpened"
22139
- on-vaadin-overlay-close="_onOverlayClosed"
22372
+ on-vaadin-overlay-closing="_onOverlayClosed"
22140
22373
  restore-focus-on-close
22141
22374
  restore-focus-node="[[inputElement]]"
22142
22375
  disable-upgrade
@@ -22196,11 +22429,6 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22196
22429
 
22197
22430
  /** @private */
22198
22431
  _onVaadinOverlayClose(e) {
22199
- if (this._openedWithFocusRing && this.hasAttribute('focused')) {
22200
- this.setAttribute('focus-ring', '');
22201
- } else if (!this.hasAttribute('focused')) {
22202
- this.blur();
22203
- }
22204
22432
  if (e.detail.sourceEvent && e.detail.sourceEvent.composedPath().includes(this)) {
22205
22433
  e.preventDefault();
22206
22434
  }
@@ -22224,13 +22452,14 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22224
22452
 
22225
22453
  customElements.define(DatePicker.is, DatePicker);
22226
22454
 
22227
- const helperFiltersCss = "@import url(\"https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap\");:host{display:block;font-family:\"Roboto\", sans-serif}.FilterButtonsWrapper{display:flex;justify-content:flex-end;gap:5px}.FilterButtonsWrapper .FilterOpen{cursor:pointer;border-radius:4px;padding:8px 15px;width:max-content;border:1px solid #00958f;background:#00958f;color:#FFF;font-size:12px;transition:all 0.2s linear;text-align:center;letter-spacing:0}.FilterButtonsWrapper .FilterOpen:hover{background:#00958f;color:#FFF}.FilterButtonsWrapper .FilterClear{cursor:pointer;border-radius:4px;padding:8px 15px;width:max-content;border:1px solid #00958f;background:#FFF;color:#000;font-size:12px;transition:all 0.2s linear;text-align:center;letter-spacing:0}.FilterButtonsWrapper .FilterClear:hover{background:#00958f;color:#FFF}.FilterModalHeader,.FilterModalBody,.FilterModalFooter{display:flex;flex-direction:column;gap:5px;align-items:center;margin:20px 0}.FilterModalHeader .FilterModalTitle,.FilterModalBody .FilterModalTitle,.FilterModalFooter .FilterModalTitle{margin:0;padding:0;font-weight:700;font-size:16px;color:#009993;text-transform:uppercase}.FilterModalHeader .FilterModalSearch,.FilterModalBody .FilterModalSearch,.FilterModalFooter .FilterModalSearch{border-radius:4px;background:#e8ebef;color:#263445;width:100%;height:26px;max-width:280px;padding:5px;font-size:15px;border:none;outline:#009993}.FilterModalHeader .FilterCalendarWrapper,.FilterModalBody .FilterCalendarWrapper,.FilterModalFooter .FilterCalendarWrapper{display:flex;gap:5px}.FilterModalHeader .FilterCalendarWrapper .VaadinDatePicker,.FilterModalBody .FilterCalendarWrapper .VaadinDatePicker,.FilterModalFooter .FilterCalendarWrapper .VaadinDatePicker{width:50%;max-width:143px}.FilterModalHeader .FilterModalButton,.FilterModalBody .FilterModalButton,.FilterModalFooter .FilterModalButton{cursor:pointer;border-radius:4px;padding:8px 60px;width:max-content;margin:5px;border:1px solid #00958f;background:#00958f;color:#FFF;font-size:12px;transition:all 0.2s linear;text-transform:uppercase;text-align:center;letter-spacing:0}.FilterModalHeader .FilterModalButton:hover,.FilterModalBody .FilterModalButton:hover,.FilterModalFooter .FilterModalButton:hover{background:#00958f;color:#FFF}";
22455
+ const helperFiltersCss = "@import url(\"https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap\");:host{display:block;font-family:\"Roboto\", sans-serif}.FilterButtonsWrapper{display:flex;justify-content:flex-end;gap:5px}.FilterButtonsWrapper .FilterOpen{cursor:pointer;border-radius:4px;padding:8px 15px;width:max-content;border:1px solid #00958f;background:#00958f;color:#FFF;font-size:12px;transition:all 0.2s linear;text-align:center;letter-spacing:0}.FilterButtonsWrapper .FilterOpen:hover{background:#00958f;color:#FFF}.FilterButtonsWrapper .FilterClear{cursor:pointer;border-radius:4px;padding:8px 15px;width:max-content;border:1px solid #00958f;background:#FFF;color:#000;font-size:12px;transition:all 0.2s linear;text-align:center;letter-spacing:0}.FilterButtonsWrapper .FilterClear:hover{background:#00958f;color:#FFF}.FilterModalHeader,.FilterModalBody,.FilterModalFooter{display:flex;flex-direction:column;gap:5px;align-items:center;margin:20px 0}.FilterModalHeader .FilterModalTitle,.FilterModalBody .FilterModalTitle,.FilterModalFooter .FilterModalTitle{margin:0;padding:0;font-weight:700;font-size:16px;color:#009993;text-transform:uppercase}.FilterModalHeader .FilterModalSearch,.FilterModalBody .FilterModalSearch,.FilterModalFooter .FilterModalSearch{border-radius:4px;background:#e8ebef;color:#263445;width:100%;height:26px;max-width:280px;padding:5px;font-size:15px;border:none;outline:#009993}.FilterModalHeader .FilterCalendarWrapper,.FilterModalBody .FilterCalendarWrapper,.FilterModalFooter .FilterCalendarWrapper{display:flex;gap:5px}.FilterModalHeader .FilterCalendarWrapper .VaadinDatePicker,.FilterModalBody .FilterCalendarWrapper .VaadinDatePicker,.FilterModalFooter .FilterCalendarWrapper .VaadinDatePicker{width:50%;max-width:143px}.FilterModalHeader .FilterModalButton,.FilterModalBody .FilterModalButton,.FilterModalFooter .FilterModalButton{cursor:pointer;border-radius:4px;padding:8px 60px;width:max-content;margin:5px;border:1px solid #00958f;background:#00958f;color:#FFF;font-size:12px;transition:all 0.2s linear;text-transform:uppercase;text-align:center;letter-spacing:0}.FilterModalHeader .FilterModalButton:hover,.FilterModalBody .FilterModalButton:hover,.FilterModalFooter .FilterModalButton:hover{background:#00958f;color:#FFF}.FilterModalHeader p,.FilterModalBody p,.FilterModalFooter p{margin:5px 0}";
22228
22456
 
22229
22457
  const HelperFilters$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement$1 {
22230
22458
  constructor() {
22231
22459
  super();
22232
22460
  this.__registerHost();
22233
22461
  this.__attachShadow();
22462
+ this.filterDraw = createEvent(this, "filterDraw", 7);
22234
22463
  this.filterSelection = createEvent(this, "filterSelection", 7);
22235
22464
  this.filterSelectionReset = createEvent(this, "filterSelectionReset", 7);
22236
22465
  /**
@@ -22264,23 +22493,29 @@ const HelperFilters$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLEleme
22264
22493
  this.showFilterModal = false;
22265
22494
  this.showClearButton = false;
22266
22495
  this.filterData = {};
22267
- this.filterDataReset = { drawTicketId: '', filterFromCalendar: '', filterToCalendar: '' };
22496
+ this.filterDataReset = { ticketDrawId: '', filterFromCalendar: '', filterToCalendar: '' };
22497
+ }
22498
+ // reset field values each time the filter modal opens
22499
+ componentDidRender() {
22500
+ this.filterData.ticketDrawId = null;
22501
+ this.filterData.filterFromCalendar = null;
22502
+ this.filterData.filterToCalendar = null;
22503
+ // @TODO: to way binding?
22504
+ if (document.getElementById('#FilterById'))
22505
+ document.getElementById('#FilterById').value = '';
22268
22506
  }
22269
22507
  filterSelectionHandler(event) {
22270
- if (this.postMessage) {
22508
+ if (this.postMessage)
22271
22509
  window.postMessage({ type: 'filterSelection', event }, window.location.href);
22272
- }
22273
- else {
22510
+ if (this.filterData.ticketDrawId)
22511
+ this.filterDraw.emit(event);
22512
+ if (this.filterData.filterFromCalendar || this.filterData.filterToCalendar)
22274
22513
  this.filterSelection.emit(event);
22275
- }
22276
22514
  }
22277
22515
  filterSelectionResetHandler(event) {
22278
- if (this.postMessage) {
22516
+ if (this.postMessage)
22279
22517
  window.postMessage({ type: 'filterSelectionReset', event }, window.location.href);
22280
- }
22281
- else {
22282
- this.filterSelectionReset.emit(event);
22283
- }
22518
+ this.filterSelectionReset.emit(event);
22284
22519
  }
22285
22520
  modalCloseEvent() {
22286
22521
  this.showFilterModal = false;
@@ -22298,9 +22533,10 @@ const HelperFilters$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLEleme
22298
22533
  resetSearch() {
22299
22534
  this.showClearButton = false;
22300
22535
  this.filterSelectionResetHandler(this.filterDataReset);
22536
+ this.filterData = {};
22301
22537
  }
22302
- handleDrawTicketId(event) {
22303
- this.filterData.drawTicketId = event.target.value;
22538
+ handleTicketDrawId(event) {
22539
+ this.filterData.ticketDrawId = event.target.value;
22304
22540
  }
22305
22541
  handleFilterFrom(event) {
22306
22542
  this.filterData.filterFromCalendar = new Date(event.target.value).toISOString();
@@ -22309,7 +22545,7 @@ const HelperFilters$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLEleme
22309
22545
  this.filterData.filterToCalendar = new Date(event.target.value).toISOString();
22310
22546
  }
22311
22547
  render() {
22312
- return (h$2("div", { class: "HelperFilters" }, h$2("div", { class: "FilterButtonsWrapper" }, h$2("button", { class: "FilterOpen", onClick: () => this.toggleFilterModal() }, translate$1('filterOpen', this.language)), this.showClearButton ? h$2("button", { class: "FilterClear", onClick: () => this.resetSearch() }, translate$1('filterClear', this.language)) : null), h$2("helper-modal", { "title-modal": "Filter Modal", visible: this.showFilterModal }, h$2("div", { class: "FilterModalHeader" }, h$2("h3", { class: "FilterModalTitle" }, this.activateTicketSearch ? translate$1('filterModalTicketTitle', this.language) : translate$1('filterModalDrawTitle', this.language))), h$2("div", { class: "FilterModalBody" }, h$2("input", { type: "text", value: this.filterData.drawTicketId, onInput: (event) => this.handleDrawTicketId(event), class: "FilterModalSearch", placeholder: this.activateTicketSearch ? translate$1('filterTicketPlaceholder', this.language) : translate$1('filterDrawPlaceholder', this.language) }), h$2("div", { class: "FilterCalendarWrapper" }, h$2("vaadin-date-picker", { value: this.filterData.filterFromCalendar, onChange: (event) => this.handleFilterFrom(event), placeholder: translate$1('filterFromCalendar', this.language), class: "VaadinDatePicker" }), h$2("vaadin-date-picker", { value: this.filterData.filterToCalendar, onChange: (event) => this.handleFilterTo(event), placeholder: translate$1('filterToCalendar', this.language), class: "VaadinDatePicker" }))), h$2("div", { class: "FilterModalFooter" }, h$2("button", { class: "FilterModalButton", onClick: () => this.filterSearch() }, translate$1('filterModalButton', this.language))))));
22548
+ return (h$2("div", { class: "HelperFilters" }, h$2("div", { class: "FilterButtonsWrapper" }, h$2("button", { class: "FilterOpen", onClick: () => this.toggleFilterModal() }, translate$1('filterOpen', this.language)), this.showClearButton ? h$2("button", { class: "FilterClear", onClick: () => this.resetSearch() }, translate$1('filterClear', this.language)) : null), h$2("helper-modal", { "title-modal": "Filter Modal", visible: this.showFilterModal }, h$2("div", { class: "FilterModalHeader" }, h$2("h3", { class: "FilterModalTitle" }, this.activateTicketSearch ? translate$1('filterModalTicketTitle', this.language) : translate$1('filterModalDrawTitle', this.language))), h$2("div", { class: "FilterModalBody" }, h$2("input", { id: "FilterById", type: "text", value: this.filterData.ticketDrawId, onInput: (event) => this.handleTicketDrawId(event), class: "FilterModalSearch", placeholder: this.activateTicketSearch ? translate$1('filterTicketPlaceholder', this.language) : translate$1('filterDrawPlaceholder', this.language) }), h$2("p", null, translate$1('filterOrDate', this.language)), h$2("div", { class: "FilterCalendarWrapper" }, h$2("vaadin-date-picker", { value: this.filterData.filterFromCalendar, onChange: (event) => this.handleFilterFrom(event), placeholder: translate$1('filterFromCalendar', this.language), class: "VaadinDatePicker" }), h$2("vaadin-date-picker", { value: this.filterData.filterToCalendar, onChange: (event) => this.handleFilterTo(event), placeholder: translate$1('filterToCalendar', this.language), class: "VaadinDatePicker" }))), h$2("div", { class: "FilterModalFooter" }, h$2("button", { class: "FilterModalButton", onClick: () => this.filterSearch() }, translate$1('filterModalButton', this.language))))));
22313
22549
  }
22314
22550
  static get style() { return helperFiltersCss; }
22315
22551
  }, [1, "helper-filters", {