@everymatrix/helper-filters 0.1.24 → 1.15.0

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.
@@ -65,9 +65,16 @@ const translate$1 = (key, customLang) => {
65
65
  * Copyright (c) 2017 - 2022 Vaadin Ltd.
66
66
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
67
67
  */
68
+
69
+ /**
70
+ * Dummy custom element used for collecting
71
+ * development time usage statistics.
72
+ *
73
+ * @private
74
+ */
68
75
  class Lumo extends HTMLElement {
69
76
  static get version() {
70
- return '23.1.5';
77
+ return '23.3.19';
71
78
  }
72
79
  }
73
80
 
@@ -84,20 +91,20 @@ const t$1=window,e$2=t$1.ShadowRoot&&(void 0===t$1.ShadyCSS||t$1.ShadyCSS.native
84
91
  * @license
85
92
  * Copyright 2017 Google LLC
86
93
  * SPDX-License-Identifier: BSD-3-Clause
87
- */var s$2;const e$1=window,r$1=e$1.trustedTypes,h$1=r$1?r$1.emptyScript:"",o$2=e$1.reactiveElementPolyfillSupport,n$2={toAttribute(t,i){switch(i){case Boolean:t=t?h$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},a$1=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:n$2,reflect:!1,hasChanged:a$1};class d$1 extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u();}static addInitializer(t){var i;null!==(i=this.h)&&void 0!==i||(this.h=[]),this.h.push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Ep(s,i);void 0!==e&&(this._$Ev.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty("finalized"))return !1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(c$1(i));}else void 0!==i&&s.push(c$1(i));return s}static _$Ep(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$ES)&&void 0!==i?i:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$ES)||void 0===i||i.splice(this._$ES.indexOf(t)>>>0,1);}_$Eg(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Ei.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return S$1(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$EO(t,i,s=l$2){var e;const r=this.constructor._$Ep(t,s);if(void 0!==r&&!0===s.reflect){const h=(void 0!==(null===(e=s.converter)||void 0===e?void 0:e.toAttribute)?s.converter:n$2).toAttribute(i,s.type);this._$El=t,null==h?this.removeAttribute(r):this.setAttribute(r,h),this._$El=null;}}_$AK(t,i){var s;const e=this.constructor,r=e._$Ev.get(t);if(void 0!==r&&this._$El!==r){const t=e.getPropertyOptions(r),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(s=t.converter)||void 0===s?void 0:s.fromAttribute)?t.converter:n$2;this._$El=r,this[r]=h.fromAttribute(i,t.type),this._$El=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||a$1)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$E_=this._$Ej());}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this._$Ei&&(this._$Ei.forEach(((t,i)=>this[i]=t)),this._$Ei=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$Ek();}catch(t){throw i=!1,this._$Ek(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$ES)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$EO(i,this[i],t))),this._$EC=void 0),this._$Ek();}updated(t){}firstUpdated(t){}}d$1.finalized=!0,d$1.elementProperties=new Map,d$1.elementStyles=[],d$1.shadowRootOptions={mode:"open"},null==o$2||o$2({ReactiveElement:d$1}),(null!==(s$2=e$1.reactiveElementVersions)&&void 0!==s$2?s$2:e$1.reactiveElementVersions=[]).push("1.4.1");
94
+ */var s$2;const e$1=window,r$1=e$1.trustedTypes,h$1=r$1?r$1.emptyScript:"",o$2=e$1.reactiveElementPolyfillSupport,n$2={toAttribute(t,i){switch(i){case Boolean:t=t?h$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},a$1=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:n$2,reflect:!1,hasChanged:a$1},d$1="finalized";class u$1 extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u();}static addInitializer(t){var i;this.finalize(),(null!==(i=this.h)&&void 0!==i?i:this.h=[]).push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Ep(s,i);void 0!==e&&(this._$Ev.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty(d$1))return !1;this[d$1]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(c$1(i));}else void 0!==i&&s.push(c$1(i));return s}static _$Ep(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$ES)&&void 0!==i?i:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$ES)||void 0===i||i.splice(this._$ES.indexOf(t)>>>0,1);}_$Eg(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Ei.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return S$1(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$EO(t,i,s=l$2){var e;const r=this.constructor._$Ep(t,s);if(void 0!==r&&!0===s.reflect){const h=(void 0!==(null===(e=s.converter)||void 0===e?void 0:e.toAttribute)?s.converter:n$2).toAttribute(i,s.type);this._$El=t,null==h?this.removeAttribute(r):this.setAttribute(r,h),this._$El=null;}}_$AK(t,i){var s;const e=this.constructor,r=e._$Ev.get(t);if(void 0!==r&&this._$El!==r){const t=e.getPropertyOptions(r),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(s=t.converter)||void 0===s?void 0:s.fromAttribute)?t.converter:n$2;this._$El=r,this[r]=h.fromAttribute(i,t.type),this._$El=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||a$1)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$E_=this._$Ej());}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this._$Ei&&(this._$Ei.forEach(((t,i)=>this[i]=t)),this._$Ei=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$Ek();}catch(t){throw i=!1,this._$Ek(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$ES)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$EO(i,this[i],t))),this._$EC=void 0),this._$Ek();}updated(t){}firstUpdated(t){}}u$1[d$1]=!0,u$1.elementProperties=new Map,u$1.elementStyles=[],u$1.shadowRootOptions={mode:"open"},null==o$2||o$2({ReactiveElement:u$1}),(null!==(s$2=e$1.reactiveElementVersions)&&void 0!==s$2?s$2:e$1.reactiveElementVersions=[]).push("1.6.2");
88
95
 
89
96
  /**
90
97
  * @license
91
98
  * Copyright 2017 Google LLC
92
99
  * SPDX-License-Identifier: BSD-3-Clause
93
100
  */
94
- var t;const i=window,s$1=i.trustedTypes,e=s$1?s$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,o$1=`lit$${(Math.random()+"").slice(9)}$`,n$1="?"+o$1,l$1=`<${n$1}>`,h=document,r=(t="")=>h.createComment(t),d=t=>null===t||"object"!=typeof t&&"function"!=typeof t,u=Array.isArray,c=t=>u(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]),v=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,a=/-->/g,f=/>/g,_=RegExp(">|[ \t\n\f\r](?:([^\\s\"'>=/]+)([ \t\n\f\r]*=[ \t\n\f\r]*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)","g"),m=/'/g,p=/"/g,$=/^(?:script|style|textarea|title)$/i,x=Symbol.for("lit-noChange"),b=Symbol.for("lit-nothing"),T=new WeakMap,A=(t,i,s)=>{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new S(i.insertBefore(r(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l},E=h.createTreeWalker(h,129,null,!1),C=(t,i)=>{const s=t.length-1,n=[];let h,r=2===i?"<svg>":"",d=v;for(let i=0;i<s;i++){const s=t[i];let e,u,c=-1,g=0;for(;g<s.length&&(d.lastIndex=g,u=d.exec(s),null!==u);)g=d.lastIndex,d===v?"!--"===u[1]?d=a:void 0!==u[1]?d=f:void 0!==u[2]?($.test(u[2])&&(h=RegExp("</"+u[2],"g")),d=_):void 0!==u[3]&&(d=_):d===_?">"===u[0]?(d=null!=h?h:v,c=-1):void 0===u[1]?c=-2:(c=d.lastIndex-u[2].length,e=u[1],d=void 0===u[3]?_:'"'===u[3]?p:m):d===p||d===m?d=_:d===a||d===f?d=v:(d=_,h=void 0);const y=d===_&&t[i+1].startsWith("/>")?" ":"";r+=d===v?s+l$1:c>=0?(n.push(e),s.slice(0,c)+"$lit$"+s.slice(c)+o$1+y):s+o$1+(-2===c?(n.push(void 0),i):y);}const u=r+(t[s]||"<?>")+(2===i?"</svg>":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==e?e.createHTML(u):u,n]};class P{constructor({strings:t,_$litType$:i},e){let l;this.parts=[];let h=0,d=0;const u=t.length-1,c=this.parts,[v,a]=C(t,i);if(this.el=P.createElement(v,e),E.currentNode=this.el.content,2===i){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(l=E.nextNode())&&c.length<u;){if(1===l.nodeType){if(l.hasAttributes()){const t=[];for(const i of l.getAttributeNames())if(i.endsWith("$lit$")||i.startsWith(o$1)){const s=a[d++];if(t.push(i),void 0!==s){const t=l.getAttribute(s.toLowerCase()+"$lit$").split(o$1),i=/([.?@])?(.*)/.exec(s);c.push({type:1,index:h,name:i[2],strings:t,ctor:"."===i[1]?R:"?"===i[1]?H:"@"===i[1]?I:M});}else c.push({type:6,index:h});}for(const i of t)l.removeAttribute(i);}if($.test(l.tagName)){const t=l.textContent.split(o$1),i=t.length-1;if(i>0){l.textContent=s$1?s$1.emptyScript:"";for(let s=0;s<i;s++)l.append(t[s],r()),E.nextNode(),c.push({type:2,index:++h});l.append(t[i],r());}}}else if(8===l.nodeType)if(l.data===n$1)c.push({type:2,index:h});else {let t=-1;for(;-1!==(t=l.data.indexOf(o$1,t+1));)c.push({type:7,index:h}),t+=o$1.length-1;}h++;}}static createElement(t,i){const s=h.createElement("template");return s.innerHTML=t,s}}function V(t,i,s=t,e){var o,n,l,h;if(i===x)return i;let r=void 0!==e?null===(o=s._$Cl)||void 0===o?void 0:o[e]:s._$Cu;const u=d(i)?void 0:i._$litDirective$;return (null==r?void 0:r.constructor)!==u&&(null===(n=null==r?void 0:r._$AO)||void 0===n||n.call(r,!1),void 0===u?r=void 0:(r=new u(t),r._$AT(t,s,e)),void 0!==e?(null!==(l=(h=s)._$Cl)&&void 0!==l?l:h._$Cl=[])[e]=r:s._$Cu=r),void 0!==r&&(i=V(t,r._$AS(t,i.values),r,e)),i}class N{constructor(t,i){this.v=[],this._$AN=void 0,this._$AD=t,this._$AM=i;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}p(t){var i;const{el:{content:s},parts:e}=this._$AD,o=(null!==(i=null==t?void 0:t.creationScope)&&void 0!==i?i:h).importNode(s,!0);E.currentNode=o;let n=E.nextNode(),l=0,r=0,d=e[0];for(;void 0!==d;){if(l===d.index){let i;2===d.type?i=new S(n,n.nextSibling,this,t):1===d.type?i=new d.ctor(n,d.name,d.strings,this,t):6===d.type&&(i=new L(n,this,t)),this.v.push(i),d=e[++r];}l!==(null==d?void 0:d.index)&&(n=E.nextNode(),l++);}return o}m(t){let i=0;for(const s of this.v)void 0!==s&&(void 0!==s.strings?(s._$AI(t,s,i),i+=s.strings.length-2):s._$AI(t[i])),i++;}}class S{constructor(t,i,s,e){var o;this.type=2,this._$AH=b,this._$AN=void 0,this._$AA=t,this._$AB=i,this._$AM=s,this.options=e,this._$C_=null===(o=null==e?void 0:e.isConnected)||void 0===o||o;}get _$AU(){var t,i;return null!==(i=null===(t=this._$AM)||void 0===t?void 0:t._$AU)&&void 0!==i?i:this._$C_}get parentNode(){let t=this._$AA.parentNode;const i=this._$AM;return void 0!==i&&11===t.nodeType&&(t=i.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,i=this){t=V(this,t,i),d(t)?t===b||null==t||""===t?(this._$AH!==b&&this._$AR(),this._$AH=b):t!==this._$AH&&t!==x&&this.$(t):void 0!==t._$litType$?this.T(t):void 0!==t.nodeType?this.k(t):c(t)?this.O(t):this.$(t);}S(t,i=this._$AB){return this._$AA.parentNode.insertBefore(t,i)}k(t){this._$AH!==t&&(this._$AR(),this._$AH=this.S(t));}$(t){this._$AH!==b&&d(this._$AH)?this._$AA.nextSibling.data=t:this.k(h.createTextNode(t)),this._$AH=t;}T(t){var i;const{values:s,_$litType$:e}=t,o="number"==typeof e?this._$AC(t):(void 0===e.el&&(e.el=P.createElement(e.h,this.options)),e);if((null===(i=this._$AH)||void 0===i?void 0:i._$AD)===o)this._$AH.m(s);else {const t=new N(o,this),i=t.p(this.options);t.m(s),this.k(i),this._$AH=t;}}_$AC(t){let i=T.get(t.strings);return void 0===i&&T.set(t.strings,i=new P(t)),i}O(t){u(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,e=0;for(const o of t)e===i.length?i.push(s=new S(this.S(r()),this.S(r()),this,this.options)):s=i[e],s._$AI(o),e++;e<i.length&&(this._$AR(s&&s._$AB.nextSibling,e),i.length=e);}_$AR(t=this._$AA.nextSibling,i){var s;for(null===(s=this._$AP)||void 0===s||s.call(this,!1,!0,i);t&&t!==this._$AB;){const i=t.nextSibling;t.remove(),t=i;}}setConnected(t){var i;void 0===this._$AM&&(this._$C_=t,null===(i=this._$AP)||void 0===i||i.call(this,t));}}class M{constructor(t,i,s,e,o){this.type=1,this._$AH=b,this._$AN=void 0,this.element=t,this.name=i,this._$AM=e,this.options=o,s.length>2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=b;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=V(this,t,i,0),n=!d(t)||t!==this._$AH&&t!==x,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;l<o.length-1;l++)h=V(this,e[s+l],i,l),h===x&&(h=this._$AH[l]),n||(n=!d(h)||h!==this._$AH[l]),h===b?t=b:t!==b&&(t+=(null!=h?h:"")+o[l+1]),this._$AH[l]=h;}n&&!e&&this.P(t);}P(t){t===b?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,null!=t?t:"");}}class R extends M{constructor(){super(...arguments),this.type=3;}P(t){this.element[this.name]=t===b?void 0:t;}}const k=s$1?s$1.emptyScript:"";class H extends M{constructor(){super(...arguments),this.type=4;}P(t){t&&t!==b?this.element.setAttribute(this.name,k):this.element.removeAttribute(this.name);}}class I extends M{constructor(t,i,s,e,o){super(t,i,s,e,o),this.type=5;}_$AI(t,i=this){var s;if((t=null!==(s=V(this,t,i,0))&&void 0!==s?s:b)===x)return;const e=this._$AH,o=t===b&&e!==b||t.capture!==e.capture||t.once!==e.once||t.passive!==e.passive,n=t!==b&&(e===b||o);o&&this.element.removeEventListener(this.name,this,e),n&&this.element.addEventListener(this.name,this,t),this._$AH=t;}handleEvent(t){var i,s;"function"==typeof this._$AH?this._$AH.call(null!==(s=null===(i=this.options)||void 0===i?void 0:i.host)&&void 0!==s?s:this.element,t):this._$AH.handleEvent(t);}}class L{constructor(t,i,s){this.element=t,this.type=6,this._$AN=void 0,this._$AM=i,this.options=s;}get _$AU(){return this._$AM._$AU}_$AI(t){V(this,t);}}const Z=i.litHtmlPolyfillSupport;null==Z||Z(P,S),(null!==(t=i.litHtmlVersions)&&void 0!==t?t:i.litHtmlVersions=[]).push("2.3.1");
101
+ var t;const i=window,s$1=i.trustedTypes,e=s$1?s$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,o$1="$lit$",n$1=`lit$${(Math.random()+"").slice(9)}$`,l$1="?"+n$1,h=`<${l$1}>`,r=document,d=()=>r.createComment(""),u=t=>null===t||"object"!=typeof t&&"function"!=typeof t,c=Array.isArray,v=t=>c(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]),a="[ \t\n\f\r]",f=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_=/-->/g,m=/>/g,p=RegExp(`>|${a}(?:([^\\s"'>=/]+)(${a}*=${a}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),g=/'/g,$=/"/g,y=/^(?:script|style|textarea|title)$/i,T=Symbol.for("lit-noChange"),A=Symbol.for("lit-nothing"),E=new WeakMap,C=r.createTreeWalker(r,129,null,!1),P=(t,i)=>{const s=t.length-1,l=[];let r,d=2===i?"<svg>":"",u=f;for(let i=0;i<s;i++){const s=t[i];let e,c,v=-1,a=0;for(;a<s.length&&(u.lastIndex=a,c=u.exec(s),null!==c);)a=u.lastIndex,u===f?"!--"===c[1]?u=_:void 0!==c[1]?u=m:void 0!==c[2]?(y.test(c[2])&&(r=RegExp("</"+c[2],"g")),u=p):void 0!==c[3]&&(u=p):u===p?">"===c[0]?(u=null!=r?r:f,v=-1):void 0===c[1]?v=-2:(v=u.lastIndex-c[2].length,e=c[1],u=void 0===c[3]?p:'"'===c[3]?$:g):u===$||u===g?u=p:u===_||u===m?u=f:(u=p,r=void 0);const w=u===p&&t[i+1].startsWith("/>")?" ":"";d+=u===f?s+h:v>=0?(l.push(e),s.slice(0,v)+o$1+s.slice(v)+n$1+w):s+n$1+(-2===v?(l.push(void 0),i):w);}const c=d+(t[s]||"<?>")+(2===i?"</svg>":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==e?e.createHTML(c):c,l]};class V{constructor({strings:t,_$litType$:i},e){let h;this.parts=[];let r=0,u=0;const c=t.length-1,v=this.parts,[a,f]=P(t,i);if(this.el=V.createElement(a,e),C.currentNode=this.el.content,2===i){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(h=C.nextNode())&&v.length<c;){if(1===h.nodeType){if(h.hasAttributes()){const t=[];for(const i of h.getAttributeNames())if(i.endsWith(o$1)||i.startsWith(n$1)){const s=f[u++];if(t.push(i),void 0!==s){const t=h.getAttribute(s.toLowerCase()+o$1).split(n$1),i=/([.?@])?(.*)/.exec(s);v.push({type:1,index:r,name:i[2],strings:t,ctor:"."===i[1]?k:"?"===i[1]?I:"@"===i[1]?L:R});}else v.push({type:6,index:r});}for(const i of t)h.removeAttribute(i);}if(y.test(h.tagName)){const t=h.textContent.split(n$1),i=t.length-1;if(i>0){h.textContent=s$1?s$1.emptyScript:"";for(let s=0;s<i;s++)h.append(t[s],d()),C.nextNode(),v.push({type:2,index:++r});h.append(t[i],d());}}}else if(8===h.nodeType)if(h.data===l$1)v.push({type:2,index:r});else {let t=-1;for(;-1!==(t=h.data.indexOf(n$1,t+1));)v.push({type:7,index:r}),t+=n$1.length-1;}r++;}}static createElement(t,i){const s=r.createElement("template");return s.innerHTML=t,s}}function N(t,i,s=t,e){var o,n,l,h;if(i===T)return i;let r=void 0!==e?null===(o=s._$Co)||void 0===o?void 0:o[e]:s._$Cl;const d=u(i)?void 0:i._$litDirective$;return (null==r?void 0:r.constructor)!==d&&(null===(n=null==r?void 0:r._$AO)||void 0===n||n.call(r,!1),void 0===d?r=void 0:(r=new d(t),r._$AT(t,s,e)),void 0!==e?(null!==(l=(h=s)._$Co)&&void 0!==l?l:h._$Co=[])[e]=r:s._$Cl=r),void 0!==r&&(i=N(t,r._$AS(t,i.values),r,e)),i}class S{constructor(t,i){this._$AV=[],this._$AN=void 0,this._$AD=t,this._$AM=i;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(t){var i;const{el:{content:s},parts:e}=this._$AD,o=(null!==(i=null==t?void 0:t.creationScope)&&void 0!==i?i:r).importNode(s,!0);C.currentNode=o;let n=C.nextNode(),l=0,h=0,d=e[0];for(;void 0!==d;){if(l===d.index){let i;2===d.type?i=new M(n,n.nextSibling,this,t):1===d.type?i=new d.ctor(n,d.name,d.strings,this,t):6===d.type&&(i=new z(n,this,t)),this._$AV.push(i),d=e[++h];}l!==(null==d?void 0:d.index)&&(n=C.nextNode(),l++);}return C.currentNode=r,o}v(t){let i=0;for(const s of this._$AV)void 0!==s&&(void 0!==s.strings?(s._$AI(t,s,i),i+=s.strings.length-2):s._$AI(t[i])),i++;}}class M{constructor(t,i,s,e){var o;this.type=2,this._$AH=A,this._$AN=void 0,this._$AA=t,this._$AB=i,this._$AM=s,this.options=e,this._$Cp=null===(o=null==e?void 0:e.isConnected)||void 0===o||o;}get _$AU(){var t,i;return null!==(i=null===(t=this._$AM)||void 0===t?void 0:t._$AU)&&void 0!==i?i:this._$Cp}get parentNode(){let t=this._$AA.parentNode;const i=this._$AM;return void 0!==i&&11===(null==t?void 0:t.nodeType)&&(t=i.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,i=this){t=N(this,t,i),u(t)?t===A||null==t||""===t?(this._$AH!==A&&this._$AR(),this._$AH=A):t!==this._$AH&&t!==T&&this._(t):void 0!==t._$litType$?this.g(t):void 0!==t.nodeType?this.$(t):v(t)?this.T(t):this._(t);}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t));}_(t){this._$AH!==A&&u(this._$AH)?this._$AA.nextSibling.data=t:this.$(r.createTextNode(t)),this._$AH=t;}g(t){var i;const{values:s,_$litType$:e}=t,o="number"==typeof e?this._$AC(t):(void 0===e.el&&(e.el=V.createElement(e.h,this.options)),e);if((null===(i=this._$AH)||void 0===i?void 0:i._$AD)===o)this._$AH.v(s);else {const t=new S(o,this),i=t.u(this.options);t.v(s),this.$(i),this._$AH=t;}}_$AC(t){let i=E.get(t.strings);return void 0===i&&E.set(t.strings,i=new V(t)),i}T(t){c(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,e=0;for(const o of t)e===i.length?i.push(s=new M(this.k(d()),this.k(d()),this,this.options)):s=i[e],s._$AI(o),e++;e<i.length&&(this._$AR(s&&s._$AB.nextSibling,e),i.length=e);}_$AR(t=this._$AA.nextSibling,i){var s;for(null===(s=this._$AP)||void 0===s||s.call(this,!1,!0,i);t&&t!==this._$AB;){const i=t.nextSibling;t.remove(),t=i;}}setConnected(t){var i;void 0===this._$AM&&(this._$Cp=t,null===(i=this._$AP)||void 0===i||i.call(this,t));}}class R{constructor(t,i,s,e,o){this.type=1,this._$AH=A,this._$AN=void 0,this.element=t,this.name=i,this._$AM=e,this.options=o,s.length>2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=A;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=N(this,t,i,0),n=!u(t)||t!==this._$AH&&t!==T,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;l<o.length-1;l++)h=N(this,e[s+l],i,l),h===T&&(h=this._$AH[l]),n||(n=!u(h)||h!==this._$AH[l]),h===A?t=A:t!==A&&(t+=(null!=h?h:"")+o[l+1]),this._$AH[l]=h;}n&&!e&&this.j(t);}j(t){t===A?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,null!=t?t:"");}}class k extends R{constructor(){super(...arguments),this.type=3;}j(t){this.element[this.name]=t===A?void 0:t;}}const H=s$1?s$1.emptyScript:"";class I extends R{constructor(){super(...arguments),this.type=4;}j(t){t&&t!==A?this.element.setAttribute(this.name,H):this.element.removeAttribute(this.name);}}class L extends R{constructor(t,i,s,e,o){super(t,i,s,e,o),this.type=5;}_$AI(t,i=this){var s;if((t=null!==(s=N(this,t,i,0))&&void 0!==s?s:A)===T)return;const e=this._$AH,o=t===A&&e!==A||t.capture!==e.capture||t.once!==e.once||t.passive!==e.passive,n=t!==A&&(e===A||o);o&&this.element.removeEventListener(this.name,this,e),n&&this.element.addEventListener(this.name,this,t),this._$AH=t;}handleEvent(t){var i,s;"function"==typeof this._$AH?this._$AH.call(null!==(s=null===(i=this.options)||void 0===i?void 0:i.host)&&void 0!==s?s:this.element,t):this._$AH.handleEvent(t);}}class z{constructor(t,i,s){this.element=t,this.type=6,this._$AN=void 0,this._$AM=i,this.options=s;}get _$AU(){return this._$AM._$AU}_$AI(t){N(this,t);}}const j=i.litHtmlPolyfillSupport;null==j||j(V,M),(null!==(t=i.litHtmlVersions)&&void 0!==t?t:i.litHtmlVersions=[]).push("2.7.4");const B=(t,i,s)=>{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new M(i.insertBefore(d(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l};
95
102
 
96
103
  /**
97
104
  * @license
98
105
  * Copyright 2017 Google LLC
99
106
  * SPDX-License-Identifier: BSD-3-Clause
100
- */var l,o;class s extends d$1{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t,e;const i=super.createRenderRoot();return null!==(t=(e=this.renderOptions).renderBefore)&&void 0!==t||(e.renderBefore=i.firstChild),i}update(t){const i=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=A(i,this.renderRoot,this.renderOptions);}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0);}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1);}render(){return x}}s.finalized=!0,s._$litElement$=!0,null===(l=globalThis.litElementHydrateSupport)||void 0===l||l.call(globalThis,{LitElement:s});const n=globalThis.litElementPolyfillSupport;null==n||n({LitElement:s});(null!==(o=globalThis.litElementVersions)&&void 0!==o?o:globalThis.litElementVersions=[]).push("3.2.2");
107
+ */var l,o;class s extends u$1{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t,e;const i=super.createRenderRoot();return null!==(t=(e=this.renderOptions).renderBefore)&&void 0!==t||(e.renderBefore=i.firstChild),i}update(t){const i=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=B(i,this.renderRoot,this.renderOptions);}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0);}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1);}render(){return T}}s.finalized=!0,s._$litElement$=!0,null===(l=globalThis.litElementHydrateSupport)||void 0===l||l.call(globalThis,{LitElement:s});const n=globalThis.litElementPolyfillSupport;null==n||n({LitElement:s});(null!==(o=globalThis.litElementVersions)&&void 0!==o?o:globalThis.litElementVersions=[]).push("3.3.2");
101
108
 
102
109
  /**
103
110
  * @license
@@ -123,7 +130,7 @@ const ThemePropertyMixin = (superClass) =>
123
130
  * **NOTE:** Extending the mixin only provides the property for binding,
124
131
  * and does not make the propagation alone.
125
132
  *
126
- * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/ds/customization/styling-components/#sub-components).
133
+ * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components/#sub-components).
127
134
  * page for more information.
128
135
  *
129
136
  * @deprecated The `theme` property is not supposed for public use and will be dropped in Vaadin 24.
@@ -148,7 +155,7 @@ const ThemePropertyMixin = (superClass) =>
148
155
  * **NOTE:** Extending the mixin only provides the property for binding,
149
156
  * and does not make the propagation alone.
150
157
  *
151
- * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/ds/customization/styling-components/#sub-components).
158
+ * See [Styling Components: Sub-components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components/#sub-components).
152
159
  * page for more information.
153
160
  *
154
161
  * @protected
@@ -253,9 +260,9 @@ function matchesThemeFor(themeFor, tagName) {
253
260
  */
254
261
  function getIncludePriority(moduleName = '') {
255
262
  let includePriority = 0;
256
- if (moduleName.indexOf('lumo-') === 0 || moduleName.indexOf('material-') === 0) {
263
+ if (moduleName.startsWith('lumo-') || moduleName.startsWith('material-')) {
257
264
  includePriority = 1;
258
- } else if (moduleName.indexOf('vaadin-') === 0) {
265
+ } else if (moduleName.startsWith('vaadin-')) {
259
266
  includePriority = 2;
260
267
  }
261
268
  return includePriority;
@@ -489,9 +496,9 @@ const colorBase = i$1`
489
496
  }
490
497
  `;
491
498
 
492
- const $tpl$5 = document.createElement('template');
493
- $tpl$5.innerHTML = `<style>${colorBase.toString().replace(':host', 'html')}</style>`;
494
- document.head.appendChild($tpl$5.content);
499
+ const $tpl$4 = document.createElement('template');
500
+ $tpl$4.innerHTML = `<style>${colorBase.toString().replace(':host', 'html')}</style>`;
501
+ document.head.appendChild($tpl$4.content);
495
502
 
496
503
  const color = i$1`
497
504
  [theme~='dark'] {
@@ -638,9 +645,9 @@ const sizing = i$1`
638
645
  }
639
646
  `;
640
647
 
641
- const $tpl$4 = document.createElement('template');
642
- $tpl$4.innerHTML = `<style>${sizing.toString().replace(':host', 'html')}</style>`;
643
- document.head.appendChild($tpl$4.content);
648
+ const $tpl$3 = document.createElement('template');
649
+ $tpl$3.innerHTML = `<style>${sizing.toString().replace(':host', 'html')}</style>`;
650
+ document.head.appendChild($tpl$3.content);
644
651
 
645
652
  /**
646
653
  * @license
@@ -668,9 +675,19 @@ const style = i$1`
668
675
  }
669
676
  `;
670
677
 
671
- const $tpl$3 = document.createElement('template');
672
- $tpl$3.innerHTML = `<style>${style.toString().replace(':host', 'html')}</style>`;
673
- document.head.appendChild($tpl$3.content);
678
+ /**
679
+ * Default values for component-specific custom properties.
680
+ */
681
+ i$1`
682
+ html {
683
+ --vaadin-checkbox-size: calc(var(--lumo-size-m) / 2);
684
+ --vaadin-radio-button-size: calc(var(--lumo-size-m) / 2);
685
+ }
686
+ `;
687
+
688
+ const $tpl$2 = document.createElement('template');
689
+ $tpl$2.innerHTML = `<style>${style.toString().replace(':host', 'html')}$</style>`;
690
+ document.head.appendChild($tpl$2.content);
674
691
 
675
692
  /**
676
693
  * @license
@@ -807,9 +824,9 @@ const typography = i$1`
807
824
 
808
825
  registerStyles('', typography, { moduleId: 'lumo-typography' });
809
826
 
810
- const $tpl$2 = document.createElement('template');
811
- $tpl$2.innerHTML = `<style>${font.toString().replace(':host', 'html')}</style>`;
812
- document.head.appendChild($tpl$2.content);
827
+ const $tpl$1 = document.createElement('template');
828
+ $tpl$1.innerHTML = `<style>${font.toString().replace(':host', 'html')}</style>`;
829
+ document.head.appendChild($tpl$1.content);
813
830
 
814
831
  registerStyles(
815
832
  'vaadin-input-container',
@@ -8101,6 +8118,7 @@ class DirHelper {
8101
8118
  * Array of Vaadin custom element classes that have been subscribed to the dir changes.
8102
8119
  */
8103
8120
  const directionSubscribers = [];
8121
+
8104
8122
  function directionUpdater() {
8105
8123
  const documentDir = getDocumentDir();
8106
8124
  directionSubscribers.forEach((element) => {
@@ -8166,7 +8184,7 @@ const DirMixin = (superClass) =>
8166
8184
  connectedCallback() {
8167
8185
  super.connectedCallback();
8168
8186
 
8169
- if (!this.hasAttribute('dir')) {
8187
+ if (!this.hasAttribute('dir') || this.__restoreSubscription) {
8170
8188
  this.__subscribe();
8171
8189
  alignDirs(this, getDocumentDir(), null);
8172
8190
  }
@@ -8192,15 +8210,15 @@ const DirMixin = (superClass) =>
8192
8210
  this.__subscribe();
8193
8211
  alignDirs(this, documentDir, newValue);
8194
8212
  } else if (newDiffValue) {
8195
- this.__subscribe(false);
8213
+ this.__unsubscribe();
8196
8214
  }
8197
8215
  }
8198
8216
 
8199
8217
  /** @protected */
8200
8218
  disconnectedCallback() {
8201
8219
  super.disconnectedCallback();
8202
- this.__subscribe(false);
8203
- this.removeAttribute('dir');
8220
+ this.__restoreSubscription = directionSubscribers.includes(this);
8221
+ this.__unsubscribe();
8204
8222
  }
8205
8223
 
8206
8224
  /** @protected */
@@ -8225,12 +8243,15 @@ const DirMixin = (superClass) =>
8225
8243
  }
8226
8244
 
8227
8245
  /** @private */
8228
- __subscribe(push = true) {
8229
- if (push) {
8230
- if (!directionSubscribers.includes(this)) {
8231
- directionSubscribers.push(this);
8232
- }
8233
- } else if (directionSubscribers.includes(this)) {
8246
+ __subscribe() {
8247
+ if (!directionSubscribers.includes(this)) {
8248
+ directionSubscribers.push(this);
8249
+ }
8250
+ }
8251
+
8252
+ /** @private */
8253
+ __unsubscribe() {
8254
+ if (directionSubscribers.includes(this)) {
8234
8255
  directionSubscribers.splice(directionSubscribers.indexOf(this), 1);
8235
8256
  }
8236
8257
  }
@@ -8308,7 +8329,6 @@ class InputContainer extends ThemableMixin(DirMixin(PolymerElement)) {
8308
8329
  ::slotted(:is(input, textarea))::placeholder {
8309
8330
  /* Use ::slotted(input:placeholder-shown) in themes to style the placeholder. */
8310
8331
  /* because ::slotted(...)::placeholder does not work in Safari. */
8311
- /* See the workaround at the end of this file. */
8312
8332
  font: inherit;
8313
8333
  color: inherit;
8314
8334
  /* Override default opacity in Firefox */
@@ -8375,18 +8395,6 @@ class InputContainer extends ThemableMixin(DirMixin(PolymerElement)) {
8375
8395
 
8376
8396
  customElements.define(InputContainer.is, InputContainer);
8377
8397
 
8378
- const placeholderStyleWorkaround = i$1`
8379
- /* Needed for Safari, where ::slotted(...)::placeholder does not work */
8380
- :is(input[slot='input'], textarea[slot='textarea'])::placeholder {
8381
- font: inherit;
8382
- color: inherit;
8383
- }
8384
- `;
8385
-
8386
- const $tpl$1 = document.createElement('template');
8387
- $tpl$1.innerHTML = `<style>${placeholderStyleWorkaround.toString()}</style>`;
8388
- document.head.appendChild($tpl$1.content);
8389
-
8390
8398
  /**
8391
8399
  * @license
8392
8400
  * Copyright (c) 2017 - 2022 Vaadin Ltd.
@@ -10049,7 +10057,7 @@ const isIOS = isIPhone || isIPad;
10049
10057
 
10050
10058
  testUserAgent(/^((?!chrome|android).)*safari/i);
10051
10059
 
10052
- (() => {
10060
+ const isTouch = (() => {
10053
10061
  try {
10054
10062
  document.createEvent('TouchEvent');
10055
10063
  return true;
@@ -10140,6 +10148,38 @@ const ControllerMixin = dedupingMixin(
10140
10148
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
10141
10149
  */
10142
10150
 
10151
+ // We consider the keyboard to be active if the window has received a keydown
10152
+ // event since the last mousedown event.
10153
+ let keyboardActive = false;
10154
+
10155
+ // Listen for top-level keydown and mousedown events.
10156
+ // Use capture phase so we detect events even if they're handled.
10157
+ window.addEventListener(
10158
+ 'keydown',
10159
+ () => {
10160
+ keyboardActive = true;
10161
+ },
10162
+ { capture: true },
10163
+ );
10164
+
10165
+ window.addEventListener(
10166
+ 'mousedown',
10167
+ () => {
10168
+ keyboardActive = false;
10169
+ },
10170
+ { capture: true },
10171
+ );
10172
+
10173
+ /**
10174
+ * Returns true if the window has received a keydown
10175
+ * event since the last mousedown event.
10176
+ *
10177
+ * @return {boolean}
10178
+ */
10179
+ function isKeyboardActive() {
10180
+ return keyboardActive;
10181
+ }
10182
+
10143
10183
  /**
10144
10184
  * Returns true if the element is hidden directly with `display: none` or `visibility: hidden`,
10145
10185
  * false otherwise.
@@ -10580,7 +10620,7 @@ class FocusTrapController {
10580
10620
  * ---|---|---
10581
10621
  * `--vaadin-overlay-viewport-bottom` | Bottom offset of the visible viewport area | `0` or detected offset
10582
10622
  *
10583
- * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
10623
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
10584
10624
  *
10585
10625
  * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
10586
10626
  * @fires {CustomEvent} vaadin-overlay-open - Fired after the overlay is opened.
@@ -10594,7 +10634,7 @@ class FocusTrapController {
10594
10634
  * @mixes DirMixin
10595
10635
  * @mixes ControllerMixin
10596
10636
  */
10597
- class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerElement))) {
10637
+ class Overlay extends ThemableMixin(DirMixin(ControllerMixin(PolymerElement))) {
10598
10638
  static get template() {
10599
10639
  return html`
10600
10640
  <style>
@@ -10894,7 +10934,7 @@ class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerEleme
10894
10934
  * @protected
10895
10935
  */
10896
10936
  _setTemplateFromNodes(nodes) {
10897
- this.template = nodes.filter((node) => node.localName && node.localName === 'template')[0] || this.template;
10937
+ this.template = nodes.find((node) => node.localName && node.localName === 'template') || this.template;
10898
10938
  }
10899
10939
 
10900
10940
  /**
@@ -11221,7 +11261,7 @@ class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerEleme
11221
11261
  */
11222
11262
  static get __attachedInstances() {
11223
11263
  return Array.from(document.body.children)
11224
- .filter((el) => el instanceof OverlayElement && !el.hasAttribute('closing'))
11264
+ .filter((el) => el instanceof Overlay && !el.hasAttribute('closing'))
11225
11265
  .sort((a, b) => a.__zIndex - b.__zIndex || 0);
11226
11266
  }
11227
11267
 
@@ -11231,7 +11271,7 @@ class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerEleme
11231
11271
  * @protected
11232
11272
  */
11233
11273
  get _last() {
11234
- return this === OverlayElement.__attachedInstances.pop();
11274
+ return this === Overlay.__attachedInstances.pop();
11235
11275
  }
11236
11276
 
11237
11277
  /** @private */
@@ -11266,7 +11306,7 @@ class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerEleme
11266
11306
  }
11267
11307
 
11268
11308
  // Disable pointer events in other attached overlays
11269
- OverlayElement.__attachedInstances.forEach((el) => {
11309
+ Overlay.__attachedInstances.forEach((el) => {
11270
11310
  if (el !== this) {
11271
11311
  el.shadowRoot.querySelector('[part="overlay"]').style.pointerEvents = 'none';
11272
11312
  }
@@ -11289,7 +11329,7 @@ class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerEleme
11289
11329
  }
11290
11330
 
11291
11331
  // Restore pointer events in the previous overlay(s)
11292
- const instances = OverlayElement.__attachedInstances;
11332
+ const instances = Overlay.__attachedInstances;
11293
11333
  let el;
11294
11334
  // Use instances.pop() to ensure the reverse order
11295
11335
  while ((el = instances.pop())) {
@@ -11468,7 +11508,7 @@ class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerEleme
11468
11508
  */
11469
11509
  bringToFront() {
11470
11510
  let zIndex = '';
11471
- const frontmost = OverlayElement.__attachedInstances.filter((o) => o !== this).pop();
11511
+ const frontmost = Overlay.__attachedInstances.filter((o) => o !== this).pop();
11472
11512
  if (frontmost) {
11473
11513
  const frontmostZIndex = frontmost.__zIndex;
11474
11514
  zIndex = frontmostZIndex + 1;
@@ -11478,7 +11518,7 @@ class OverlayElement extends ThemableMixin(DirMixin(ControllerMixin(PolymerEleme
11478
11518
  }
11479
11519
  }
11480
11520
 
11481
- customElements.define(OverlayElement.is, OverlayElement);
11521
+ customElements.define(Overlay.is, Overlay);
11482
11522
 
11483
11523
  /**
11484
11524
  * @license
@@ -11664,7 +11704,7 @@ const button = i$1`
11664
11704
  -moz-osx-font-smoothing: grayscale;
11665
11705
  }
11666
11706
 
11667
- /* Set only for the internal parts so we dont affect the host vertical alignment */
11707
+ /* Set only for the internal parts so we don't affect the host vertical alignment */
11668
11708
  [part='label'],
11669
11709
  [part='prefix'],
11670
11710
  [part='suffix'] {
@@ -11900,7 +11940,7 @@ const button = i$1`
11900
11940
  registerStyles('vaadin-button', button, { moduleId: 'lumo-button' });
11901
11941
 
11902
11942
  const DEV_MODE_CODE_REGEXP =
11903
- /\/\*\*\s+vaadin-dev-mode:start([\s\S]*)vaadin-dev-mode:end\s+\*\*\//i;
11943
+ /\/\*[\*!]\s+vaadin-dev-mode:start([\s\S]*)vaadin-dev-mode:end\s+\*\*\//i;
11904
11944
 
11905
11945
  const FlowClients = window.Vaadin && window.Vaadin.Flow && window.Vaadin.Flow.clients;
11906
11946
 
@@ -12850,7 +12890,7 @@ const registered = new Set();
12850
12890
  const ElementMixin = (superClass) =>
12851
12891
  class VaadinElementMixin extends DirMixin(superClass) {
12852
12892
  static get version() {
12853
- return '23.1.5';
12893
+ return '23.3.19';
12854
12894
  }
12855
12895
 
12856
12896
  /** @protected */
@@ -12885,174 +12925,553 @@ const ElementMixin = (superClass) =>
12885
12925
  };
12886
12926
 
12887
12927
  /**
12888
- @license
12889
- Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
12890
- This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
12891
- The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
12892
- The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
12893
- Code distributed by Google as part of the polymer project is also
12894
- subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
12895
- */
12896
-
12897
- const passiveTouchGestures = false;
12898
- const wrap = (node) => node;
12899
-
12900
- // Detect native touch action support
12901
- const HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string';
12902
- const GESTURE_KEY = '__polymerGestures';
12903
- const HANDLED_OBJ = '__polymerGesturesHandled';
12904
- const TOUCH_ACTION = '__polymerGesturesTouchAction';
12905
- // Radius for tap and track
12906
- const TAP_DISTANCE = 25;
12907
- const TRACK_DISTANCE = 5;
12908
- // Number of last N track positions to keep
12909
- const TRACK_LENGTH = 2;
12928
+ * @license
12929
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
12930
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
12931
+ */
12910
12932
 
12911
- const MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'click'];
12912
- // An array of bitmask values for mapping MouseEvent.which to MouseEvent.buttons
12913
- const MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2];
12914
- const MOUSE_HAS_BUTTONS = (function () {
12915
- try {
12916
- return new MouseEvent('test', { buttons: 1 }).buttons === 1;
12917
- } catch (e) {
12918
- return false;
12919
- }
12920
- })();
12933
+ let uniqueId = 0;
12921
12934
 
12922
12935
  /**
12923
- * @param {string} name Possible mouse event name
12924
- * @return {boolean} true if mouse event, false if not
12936
+ * Returns a unique integer id.
12937
+ *
12938
+ * @return {number}
12925
12939
  */
12926
- function isMouseEvent(name) {
12927
- return MOUSE_EVENTS.indexOf(name) > -1;
12940
+ function generateUniqueId() {
12941
+ // eslint-disable-next-line no-plusplus
12942
+ return uniqueId++;
12928
12943
  }
12929
12944
 
12930
- /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
12931
- // check for passive event listeners
12932
- let supportsPassive = false;
12933
- (function () {
12934
- try {
12935
- const opts = Object.defineProperty({}, 'passive', {
12936
- // eslint-disable-next-line getter-return
12937
- get() {
12938
- supportsPassive = true;
12939
- },
12940
- });
12941
- window.addEventListener('test', null, opts);
12942
- window.removeEventListener('test', null, opts);
12943
- } catch (e) {}
12944
- })();
12945
+ /**
12946
+ * @license
12947
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
12948
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
12949
+ */
12945
12950
 
12946
12951
  /**
12947
- * Generate settings for event listeners, dependant on `passiveTouchGestures`
12948
- *
12949
- * @param {string} eventName Event name to determine if `{passive}` option is
12950
- * needed
12951
- * @return {{passive: boolean} | undefined} Options to use for addEventListener
12952
- * and removeEventListener
12952
+ * A controller for providing content to slot element and observing changes.
12953
12953
  */
12954
- function PASSIVE_TOUCH(eventName) {
12955
- if (isMouseEvent(eventName) || eventName === 'touchend') {
12956
- return;
12957
- }
12958
- if (HAS_NATIVE_TA && supportsPassive && passiveTouchGestures) {
12959
- return { passive: true };
12954
+ class SlotController extends EventTarget {
12955
+ /**
12956
+ * Ensure that every instance has unique ID.
12957
+ *
12958
+ * @param {string} slotName
12959
+ * @param {HTMLElement} host
12960
+ * @return {string}
12961
+ * @protected
12962
+ */
12963
+ static generateId(slotName, host) {
12964
+ const prefix = slotName || 'default';
12965
+ return `${prefix}-${host.localName}-${generateUniqueId()}`;
12960
12966
  }
12961
- }
12962
12967
 
12963
- // Check for touch-only devices
12964
- const IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
12968
+ constructor(host, slotName, slotFactory, slotInitializer, useUniqueId) {
12969
+ super();
12965
12970
 
12966
- // Defined at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute
12967
- /** @type {!Object<boolean>} */
12968
- const canBeDisabled = {
12969
- button: true,
12970
- command: true,
12971
- fieldset: true,
12972
- input: true,
12973
- keygen: true,
12974
- optgroup: true,
12975
- option: true,
12976
- select: true,
12977
- textarea: true,
12978
- };
12971
+ this.host = host;
12972
+ this.slotName = slotName;
12973
+ this.slotFactory = slotFactory;
12974
+ this.slotInitializer = slotInitializer;
12979
12975
 
12980
- /**
12981
- * @param {MouseEvent} ev event to test for left mouse button down
12982
- * @return {boolean} has left mouse button down
12983
- */
12984
- function hasLeftMouseButton(ev) {
12985
- const type = ev.type;
12986
- // Exit early if the event is not a mouse event
12987
- if (!isMouseEvent(type)) {
12988
- return false;
12989
- }
12990
- // Ev.button is not reliable for mousemove (0 is overloaded as both left button and no buttons)
12991
- // instead we use ev.buttons (bitmask of buttons) or fall back to ev.which (deprecated, 0 for no buttons, 1 for left button)
12992
- if (type === 'mousemove') {
12993
- // Allow undefined for testing events
12994
- let buttons = ev.buttons === undefined ? 1 : ev.buttons;
12995
- if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) {
12996
- buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
12976
+ // Only generate the default ID if requested by the controller.
12977
+ if (useUniqueId) {
12978
+ this.defaultId = SlotController.generateId(slotName, host);
12997
12979
  }
12998
- // Buttons is a bitmask, check that the left button bit is set (1)
12999
- return Boolean(buttons & 1);
13000
12980
  }
13001
- // Allow undefined for testing events
13002
- const button = ev.button === undefined ? 0 : ev.button;
13003
- // Ev.button is 0 in mousedown/mouseup/click for left button activation
13004
- return button === 0;
13005
- }
13006
12981
 
13007
- function isSyntheticClick(ev) {
13008
- if (ev.type === 'click') {
13009
- // Ev.detail is 0 for HTMLElement.click in most browsers
13010
- if (ev.detail === 0) {
13011
- return true;
13012
- }
13013
- // In the worst case, check that the x/y position of the click is within
13014
- // the bounding box of the target of the event
13015
- // Thanks IE 10 >:(
13016
- const t = _findOriginalTarget(ev);
13017
- // Make sure the target of the event is an element so we can use getBoundingClientRect,
13018
- // if not, just assume it is a synthetic click
13019
- if (!t.nodeType || /** @type {Element} */ (t).nodeType !== Node.ELEMENT_NODE) {
13020
- return true;
13021
- }
13022
- const bcr = /** @type {Element} */ (t).getBoundingClientRect();
13023
- // Use page x/y to account for scrolling
13024
- const x = ev.pageX,
13025
- y = ev.pageY;
13026
- // Ev is a synthetic click if the position is outside the bounding box of the target
13027
- return !(x >= bcr.left && x <= bcr.right && y >= bcr.top && y <= bcr.bottom);
13028
- }
13029
- return false;
13030
- }
12982
+ hostConnected() {
12983
+ if (!this.initialized) {
12984
+ let node = this.getSlotChild();
13031
12985
 
13032
- const POINTERSTATE = {
13033
- mouse: {
13034
- target: null,
13035
- mouseIgnoreJob: null,
13036
- },
13037
- touch: {
13038
- x: 0,
13039
- y: 0,
13040
- id: -1,
13041
- scrollDecided: false,
13042
- },
13043
- };
12986
+ if (!node) {
12987
+ node = this.attachDefaultNode();
12988
+ } else {
12989
+ this.node = node;
12990
+ this.initCustomNode(node);
12991
+ }
13044
12992
 
13045
- function firstTouchAction(ev) {
13046
- let ta = 'auto';
13047
- const path = getComposedPath(ev);
13048
- for (let i = 0, n; i < path.length; i++) {
13049
- n = path[i];
13050
- if (n[TOUCH_ACTION]) {
13051
- ta = n[TOUCH_ACTION];
13052
- break;
12993
+ this.initNode(node);
12994
+
12995
+ // TODO: Consider making this behavior opt-in to improve performance.
12996
+ this.observe();
12997
+
12998
+ this.initialized = true;
13053
12999
  }
13054
13000
  }
13055
- return ta;
13001
+
13002
+ /**
13003
+ * Create and attach default node using the slot factory.
13004
+ * @return {Node | undefined}
13005
+ * @protected
13006
+ */
13007
+ attachDefaultNode() {
13008
+ const { host, slotName, slotFactory } = this;
13009
+
13010
+ // Check if the node was created previously and if so, reuse it.
13011
+ let node = this.defaultNode;
13012
+
13013
+ // Slot factory is optional, some slots don't have default content.
13014
+ if (!node && slotFactory) {
13015
+ node = slotFactory(host);
13016
+ if (node instanceof Element) {
13017
+ if (slotName !== '') {
13018
+ node.setAttribute('slot', slotName);
13019
+ }
13020
+ this.node = node;
13021
+ this.defaultNode = node;
13022
+ }
13023
+ }
13024
+
13025
+ if (node) {
13026
+ host.appendChild(node);
13027
+ }
13028
+
13029
+ return node;
13030
+ }
13031
+
13032
+ /**
13033
+ * Return a reference to the node managed by the controller.
13034
+ * @return {Node}
13035
+ */
13036
+ getSlotChild() {
13037
+ const { slotName } = this;
13038
+ return Array.from(this.host.childNodes).find((node) => {
13039
+ // Either an element (any slot) or a text node (only un-named slot).
13040
+ return (
13041
+ (node.nodeType === Node.ELEMENT_NODE && node.slot === slotName) ||
13042
+ (node.nodeType === Node.TEXT_NODE && node.textContent.trim() && slotName === '')
13043
+ );
13044
+ });
13045
+ }
13046
+
13047
+ /**
13048
+ * @param {Node} node
13049
+ * @protected
13050
+ */
13051
+ initNode(node) {
13052
+ const { slotInitializer } = this;
13053
+ // Don't try to bind `this` to initializer (normally it's arrow function).
13054
+ // Instead, pass the host as a first argument to access component's state.
13055
+ if (slotInitializer) {
13056
+ slotInitializer(this.host, node);
13057
+ }
13058
+ }
13059
+
13060
+ /**
13061
+ * Override to initialize the newly added custom node.
13062
+ *
13063
+ * @param {Node} _node
13064
+ * @protected
13065
+ */
13066
+ initCustomNode(_node) {}
13067
+
13068
+ /**
13069
+ * Override to teardown slotted node when it's removed.
13070
+ *
13071
+ * @param {Node} _node
13072
+ * @protected
13073
+ */
13074
+ teardownNode(_node) {}
13075
+
13076
+ /**
13077
+ * Setup the observer to manage slot content changes.
13078
+ * @protected
13079
+ */
13080
+ observe() {
13081
+ const { slotName } = this;
13082
+ const selector = slotName === '' ? 'slot:not([name])' : `slot[name=${slotName}]`;
13083
+ const slot = this.host.shadowRoot.querySelector(selector);
13084
+
13085
+ this.__slotObserver = new FlattenedNodesObserver(slot, (info) => {
13086
+ // TODO: support default slot with multiple nodes (e.g. confirm-dialog)
13087
+ const current = this.node;
13088
+ const newNode = info.addedNodes.find((node) => node !== current);
13089
+
13090
+ if (info.removedNodes.length) {
13091
+ info.removedNodes.forEach((node) => {
13092
+ this.teardownNode(node);
13093
+ });
13094
+ }
13095
+
13096
+ if (newNode) {
13097
+ // Custom node is added, remove the current one.
13098
+ if (current && current.isConnected) {
13099
+ this.host.removeChild(current);
13100
+ }
13101
+
13102
+ this.node = newNode;
13103
+
13104
+ if (newNode !== this.defaultNode) {
13105
+ this.initCustomNode(newNode);
13106
+
13107
+ this.initNode(newNode);
13108
+ }
13109
+ }
13110
+ });
13111
+ }
13112
+ }
13113
+
13114
+ /**
13115
+ * @license
13116
+ * Copyright (c) 2022 Vaadin Ltd.
13117
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
13118
+ */
13119
+
13120
+ /**
13121
+ * A controller that manages the slotted tooltip element.
13122
+ */
13123
+ class TooltipController extends SlotController {
13124
+ constructor(host) {
13125
+ // Do not provide slot factory to create tooltip lazily.
13126
+ super(host, 'tooltip');
13127
+
13128
+ this.setTarget(host);
13129
+ }
13130
+
13131
+ /**
13132
+ * Override to initialize the newly added custom tooltip.
13133
+ *
13134
+ * @param {Node} tooltipNode
13135
+ * @protected
13136
+ * @override
13137
+ */
13138
+ initCustomNode(tooltipNode) {
13139
+ tooltipNode.target = this.target;
13140
+
13141
+ if (this.context !== undefined) {
13142
+ tooltipNode.context = this.context;
13143
+ }
13144
+
13145
+ if (this.manual !== undefined) {
13146
+ tooltipNode.manual = this.manual;
13147
+ }
13148
+
13149
+ if (this.opened !== undefined) {
13150
+ tooltipNode.opened = this.opened;
13151
+ }
13152
+
13153
+ if (this.position !== undefined) {
13154
+ tooltipNode._position = this.position;
13155
+ }
13156
+
13157
+ if (this.shouldShow !== undefined) {
13158
+ tooltipNode.shouldShow = this.shouldShow;
13159
+ }
13160
+ }
13161
+
13162
+ /**
13163
+ * Set a context object to be used by generator.
13164
+ * @param {object} context
13165
+ */
13166
+ setContext(context) {
13167
+ this.context = context;
13168
+
13169
+ const tooltipNode = this.node;
13170
+ if (tooltipNode) {
13171
+ tooltipNode.context = context;
13172
+ }
13173
+ }
13174
+
13175
+ /**
13176
+ * Toggle manual state on the slotted tooltip.
13177
+ * @param {boolean} manual
13178
+ */
13179
+ setManual(manual) {
13180
+ this.manual = manual;
13181
+
13182
+ const tooltipNode = this.node;
13183
+ if (tooltipNode) {
13184
+ tooltipNode.manual = manual;
13185
+ }
13186
+ }
13187
+
13188
+ /**
13189
+ * Toggle opened state on the slotted tooltip.
13190
+ * @param {boolean} opened
13191
+ */
13192
+ setOpened(opened) {
13193
+ this.opened = opened;
13194
+
13195
+ const tooltipNode = this.node;
13196
+ if (tooltipNode) {
13197
+ tooltipNode.opened = opened;
13198
+ }
13199
+ }
13200
+
13201
+ /**
13202
+ * Set default position for the slotted tooltip.
13203
+ * This can be overridden by setting the position
13204
+ * using corresponding property or attribute.
13205
+ * @param {string} position
13206
+ */
13207
+ setPosition(position) {
13208
+ this.position = position;
13209
+
13210
+ const tooltipNode = this.node;
13211
+ if (tooltipNode) {
13212
+ tooltipNode._position = position;
13213
+ }
13214
+ }
13215
+
13216
+ /**
13217
+ * Set function used to detect whether to show
13218
+ * the tooltip based on a condition.
13219
+ * @param {Function} shouldShow
13220
+ */
13221
+ setShouldShow(shouldShow) {
13222
+ this.shouldShow = shouldShow;
13223
+
13224
+ const tooltipNode = this.node;
13225
+ if (tooltipNode) {
13226
+ tooltipNode.shouldShow = shouldShow;
13227
+ }
13228
+ }
13229
+
13230
+ /**
13231
+ * Set an HTML element to attach the tooltip to.
13232
+ * @param {HTMLElement} target
13233
+ */
13234
+ setTarget(target) {
13235
+ this.target = target;
13236
+
13237
+ const tooltipNode = this.node;
13238
+ if (tooltipNode) {
13239
+ tooltipNode.target = target;
13240
+ }
13241
+ }
13242
+ }
13243
+
13244
+ /**
13245
+ * @license
13246
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
13247
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
13248
+ */
13249
+
13250
+ /**
13251
+ * A mixin to provide disabled property for field components.
13252
+ *
13253
+ * @polymerMixin
13254
+ */
13255
+ const DisabledMixin = dedupingMixin(
13256
+ (superclass) =>
13257
+ class DisabledMixinClass extends superclass {
13258
+ static get properties() {
13259
+ return {
13260
+ /**
13261
+ * If true, the user cannot interact with this element.
13262
+ */
13263
+ disabled: {
13264
+ type: Boolean,
13265
+ value: false,
13266
+ observer: '_disabledChanged',
13267
+ reflectToAttribute: true,
13268
+ },
13269
+ };
13270
+ }
13271
+
13272
+ /**
13273
+ * @param {boolean} disabled
13274
+ * @protected
13275
+ */
13276
+ _disabledChanged(disabled) {
13277
+ this._setAriaDisabled(disabled);
13278
+ }
13279
+
13280
+ /**
13281
+ * @param {boolean} disabled
13282
+ * @protected
13283
+ */
13284
+ _setAriaDisabled(disabled) {
13285
+ if (disabled) {
13286
+ this.setAttribute('aria-disabled', 'true');
13287
+ } else {
13288
+ this.removeAttribute('aria-disabled');
13289
+ }
13290
+ }
13291
+
13292
+ /**
13293
+ * Overrides the default element `click` method in order to prevent
13294
+ * firing the `click` event when the element is disabled.
13295
+ * @protected
13296
+ * @override
13297
+ */
13298
+ click() {
13299
+ if (!this.disabled) {
13300
+ super.click();
13301
+ }
13302
+ }
13303
+ },
13304
+ );
13305
+
13306
+ /**
13307
+ @license
13308
+ Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
13309
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
13310
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
13311
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
13312
+ Code distributed by Google as part of the polymer project is also
13313
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
13314
+ */
13315
+
13316
+ const passiveTouchGestures = false;
13317
+ const wrap = (node) => node;
13318
+
13319
+ // Detect native touch action support
13320
+ const HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string';
13321
+ const GESTURE_KEY = '__polymerGestures';
13322
+ const HANDLED_OBJ = '__polymerGesturesHandled';
13323
+ const TOUCH_ACTION = '__polymerGesturesTouchAction';
13324
+ // Radius for tap and track
13325
+ const TAP_DISTANCE = 25;
13326
+ const TRACK_DISTANCE = 5;
13327
+ // Number of last N track positions to keep
13328
+ const TRACK_LENGTH = 2;
13329
+
13330
+ const MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'click'];
13331
+ // An array of bitmask values for mapping MouseEvent.which to MouseEvent.buttons
13332
+ const MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2];
13333
+ const MOUSE_HAS_BUTTONS = (function () {
13334
+ try {
13335
+ return new MouseEvent('test', { buttons: 1 }).buttons === 1;
13336
+ } catch (e) {
13337
+ return false;
13338
+ }
13339
+ })();
13340
+
13341
+ /**
13342
+ * @param {string} name Possible mouse event name
13343
+ * @return {boolean} true if mouse event, false if not
13344
+ */
13345
+ function isMouseEvent(name) {
13346
+ return MOUSE_EVENTS.indexOf(name) > -1;
13347
+ }
13348
+
13349
+ /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
13350
+ // check for passive event listeners
13351
+ let supportsPassive = false;
13352
+ (function () {
13353
+ try {
13354
+ const opts = Object.defineProperty({}, 'passive', {
13355
+ // eslint-disable-next-line getter-return
13356
+ get() {
13357
+ supportsPassive = true;
13358
+ },
13359
+ });
13360
+ window.addEventListener('test', null, opts);
13361
+ window.removeEventListener('test', null, opts);
13362
+ } catch (e) {}
13363
+ })();
13364
+
13365
+ /**
13366
+ * Generate settings for event listeners, dependant on `passiveTouchGestures`
13367
+ *
13368
+ * @param {string} eventName Event name to determine if `{passive}` option is
13369
+ * needed
13370
+ * @return {{passive: boolean} | undefined} Options to use for addEventListener
13371
+ * and removeEventListener
13372
+ */
13373
+ function PASSIVE_TOUCH(eventName) {
13374
+ if (isMouseEvent(eventName) || eventName === 'touchend') {
13375
+ return;
13376
+ }
13377
+ if (HAS_NATIVE_TA && supportsPassive && passiveTouchGestures) {
13378
+ return { passive: true };
13379
+ }
13380
+ }
13381
+
13382
+ // Check for touch-only devices
13383
+ const IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
13384
+
13385
+ // Defined at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute
13386
+ /** @type {!Object<boolean>} */
13387
+ const canBeDisabled = {
13388
+ button: true,
13389
+ command: true,
13390
+ fieldset: true,
13391
+ input: true,
13392
+ keygen: true,
13393
+ optgroup: true,
13394
+ option: true,
13395
+ select: true,
13396
+ textarea: true,
13397
+ };
13398
+
13399
+ /**
13400
+ * @param {MouseEvent} ev event to test for left mouse button down
13401
+ * @return {boolean} has left mouse button down
13402
+ */
13403
+ function hasLeftMouseButton(ev) {
13404
+ const type = ev.type;
13405
+ // Exit early if the event is not a mouse event
13406
+ if (!isMouseEvent(type)) {
13407
+ return false;
13408
+ }
13409
+ // Ev.button is not reliable for mousemove (0 is overloaded as both left button and no buttons)
13410
+ // instead we use ev.buttons (bitmask of buttons) or fall back to ev.which (deprecated, 0 for no buttons, 1 for left button)
13411
+ if (type === 'mousemove') {
13412
+ // Allow undefined for testing events
13413
+ let buttons = ev.buttons === undefined ? 1 : ev.buttons;
13414
+ if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) {
13415
+ buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
13416
+ }
13417
+ // Buttons is a bitmask, check that the left button bit is set (1)
13418
+ return Boolean(buttons & 1);
13419
+ }
13420
+ // Allow undefined for testing events
13421
+ const button = ev.button === undefined ? 0 : ev.button;
13422
+ // Ev.button is 0 in mousedown/mouseup/click for left button activation
13423
+ return button === 0;
13424
+ }
13425
+
13426
+ function isSyntheticClick(ev) {
13427
+ if (ev.type === 'click') {
13428
+ // Ev.detail is 0 for HTMLElement.click in most browsers
13429
+ if (ev.detail === 0) {
13430
+ return true;
13431
+ }
13432
+ // In the worst case, check that the x/y position of the click is within
13433
+ // the bounding box of the target of the event
13434
+ // Thanks IE 10 >:(
13435
+ const t = _findOriginalTarget(ev);
13436
+ // Make sure the target of the event is an element so we can use getBoundingClientRect,
13437
+ // if not, just assume it is a synthetic click
13438
+ if (!t.nodeType || /** @type {Element} */ (t).nodeType !== Node.ELEMENT_NODE) {
13439
+ return true;
13440
+ }
13441
+ const bcr = /** @type {Element} */ (t).getBoundingClientRect();
13442
+ // Use page x/y to account for scrolling
13443
+ const x = ev.pageX,
13444
+ y = ev.pageY;
13445
+ // Ev is a synthetic click if the position is outside the bounding box of the target
13446
+ return !(x >= bcr.left && x <= bcr.right && y >= bcr.top && y <= bcr.bottom);
13447
+ }
13448
+ return false;
13449
+ }
13450
+
13451
+ const POINTERSTATE = {
13452
+ mouse: {
13453
+ target: null,
13454
+ mouseIgnoreJob: null,
13455
+ },
13456
+ touch: {
13457
+ x: 0,
13458
+ y: 0,
13459
+ id: -1,
13460
+ scrollDecided: false,
13461
+ },
13462
+ };
13463
+
13464
+ function firstTouchAction(ev) {
13465
+ let ta = 'auto';
13466
+ const path = getComposedPath(ev);
13467
+ for (let i = 0, n; i < path.length; i++) {
13468
+ n = path[i];
13469
+ if (n[TOUCH_ACTION]) {
13470
+ ta = n[TOUCH_ACTION];
13471
+ break;
13472
+ }
13473
+ }
13474
+ return ta;
13056
13475
  }
13057
13476
 
13058
13477
  function trackDocument(stateObj, movefn, upfn) {
@@ -13148,7 +13567,7 @@ function _handleNative(ev) {
13148
13567
  }
13149
13568
  if (!ev[HANDLED_OBJ]) {
13150
13569
  ev[HANDLED_OBJ] = {};
13151
- if (type.slice(0, 5) === 'touch') {
13570
+ if (type.startsWith('touch')) {
13152
13571
  const t = ev.changedTouches[0];
13153
13572
  if (type === 'touchstart') {
13154
13573
  // Only handle the first finger
@@ -13712,107 +14131,45 @@ register({
13712
14131
  const touch = e.changedTouches[0];
13713
14132
  this.info.x = touch.clientX;
13714
14133
  this.info.y = touch.clientY;
13715
- },
13716
-
13717
- /**
13718
- * @this {GestureRecognizer}
13719
- * @param {TouchEvent} e
13720
- * @return {void}
13721
- */
13722
- touchend(e) {
13723
- trackForward(this.info, e.changedTouches[0], e);
13724
- },
13725
- });
13726
-
13727
- /**
13728
- * @param {!GestureInfo} info
13729
- * @param {Event | Touch} e
13730
- * @param {Event=} preventer
13731
- * @return {void}
13732
- */
13733
- function trackForward(info, e, preventer) {
13734
- const dx = Math.abs(e.clientX - info.x);
13735
- const dy = Math.abs(e.clientY - info.y);
13736
- // Find original target from `preventer` for TouchEvents, or `e` for MouseEvents
13737
- const t = _findOriginalTarget(preventer || e);
13738
- if (!t || (canBeDisabled[/** @type {!HTMLElement} */ (t).localName] && t.hasAttribute('disabled'))) {
13739
- return;
13740
- }
13741
- // Dx,dy can be NaN if `click` has been simulated and there was no `down` for `start`
13742
- if (isNaN(dx) || isNaN(dy) || (dx <= TAP_DISTANCE && dy <= TAP_DISTANCE) || isSyntheticClick(e)) {
13743
- // Prevent taps from being generated if an event has canceled them
13744
- if (!info.prevent) {
13745
- _fire(t, 'tap', {
13746
- x: e.clientX,
13747
- y: e.clientY,
13748
- sourceEvent: e,
13749
- preventer,
13750
- });
13751
- }
13752
- }
13753
- }
13754
-
13755
- /**
13756
- * @license
13757
- * Copyright (c) 2021 - 2022 Vaadin Ltd.
13758
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
13759
- */
13760
-
13761
- /**
13762
- * A mixin to provide disabled property for field components.
13763
- *
13764
- * @polymerMixin
13765
- */
13766
- const DisabledMixin = dedupingMixin(
13767
- (superclass) =>
13768
- class DisabledMixinClass extends superclass {
13769
- static get properties() {
13770
- return {
13771
- /**
13772
- * If true, the user cannot interact with this element.
13773
- */
13774
- disabled: {
13775
- type: Boolean,
13776
- value: false,
13777
- observer: '_disabledChanged',
13778
- reflectToAttribute: true,
13779
- },
13780
- };
13781
- }
13782
-
13783
- /**
13784
- * @param {boolean} disabled
13785
- * @protected
13786
- */
13787
- _disabledChanged(disabled) {
13788
- this._setAriaDisabled(disabled);
13789
- }
14134
+ },
13790
14135
 
13791
- /**
13792
- * @param {boolean} disabled
13793
- * @protected
13794
- */
13795
- _setAriaDisabled(disabled) {
13796
- if (disabled) {
13797
- this.setAttribute('aria-disabled', 'true');
13798
- } else {
13799
- this.removeAttribute('aria-disabled');
13800
- }
13801
- }
14136
+ /**
14137
+ * @this {GestureRecognizer}
14138
+ * @param {TouchEvent} e
14139
+ * @return {void}
14140
+ */
14141
+ touchend(e) {
14142
+ trackForward(this.info, e.changedTouches[0], e);
14143
+ },
14144
+ });
13802
14145
 
13803
- /**
13804
- * Overrides the default element `click` method in order to prevent
13805
- * firing the `click` event when the element is disabled.
13806
- * @protected
13807
- * @override
13808
- */
13809
- click() {
13810
- if (!this.disabled) {
13811
- super.click();
13812
- }
13813
- }
13814
- },
13815
- );
14146
+ /**
14147
+ * @param {!GestureInfo} info
14148
+ * @param {Event | Touch} e
14149
+ * @param {Event=} preventer
14150
+ * @return {void}
14151
+ */
14152
+ function trackForward(info, e, preventer) {
14153
+ const dx = Math.abs(e.clientX - info.x);
14154
+ const dy = Math.abs(e.clientY - info.y);
14155
+ // Find original target from `preventer` for TouchEvents, or `e` for MouseEvents
14156
+ const t = _findOriginalTarget(preventer || e);
14157
+ if (!t || (canBeDisabled[/** @type {!HTMLElement} */ (t).localName] && t.hasAttribute('disabled'))) {
14158
+ return;
14159
+ }
14160
+ // Dx,dy can be NaN if `click` has been simulated and there was no `down` for `start`
14161
+ if (isNaN(dx) || isNaN(dy) || (dx <= TAP_DISTANCE && dy <= TAP_DISTANCE) || isSyntheticClick(e)) {
14162
+ // Prevent taps from being generated if an event has canceled them
14163
+ if (!info.prevent) {
14164
+ _fire(t, 'tap', {
14165
+ x: e.clientX,
14166
+ y: e.clientY,
14167
+ sourceEvent: e,
14168
+ preventer,
14169
+ });
14170
+ }
14171
+ }
14172
+ }
13816
14173
 
13817
14174
  /**
13818
14175
  * @license
@@ -14007,28 +14364,6 @@ const ActiveMixin = (superclass) =>
14007
14364
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
14008
14365
  */
14009
14366
 
14010
- // We consider the keyboard to be active if the window has received a keydown
14011
- // event since the last mousedown event.
14012
- let keyboardActive = false;
14013
-
14014
- // Listen for top-level keydown and mousedown events.
14015
- // Use capture phase so we detect events even if they're handled.
14016
- window.addEventListener(
14017
- 'keydown',
14018
- () => {
14019
- keyboardActive = true;
14020
- },
14021
- { capture: true },
14022
- );
14023
-
14024
- window.addEventListener(
14025
- 'mousedown',
14026
- () => {
14027
- keyboardActive = false;
14028
- },
14029
- { capture: true },
14030
- );
14031
-
14032
14367
  /**
14033
14368
  * A mixin to handle `focused` and `focus-ring` attributes based on focus.
14034
14369
  *
@@ -14042,7 +14377,7 @@ const FocusMixin = dedupingMixin(
14042
14377
  * @return {boolean}
14043
14378
  */
14044
14379
  get _keyboardActive() {
14045
- return keyboardActive;
14380
+ return isKeyboardActive();
14046
14381
  }
14047
14382
 
14048
14383
  /** @protected */
@@ -14306,14 +14641,15 @@ const ButtonMixin = (superClass) =>
14306
14641
  * `focus-ring` | Set when the button is focused using the keyboard.
14307
14642
  * `focused` | Set when the button is focused.
14308
14643
  *
14309
- * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
14644
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
14310
14645
  *
14311
14646
  * @extends HTMLElement
14312
14647
  * @mixes ButtonMixin
14648
+ * @mixes ControllerMixin
14313
14649
  * @mixes ElementMixin
14314
14650
  * @mixes ThemableMixin
14315
14651
  */
14316
- class Button extends ButtonMixin(ElementMixin(ThemableMixin(PolymerElement))) {
14652
+ class Button extends ButtonMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement)))) {
14317
14653
  static get is() {
14318
14654
  return 'vaadin-button';
14319
14655
  }
@@ -14367,18 +14703,27 @@ class Button extends ButtonMixin(ElementMixin(ThemableMixin(PolymerElement))) {
14367
14703
  }
14368
14704
  </style>
14369
14705
  <div class="vaadin-button-container">
14370
- <span part="prefix">
14706
+ <span part="prefix" aria-hidden="true">
14371
14707
  <slot name="prefix"></slot>
14372
14708
  </span>
14373
14709
  <span part="label">
14374
14710
  <slot></slot>
14375
14711
  </span>
14376
- <span part="suffix">
14712
+ <span part="suffix" aria-hidden="true">
14377
14713
  <slot name="suffix"></slot>
14378
14714
  </span>
14379
14715
  </div>
14716
+ <slot name="tooltip"></slot>
14380
14717
  `;
14381
14718
  }
14719
+
14720
+ /** @protected */
14721
+ ready() {
14722
+ super.ready();
14723
+
14724
+ this._tooltipController = new TooltipController(this);
14725
+ this.addController(this._tooltipController);
14726
+ }
14382
14727
  }
14383
14728
 
14384
14729
  customElements.define(Button.is, Button);
@@ -14388,7 +14733,6 @@ registerStyles(
14388
14733
  i$1`
14389
14734
  :host {
14390
14735
  position: relative;
14391
- background-color: transparent;
14392
14736
  /* Background for the year scroller, placed here as we are using a mask image on the actual years part */
14393
14737
  background-image: linear-gradient(var(--lumo-shade-5pct), var(--lumo-shade-5pct));
14394
14738
  background-size: 57px 100%;
@@ -14414,7 +14758,7 @@ registerStyles(
14414
14758
  + var(--lumo-size-m) * 6
14415
14759
  + var(--lumo-space-s)
14416
14760
  );
14417
- --vaadin-infinite-scroller-buffer-offset: 20%;
14761
+ --vaadin-infinite-scroller-buffer-offset: 10%;
14418
14762
  -webkit-mask-image: linear-gradient(transparent, #000 10%, #000 85%, transparent);
14419
14763
  mask-image: linear-gradient(transparent, #000 10%, #000 85%, transparent);
14420
14764
  position: relative;
@@ -14496,17 +14840,10 @@ registerStyles(
14496
14840
 
14497
14841
  [part='toolbar'] {
14498
14842
  padding: var(--lumo-space-s);
14499
- box-shadow: 0 -1px 0 0 var(--lumo-contrast-10pct);
14500
14843
  border-bottom-left-radius: var(--lumo-border-radius-l);
14501
14844
  margin-right: 57px;
14502
14845
  }
14503
14846
 
14504
- @supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
14505
- [part='toolbar'] {
14506
- box-shadow: none;
14507
- }
14508
- }
14509
-
14510
14847
  /* Today and Cancel buttons */
14511
14848
 
14512
14849
  [part='toolbar'] [part\$='button'] {
@@ -14539,8 +14876,6 @@ registerStyles(
14539
14876
  /* Very narrow screen (year scroller initially hidden) */
14540
14877
 
14541
14878
  [part='years-toggle-button'] {
14542
- position: relative;
14543
- right: auto;
14544
14879
  display: flex;
14545
14880
  align-items: center;
14546
14881
  height: var(--lumo-size-s);
@@ -14558,11 +14893,7 @@ registerStyles(
14558
14893
  color: var(--lumo-primary-contrast-color);
14559
14894
  }
14560
14895
 
14561
- [part='years-toggle-button']::before {
14562
- content: none;
14563
- }
14564
-
14565
- /* TODO magic number (same as used for iron-media-query in vaadin-date-picker-overlay-content) */
14896
+ /* TODO magic number (same as used for media-query in vaadin-date-picker-overlay-content) */
14566
14897
  @media screen and (max-width: 374px) {
14567
14898
  :host {
14568
14899
  background-image: none;
@@ -14737,9 +15068,9 @@ registerStyles(
14737
15068
  { moduleId: 'lumo-month-calendar' },
14738
15069
  );
14739
15070
 
14740
- const $_documentContainer$1 = document.createElement('template');
15071
+ const template$1 = document.createElement('template');
14741
15072
 
14742
- $_documentContainer$1.innerHTML = `
15073
+ template$1.innerHTML = `
14743
15074
  <style>
14744
15075
  @keyframes vaadin-date-picker-month-calendar-focus-date {
14745
15076
  50% {
@@ -14749,7 +15080,7 @@ $_documentContainer$1.innerHTML = `
14749
15080
  </style>
14750
15081
  `;
14751
15082
 
14752
- document.head.appendChild($_documentContainer$1.content);
15083
+ document.head.appendChild(template$1.content);
14753
15084
 
14754
15085
  /**
14755
15086
  * @license
@@ -14757,9 +15088,9 @@ document.head.appendChild($_documentContainer$1.content);
14757
15088
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
14758
15089
  */
14759
15090
 
14760
- const $_documentContainer = document.createElement('template');
15091
+ const template = document.createElement('template');
14761
15092
 
14762
- $_documentContainer.innerHTML = `
15093
+ template.innerHTML = `
14763
15094
  <style>
14764
15095
  @font-face {
14765
15096
  font-family: 'lumo-icons';
@@ -14815,7 +15146,7 @@ $_documentContainer.innerHTML = `
14815
15146
  </style>
14816
15147
  `;
14817
15148
 
14818
- document.head.appendChild($_documentContainer.content);
15149
+ document.head.appendChild(template.content);
14819
15150
 
14820
15151
  /**
14821
15152
  * @license
@@ -14929,6 +15260,10 @@ const requiredField = i$1`
14929
15260
  line-height: 1;
14930
15261
  padding-right: 1em;
14931
15262
  padding-bottom: 0.5em;
15263
+ /* As a workaround for diacritics being cut off, add a top padding and a
15264
+ negative margin to compensate */
15265
+ padding-top: 0.25em;
15266
+ margin-top: -0.25em;
14932
15267
  overflow: hidden;
14933
15268
  white-space: nowrap;
14934
15269
  text-overflow: ellipsis;
@@ -14949,6 +15284,10 @@ const requiredField = i$1`
14949
15284
  padding-top: var(--lumo-space-m);
14950
15285
  }
14951
15286
 
15287
+ :host([has-label]) ::slotted([slot='tooltip']) {
15288
+ --vaadin-tooltip-offset-bottom: calc((var(--lumo-space-m) - var(--lumo-space-xs)) * -1);
15289
+ }
15290
+
14952
15291
  :host([required]) [part='required-indicator']::after {
14953
15292
  content: var(--lumo-required-field-indicator, '•');
14954
15293
  transition: opacity 0.2s;
@@ -15382,6 +15721,57 @@ function getAncestorRootNodes(node) {
15382
15721
  return result;
15383
15722
  }
15384
15723
 
15724
+ /**
15725
+ * @param {string} value
15726
+ * @return {Set<string>}
15727
+ */
15728
+ function deserializeAttributeValue(value) {
15729
+ if (!value) {
15730
+ return new Set();
15731
+ }
15732
+
15733
+ return new Set(value.split(' '));
15734
+ }
15735
+
15736
+ /**
15737
+ * @param {Set<string>} values
15738
+ * @return {string}
15739
+ */
15740
+ function serializeAttributeValue(values) {
15741
+ return [...values].join(' ');
15742
+ }
15743
+
15744
+ /**
15745
+ * Adds a value to an attribute containing space-delimited values.
15746
+ *
15747
+ * @param {HTMLElement} element
15748
+ * @param {string} attr
15749
+ * @param {string} value
15750
+ */
15751
+ function addValueToAttribute(element, attr, value) {
15752
+ const values = deserializeAttributeValue(element.getAttribute(attr));
15753
+ values.add(value);
15754
+ element.setAttribute(attr, serializeAttributeValue(values));
15755
+ }
15756
+
15757
+ /**
15758
+ * Removes a value from an attribute containing space-delimited values.
15759
+ * If the value is the last one, the whole attribute is removed.
15760
+ *
15761
+ * @param {HTMLElement} element
15762
+ * @param {string} attr
15763
+ * @param {string} value
15764
+ */
15765
+ function removeValueFromAttribute(element, attr, value) {
15766
+ const values = deserializeAttributeValue(element.getAttribute(attr));
15767
+ values.delete(value);
15768
+ if (values.size === 0) {
15769
+ element.removeAttribute(attr);
15770
+ return;
15771
+ }
15772
+ element.setAttribute(attr, serializeAttributeValue(values));
15773
+ }
15774
+
15385
15775
  /**
15386
15776
  * @license
15387
15777
  * Copyright (c) 2017 - 2022 Vaadin Ltd.
@@ -15398,6 +15788,16 @@ const PROP_NAMES_HORIZONTAL = {
15398
15788
  end: 'right',
15399
15789
  };
15400
15790
 
15791
+ const targetResizeObserver = new ResizeObserver((entries) => {
15792
+ setTimeout(() => {
15793
+ entries.forEach((entry) => {
15794
+ if (entry.target.__overlay) {
15795
+ entry.target.__overlay._updatePosition();
15796
+ }
15797
+ });
15798
+ });
15799
+ });
15800
+
15401
15801
  /**
15402
15802
  * @polymerMixin
15403
15803
  */
@@ -15423,6 +15823,8 @@ const PositionMixin = (superClass) =>
15423
15823
  * RTL is taken into account when interpreting the value.
15424
15824
  * The overlay is automatically flipped to the opposite side when it doesn't fit into
15425
15825
  * the default side defined by this property.
15826
+ *
15827
+ * @attr {start|end} horizontal-align
15426
15828
  */
15427
15829
  horizontalAlign: {
15428
15830
  type: String,
@@ -15435,6 +15837,8 @@ const PositionMixin = (superClass) =>
15435
15837
  * Possible values are `top` and `bottom`.
15436
15838
  * The overlay is automatically flipped to the opposite side when it doesn't fit into
15437
15839
  * the default side defined by this property.
15840
+ *
15841
+ * @attr {top|bottom} vertical-align
15438
15842
  */
15439
15843
  verticalAlign: {
15440
15844
  type: String,
@@ -15444,6 +15848,8 @@ const PositionMixin = (superClass) =>
15444
15848
  /**
15445
15849
  * When `positionTarget` is set, this property defines whether the overlay should overlap
15446
15850
  * the target element in the x-axis, or be positioned right next to it.
15851
+ *
15852
+ * @attr {boolean} no-horizontal-overlap
15447
15853
  */
15448
15854
  noHorizontalOverlap: {
15449
15855
  type: Boolean,
@@ -15453,17 +15859,32 @@ const PositionMixin = (superClass) =>
15453
15859
  /**
15454
15860
  * When `positionTarget` is set, this property defines whether the overlay should overlap
15455
15861
  * the target element in the y-axis, or be positioned right above/below it.
15862
+ *
15863
+ * @attr {boolean} no-vertical-overlap
15456
15864
  */
15457
15865
  noVerticalOverlap: {
15458
15866
  type: Boolean,
15459
15867
  value: false,
15460
15868
  },
15869
+
15870
+ /**
15871
+ * If the overlay content has no intrinsic height, this property can be used to set
15872
+ * the minimum vertical space (in pixels) required by the overlay. Setting a value to
15873
+ * the property effectively disables the content measurement in favor of using this
15874
+ * fixed value for determining the open direction.
15875
+ *
15876
+ * @attr {number} required-vertical-space
15877
+ */
15878
+ requiredVerticalSpace: {
15879
+ type: Number,
15880
+ value: 0,
15881
+ },
15461
15882
  };
15462
15883
  }
15463
15884
 
15464
15885
  static get observers() {
15465
15886
  return [
15466
- '__positionSettingsChanged(horizontalAlign, verticalAlign, noHorizontalOverlap, noVerticalOverlap)',
15887
+ '__positionSettingsChanged(horizontalAlign, verticalAlign, noHorizontalOverlap, noVerticalOverlap, requiredVerticalSpace)',
15467
15888
  '__overlayOpenedChanged(opened, positionTarget)',
15468
15889
  ];
15469
15890
  }
@@ -15471,6 +15892,7 @@ const PositionMixin = (superClass) =>
15471
15892
  constructor() {
15472
15893
  super();
15473
15894
 
15895
+ this.__onScroll = this.__onScroll.bind(this);
15474
15896
  this._updatePosition = this._updatePosition.bind(this);
15475
15897
  }
15476
15898
 
@@ -15495,7 +15917,7 @@ const PositionMixin = (superClass) =>
15495
15917
 
15496
15918
  this.__positionTargetAncestorRootNodes = getAncestorRootNodes(this.positionTarget);
15497
15919
  this.__positionTargetAncestorRootNodes.forEach((node) => {
15498
- node.addEventListener('scroll', this._updatePosition, true);
15920
+ node.addEventListener('scroll', this.__onScroll, true);
15499
15921
  });
15500
15922
  }
15501
15923
 
@@ -15505,7 +15927,7 @@ const PositionMixin = (superClass) =>
15505
15927
 
15506
15928
  if (this.__positionTargetAncestorRootNodes) {
15507
15929
  this.__positionTargetAncestorRootNodes.forEach((node) => {
15508
- node.removeEventListener('scroll', this._updatePosition, true);
15930
+ node.removeEventListener('scroll', this.__onScroll, true);
15509
15931
  });
15510
15932
  this.__positionTargetAncestorRootNodes = null;
15511
15933
  }
@@ -15515,8 +15937,15 @@ const PositionMixin = (superClass) =>
15515
15937
  __overlayOpenedChanged(opened, positionTarget) {
15516
15938
  this.__removeUpdatePositionEventListeners();
15517
15939
 
15518
- if (opened && positionTarget) {
15519
- this.__addUpdatePositionEventListeners();
15940
+ if (positionTarget) {
15941
+ positionTarget.__overlay = null;
15942
+ targetResizeObserver.unobserve(positionTarget);
15943
+
15944
+ if (opened) {
15945
+ this.__addUpdatePositionEventListeners();
15946
+ positionTarget.__overlay = this;
15947
+ targetResizeObserver.observe(positionTarget);
15948
+ }
15520
15949
  }
15521
15950
 
15522
15951
  if (opened) {
@@ -15543,6 +15972,14 @@ const PositionMixin = (superClass) =>
15543
15972
  this._updatePosition();
15544
15973
  }
15545
15974
 
15975
+ /** @private */
15976
+ __onScroll(e) {
15977
+ // If the scroll event occurred inside the overlay, ignore it.
15978
+ if (!this.contains(e.target)) {
15979
+ this._updatePosition();
15980
+ }
15981
+ }
15982
+
15546
15983
  _updatePosition() {
15547
15984
  if (!this.positionTarget || !this.opened) {
15548
15985
  return;
@@ -15615,7 +16052,8 @@ const PositionMixin = (superClass) =>
15615
16052
  __shouldAlignStartVertically(targetRect) {
15616
16053
  // Using previous size to fix a case where window resize may cause the overlay to be squeezed
15617
16054
  // smaller than its current space before the fit-calculations.
15618
- const contentHeight = Math.max(this.__oldContentHeight || 0, this.$.overlay.offsetHeight);
16055
+ const contentHeight =
16056
+ this.requiredVerticalSpace || Math.max(this.__oldContentHeight || 0, this.$.overlay.offsetHeight);
15619
16057
  this.__oldContentHeight = this.$.overlay.offsetHeight;
15620
16058
 
15621
16059
  const viewportHeight = Math.min(window.innerHeight, document.documentElement.clientHeight);
@@ -15647,9 +16085,47 @@ const PositionMixin = (superClass) =>
15647
16085
  return defaultAlignStart === shouldGoToDefaultSide;
15648
16086
  }
15649
16087
 
16088
+ /**
16089
+ * Returns an adjusted value after resizing the browser window,
16090
+ * to avoid wrong calculations when e.g. previously set `bottom`
16091
+ * CSS property value is larger than the updated viewport height.
16092
+ * See https://github.com/vaadin/web-components/issues/4604
16093
+ */
16094
+ __adjustBottomProperty(cssPropNameToSet, propNames, currentValue) {
16095
+ let adjustedProp;
16096
+
16097
+ if (cssPropNameToSet === propNames.end) {
16098
+ // Adjust horizontally
16099
+ if (propNames.end === PROP_NAMES_VERTICAL.end) {
16100
+ const viewportHeight = Math.min(window.innerHeight, document.documentElement.clientHeight);
16101
+
16102
+ if (currentValue > viewportHeight && this.__oldViewportHeight) {
16103
+ const heightDiff = this.__oldViewportHeight - viewportHeight;
16104
+ adjustedProp = currentValue - heightDiff;
16105
+ }
16106
+
16107
+ this.__oldViewportHeight = viewportHeight;
16108
+ }
16109
+
16110
+ // Adjust vertically
16111
+ if (propNames.end === PROP_NAMES_HORIZONTAL.end) {
16112
+ const viewportWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);
16113
+
16114
+ if (currentValue > viewportWidth && this.__oldViewportWidth) {
16115
+ const widthDiff = this.__oldViewportWidth - viewportWidth;
16116
+ adjustedProp = currentValue - widthDiff;
16117
+ }
16118
+
16119
+ this.__oldViewportWidth = viewportWidth;
16120
+ }
16121
+ }
16122
+
16123
+ return adjustedProp;
16124
+ }
16125
+
15650
16126
  /**
15651
16127
  * Returns an object with CSS position properties to set,
15652
- * e.g. { top: "100px", bottom: "" }
16128
+ * e.g. { top: "100px" }
15653
16129
  */
15654
16130
  // eslint-disable-next-line max-params
15655
16131
  __calculatePositionInOneDimension(targetRect, overlayRect, noOverlap, propNames, overlay, shouldAlignStart) {
@@ -15657,13 +16133,18 @@ const PositionMixin = (superClass) =>
15657
16133
  const cssPropNameToClear = shouldAlignStart ? propNames.end : propNames.start;
15658
16134
 
15659
16135
  const currentValue = parseFloat(overlay.style[cssPropNameToSet] || getComputedStyle(overlay)[cssPropNameToSet]);
16136
+ const adjustedValue = this.__adjustBottomProperty(cssPropNameToSet, propNames, currentValue);
15660
16137
 
15661
16138
  const diff =
15662
16139
  overlayRect[shouldAlignStart ? propNames.start : propNames.end] -
15663
16140
  targetRect[noOverlap === shouldAlignStart ? propNames.end : propNames.start];
15664
16141
 
16142
+ const valueToSet = adjustedValue
16143
+ ? `${adjustedValue}px`
16144
+ : `${currentValue + diff * (shouldAlignStart ? -1 : 1)}px`;
16145
+
15665
16146
  return {
15666
- [cssPropNameToSet]: `${currentValue + diff * (shouldAlignStart ? -1 : 1)}px`,
16147
+ [cssPropNameToSet]: valueToSet,
15667
16148
  [cssPropNameToClear]: '',
15668
16149
  };
15669
16150
  }
@@ -15680,11 +16161,6 @@ const datePickerStyles = i$1`
15680
16161
  direction: ltr;
15681
16162
  }
15682
16163
 
15683
- :host([dir='rtl']) [part='value']::placeholder {
15684
- direction: rtl;
15685
- text-align: left;
15686
- }
15687
-
15688
16164
  :host([dir='rtl']) [part='input-field'] ::slotted(input)::placeholder {
15689
16165
  direction: rtl;
15690
16166
  text-align: left;
@@ -15717,10 +16193,10 @@ let memoizedTemplate;
15717
16193
  /**
15718
16194
  * An element used internally by `<vaadin-date-picker>`. Not intended to be used separately.
15719
16195
  *
15720
- * @extends OverlayElement
16196
+ * @extends Overlay
15721
16197
  * @private
15722
16198
  */
15723
- class DatePickerOverlay extends DisableUpgradeMixin(PositionMixin(OverlayElement)) {
16199
+ class DatePickerOverlay extends DisableUpgradeMixin(PositionMixin(Overlay)) {
15724
16200
  static get is() {
15725
16201
  return 'vaadin-date-picker-overlay';
15726
16202
  }
@@ -16860,6 +17336,53 @@ function extractDateParts(date) {
16860
17336
  };
16861
17337
  }
16862
17338
 
17339
+ /**
17340
+ * Calculate the year of the date based on the provided reference date.
17341
+ * Gets a two-digit year and returns a full year.
17342
+ * @param {!Date} referenceDate The date to act as basis in the calculation
17343
+ * @param {!number} year Should be in the range of [0, 99]
17344
+ * @param {number} month
17345
+ * @param {number} day
17346
+ * @return {!number} Adjusted year value
17347
+ */
17348
+ function getAdjustedYear(referenceDate, year, month = 0, day = 1) {
17349
+ if (year > 99) {
17350
+ throw new Error('The provided year cannot have more than 2 digits.');
17351
+ }
17352
+ if (year < 0) {
17353
+ throw new Error('The provided year cannot be negative.');
17354
+ }
17355
+ // Year values up to 2 digits are parsed based on the reference date.
17356
+ let adjustedYear = year + Math.floor(referenceDate.getFullYear() / 100) * 100;
17357
+ if (referenceDate < new Date(adjustedYear - 50, month, day)) {
17358
+ adjustedYear -= 100;
17359
+ } else if (referenceDate > new Date(adjustedYear + 50, month, day)) {
17360
+ adjustedYear += 100;
17361
+ }
17362
+ return adjustedYear;
17363
+ }
17364
+
17365
+ /**
17366
+ * Parse date string of one of the following date formats:
17367
+ * - ISO 8601 `"YYYY-MM-DD"`
17368
+ * - 6-digit extended ISO 8601 `"+YYYYYY-MM-DD"`, `"-YYYYYY-MM-DD"`
17369
+ * @param {!string} str Date string to parse
17370
+ * @return {Date} Parsed date
17371
+ */
17372
+ function parseDate(str) {
17373
+ // Parsing with RegExp to ensure correct format
17374
+ const parts = /^([-+]\d{1}|\d{2,4}|[-+]\d{6})-(\d{1,2})-(\d{1,2})$/.exec(str);
17375
+ if (!parts) {
17376
+ return undefined;
17377
+ }
17378
+
17379
+ const date = new Date(0, 0); // Wrong date (1900-01-01), but with midnight in local time
17380
+ date.setFullYear(parseInt(parts[1], 10));
17381
+ date.setMonth(parseInt(parts[2], 10) - 1);
17382
+ date.setDate(parseInt(parts[3], 10));
17383
+ return date;
17384
+ }
17385
+
16863
17386
  /**
16864
17387
  * @license
16865
17388
  * Copyright (c) 2016 - 2022 Vaadin Ltd.
@@ -16936,7 +17459,9 @@ class MonthCalendar extends FocusMixin(ThemableMixin(PolymerElement)) {
16936
17459
  is="dom-repeat"
16937
17460
  items="[[_getWeekDayNames(i18n.weekdays, i18n.weekdaysShort, showWeekNumbers, i18n.firstDayOfWeek)]]"
16938
17461
  >
16939
- <th role="columnheader" part="weekday" scope="col" abbr$="[[item.weekDay]]">[[item.weekDayShort]]</th>
17462
+ <th role="columnheader" part="weekday" scope="col" abbr$="[[item.weekDay]]" aria-hidden="true">
17463
+ [[item.weekDayShort]]
17464
+ </th>
16940
17465
  </template>
16941
17466
  </tr>
16942
17467
  </thead>
@@ -17106,7 +17631,9 @@ class MonthCalendar extends FocusMixin(ThemableMixin(PolymerElement)) {
17106
17631
 
17107
17632
  _onMonthGridTouchStart() {
17108
17633
  this._notTapping = false;
17109
- setTimeout(() => (this._notTapping = true), 300);
17634
+ setTimeout(() => {
17635
+ this._notTapping = true;
17636
+ }, 300);
17110
17637
  }
17111
17638
 
17112
17639
  _dateAdd(date, delta) {
@@ -17280,12 +17807,6 @@ class MonthCalendar extends FocusMixin(ThemableMixin(PolymerElement)) {
17280
17807
 
17281
17808
  return '-1';
17282
17809
  }
17283
-
17284
- __getWeekNumbers(dates) {
17285
- return dates
17286
- .map((date) => this.__getWeekNumber(date, dates))
17287
- .filter((week, index, arr) => arr.indexOf(week) === index);
17288
- }
17289
17810
  }
17290
17811
 
17291
17812
  customElements.define(MonthCalendar.is, MonthCalendar);
@@ -17375,6 +17896,7 @@ class InfiniteScroller extends PolymerElement {
17375
17896
  /**
17376
17897
  * The amount of initial scroll top. Needed in order for the
17377
17898
  * user to be able to scroll backwards.
17899
+ * @private
17378
17900
  */
17379
17901
  _initialScroll: {
17380
17902
  value: 500000,
@@ -17382,17 +17904,22 @@ class InfiniteScroller extends PolymerElement {
17382
17904
 
17383
17905
  /**
17384
17906
  * The index/position mapped at _initialScroll point.
17907
+ * @private
17385
17908
  */
17386
17909
  _initialIndex: {
17387
17910
  value: 0,
17388
17911
  },
17389
17912
 
17913
+ /** @private */
17390
17914
  _buffers: Array,
17391
17915
 
17916
+ /** @private */
17392
17917
  _preventScrollEvent: Boolean,
17393
17918
 
17919
+ /** @private */
17394
17920
  _mayHaveMomentum: Boolean,
17395
17921
 
17922
+ /** @private */
17396
17923
  _initialized: Boolean,
17397
17924
 
17398
17925
  active: {
@@ -17402,10 +17929,11 @@ class InfiniteScroller extends PolymerElement {
17402
17929
  };
17403
17930
  }
17404
17931
 
17932
+ /** @protected */
17405
17933
  ready() {
17406
17934
  super.ready();
17407
17935
 
17408
- this._buffers = Array.prototype.slice.call(this.root.querySelectorAll('.buffer'));
17936
+ this._buffers = [...this.shadowRoot.querySelectorAll('.buffer')];
17409
17937
 
17410
17938
  this.$.fullHeight.style.height = `${this._initialScroll * 2}px`;
17411
17939
 
@@ -17414,8 +17942,8 @@ class InfiniteScroller extends PolymerElement {
17414
17942
  forwardHostProp(prop, value) {
17415
17943
  if (prop !== 'index') {
17416
17944
  this._buffers.forEach((buffer) => {
17417
- [].forEach.call(buffer.children, (insertionPoint) => {
17418
- insertionPoint._itemWrapper.instance[prop] = value;
17945
+ [...buffer.children].forEach((slot) => {
17946
+ slot._itemWrapper.instance[prop] = value;
17419
17947
  });
17420
17948
  });
17421
17949
  }
@@ -17429,6 +17957,19 @@ class InfiniteScroller extends PolymerElement {
17429
17957
  }
17430
17958
  }
17431
17959
 
17960
+ /**
17961
+ * Force the scroller to update clones after a reset, without
17962
+ * waiting for the debouncer to resolve.
17963
+ */
17964
+ forceUpdate() {
17965
+ if (this._debouncerUpdateClones) {
17966
+ this._buffers[0].updated = this._buffers[1].updated = false;
17967
+ this._updateClones();
17968
+ this._debouncerUpdateClones.cancel();
17969
+ }
17970
+ }
17971
+
17972
+ /** @private */
17432
17973
  _activated(active) {
17433
17974
  if (active && !this._initialized) {
17434
17975
  this._createPool();
@@ -17436,12 +17977,15 @@ class InfiniteScroller extends PolymerElement {
17436
17977
  }
17437
17978
  }
17438
17979
 
17980
+ /** @private */
17439
17981
  _finishInit() {
17440
17982
  if (!this._initDone) {
17441
17983
  // Once the first set of items start fading in, stamp the rest
17442
17984
  this._buffers.forEach((buffer) => {
17443
- [].forEach.call(buffer.children, (insertionPoint) => this._ensureStampedInstance(insertionPoint._itemWrapper));
17444
- }, this);
17985
+ [...buffer.children].forEach((slot) => {
17986
+ this._ensureStampedInstance(slot._itemWrapper);
17987
+ });
17988
+ });
17445
17989
 
17446
17990
  if (!this._buffers[0].translateY) {
17447
17991
  this._reset();
@@ -17451,6 +17995,7 @@ class InfiniteScroller extends PolymerElement {
17451
17995
  }
17452
17996
  }
17453
17997
 
17998
+ /** @private */
17454
17999
  _translateBuffer(up) {
17455
18000
  const index = up ? 1 : 0;
17456
18001
  this._buffers[index].translateY = this._buffers[index ? 0 : 1].translateY + this._bufferHeight * (index ? -1 : 1);
@@ -17459,6 +18004,7 @@ class InfiniteScroller extends PolymerElement {
17459
18004
  this._buffers.reverse();
17460
18005
  }
17461
18006
 
18007
+ /** @private */
17462
18008
  _scroll() {
17463
18009
  if (this._scrollDisabled) {
17464
18010
  return;
@@ -17555,10 +18101,12 @@ class InfiniteScroller extends PolymerElement {
17555
18101
  return this._itemHeightVal;
17556
18102
  }
17557
18103
 
18104
+ /** @private */
17558
18105
  get _bufferHeight() {
17559
18106
  return this.itemHeight * this.bufferSize;
17560
18107
  }
17561
18108
 
18109
+ /** @private */
17562
18110
  _reset() {
17563
18111
  this._scrollDisabled = true;
17564
18112
  this.$.scroller.scrollTop = this._initialScroll;
@@ -17578,6 +18126,7 @@ class InfiniteScroller extends PolymerElement {
17578
18126
  this._scrollDisabled = false;
17579
18127
  }
17580
18128
 
18129
+ /** @private */
17581
18130
  _createPool() {
17582
18131
  const container = this.getBoundingClientRect();
17583
18132
  this._buffers.forEach((buffer) => {
@@ -17589,10 +18138,10 @@ class InfiniteScroller extends PolymerElement {
17589
18138
  const contentId = (InfiniteScroller._contentIndex = InfiniteScroller._contentIndex + 1 || 0);
17590
18139
  const slotName = `vaadin-infinite-scroller-item-content-${contentId}`;
17591
18140
 
17592
- const insertionPoint = document.createElement('slot');
17593
- insertionPoint.setAttribute('name', slotName);
17594
- insertionPoint._itemWrapper = itemWrapper;
17595
- buffer.appendChild(insertionPoint);
18141
+ const slot = document.createElement('slot');
18142
+ slot.setAttribute('name', slotName);
18143
+ slot._itemWrapper = itemWrapper;
18144
+ buffer.appendChild(slot);
17596
18145
 
17597
18146
  itemWrapper.setAttribute('slot', slotName);
17598
18147
  this.appendChild(itemWrapper);
@@ -17604,13 +18153,14 @@ class InfiniteScroller extends PolymerElement {
17604
18153
  }
17605
18154
  }, 1); // Wait for first reset
17606
18155
  }
17607
- }, this);
18156
+ });
17608
18157
 
17609
18158
  setTimeout(() => {
17610
18159
  afterNextRender(this, this._finishInit.bind(this));
17611
18160
  }, 1);
17612
18161
  }
17613
18162
 
18163
+ /** @private */
17614
18164
  _ensureStampedInstance(itemWrapper) {
17615
18165
  if (itemWrapper.firstElementChild) {
17616
18166
  return;
@@ -17626,6 +18176,7 @@ class InfiniteScroller extends PolymerElement {
17626
18176
  });
17627
18177
  }
17628
18178
 
18179
+ /** @private */
17629
18180
  _updateClones(viewPortOnly) {
17630
18181
  this._firstIndex = ~~((this._buffers[0].translateY - this._initialScroll) / this.itemHeight) + this._initialIndex;
17631
18182
 
@@ -17634,17 +18185,18 @@ class InfiniteScroller extends PolymerElement {
17634
18185
  if (!buffer.updated) {
17635
18186
  const firstIndex = this._firstIndex + this.bufferSize * bufferIndex;
17636
18187
 
17637
- [].forEach.call(buffer.children, (insertionPoint, index) => {
17638
- const itemWrapper = insertionPoint._itemWrapper;
18188
+ [...buffer.children].forEach((slot, index) => {
18189
+ const itemWrapper = slot._itemWrapper;
17639
18190
  if (!viewPortOnly || this._isVisible(itemWrapper, scrollerRect)) {
17640
18191
  itemWrapper.instance.index = firstIndex + index;
17641
18192
  }
17642
18193
  });
17643
18194
  buffer.updated = true;
17644
18195
  }
17645
- }, this);
18196
+ });
17646
18197
  }
17647
18198
 
18199
+ /** @private */
17648
18200
  _isVisible(element, container) {
17649
18201
  const rect = element.getBoundingClientRect();
17650
18202
  return rect.bottom > container.top && rect.top < container.bottom;
@@ -17741,7 +18293,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17741
18293
  height: 100%;
17742
18294
  width: 100%;
17743
18295
  outline: none;
17744
- background: #fff;
17745
18296
  }
17746
18297
 
17747
18298
  [part='overlay-header'] {
@@ -17759,22 +18310,14 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17759
18310
  flex-grow: 1;
17760
18311
  }
17761
18312
 
17762
- [part='clear-button']:not([showclear]) {
17763
- display: none;
18313
+ [hidden] {
18314
+ display: none !important;
17764
18315
  }
17765
18316
 
17766
18317
  [part='years-toggle-button'] {
17767
18318
  display: flex;
17768
18319
  }
17769
18320
 
17770
- [part='years-toggle-button'][desktop] {
17771
- display: none;
17772
- }
17773
-
17774
- :host(:not([years-visible])) [part='years-toggle-button']::before {
17775
- transform: rotate(180deg);
17776
- }
17777
-
17778
18321
  #scrollers {
17779
18322
  display: flex;
17780
18323
  height: 100%;
@@ -17843,32 +18386,19 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17843
18386
  }
17844
18387
 
17845
18388
  [part='toolbar'] {
17846
- display: flex;
17847
- justify-content: space-between;
17848
- z-index: 2;
17849
- flex-shrink: 0;
17850
- }
17851
-
17852
- [part~='overlay-header']:not([desktop]) {
17853
- padding-bottom: 40px;
17854
- }
17855
-
17856
- [part~='years-toggle-button'] {
17857
- position: absolute;
17858
- top: auto;
17859
- right: 8px;
17860
- bottom: 0;
17861
- z-index: 1;
17862
- padding: 8px;
18389
+ display: flex;
18390
+ justify-content: space-between;
18391
+ z-index: 2;
18392
+ flex-shrink: 0;
17863
18393
  }
17864
18394
  </style>
17865
18395
 
17866
18396
  <div part="overlay-header" on-touchend="_preventDefault" desktop$="[[_desktopMode]]" aria-hidden="true">
17867
18397
  <div part="label">[[_formatDisplayed(selectedDate, i18n.formatDate, label)]]</div>
17868
- <div part="clear-button" showclear$="[[_showClear(selectedDate)]]"></div>
18398
+ <div part="clear-button" hidden$="[[!selectedDate]]"></div>
17869
18399
  <div part="toggle-button"></div>
17870
18400
 
17871
- <div part="years-toggle-button" desktop$="[[_desktopMode]]" aria-hidden="true">
18401
+ <div part="years-toggle-button" hidden$="[[_desktopMode]]" aria-hidden="true">
17872
18402
  [[_yearAfterXMonths(_visibleMonthIndex)]]
17873
18403
  </div>
17874
18404
  </div>
@@ -17954,6 +18484,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17954
18484
  */
17955
18485
  selectedDate: {
17956
18486
  type: Date,
18487
+ value: null,
17957
18488
  },
17958
18489
 
17959
18490
  /**
@@ -18029,10 +18560,24 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18029
18560
  return this.getAttribute('dir') === 'rtl';
18030
18561
  }
18031
18562
 
18563
+ /**
18564
+ * Whether to scroll to a sub-month position when scrolling to a date.
18565
+ * This is active if the month scroller is not large enough to fit a
18566
+ * full month. In that case we want to scroll to a position between
18567
+ * two months in order to have the focused date in the visible area.
18568
+ * @returns {boolean} whether to use sub-month scrolling
18569
+ * @private
18570
+ */
18571
+ get __useSubMonthScrolling() {
18572
+ return this.$.monthScroller.clientHeight < this.$.monthScroller.itemHeight + this.$.monthScroller.bufferOffset;
18573
+ }
18574
+
18575
+ get calendars() {
18576
+ return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')];
18577
+ }
18578
+
18032
18579
  get focusableDateElement() {
18033
- return [...this.shadowRoot.querySelectorAll('vaadin-month-calendar')]
18034
- .map((calendar) => calendar.focusableDateElement)
18035
- .find(Boolean);
18580
+ return this.calendars.map((calendar) => calendar.focusableDateElement).find(Boolean);
18036
18581
  }
18037
18582
 
18038
18583
  ready() {
@@ -18040,7 +18585,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18040
18585
 
18041
18586
  this.setAttribute('role', 'dialog');
18042
18587
 
18043
- addListener(this, 'tap', this._stopPropagation);
18044
18588
  addListener(this.$.scrollers, 'track', this._track.bind(this));
18045
18589
  addListener(this.shadowRoot.querySelector('[part="clear-button"]'), 'tap', this._clear.bind(this));
18046
18590
  addListener(this.shadowRoot.querySelector('[part="today-button"]'), 'tap', this._onTodayTap.bind(this));
@@ -18085,7 +18629,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18085
18629
  * Scrolls the list to the given Date.
18086
18630
  */
18087
18631
  scrollToDate(date, animate) {
18088
- this._scrollToPosition(this._differenceInMonths(date, this._originDate), animate);
18632
+ const offset = this.__useSubMonthScrolling ? this._calculateWeekScrollOffset(date) : 0;
18633
+ this._scrollToPosition(this._differenceInMonths(date, this._originDate) + offset, animate);
18634
+ this.$.monthScroller.forceUpdate();
18089
18635
  }
18090
18636
 
18091
18637
  /**
@@ -18117,23 +18663,63 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18117
18663
  * Scrolls the month and year scrollers enough to reveal the given date.
18118
18664
  */
18119
18665
  revealDate(date, animate = true) {
18120
- if (date) {
18121
- const diff = this._differenceInMonths(date, this._originDate);
18122
- const scrolledAboveViewport = this.$.monthScroller.position > diff;
18666
+ if (!date) {
18667
+ return;
18668
+ }
18669
+ const diff = this._differenceInMonths(date, this._originDate);
18670
+ // If scroll area does not fit the full month, then always scroll with an offset to
18671
+ // approximately display the week of the date
18672
+ if (this.__useSubMonthScrolling) {
18673
+ const offset = this._calculateWeekScrollOffset(date);
18674
+ this._scrollToPosition(diff + offset, animate);
18675
+ return;
18676
+ }
18123
18677
 
18124
- const visibleArea = Math.max(
18125
- this.$.monthScroller.itemHeight,
18126
- this.$.monthScroller.clientHeight - this.$.monthScroller.bufferOffset * 2,
18127
- );
18128
- const visibleItems = visibleArea / this.$.monthScroller.itemHeight;
18129
- const scrolledBelowViewport = this.$.monthScroller.position + visibleItems - 1 < diff;
18678
+ // Otherwise determine if we need to scroll to make the month of the date visible
18679
+ const scrolledAboveViewport = this.$.monthScroller.position > diff;
18680
+
18681
+ const visibleArea = Math.max(
18682
+ this.$.monthScroller.itemHeight,
18683
+ this.$.monthScroller.clientHeight - this.$.monthScroller.bufferOffset * 2,
18684
+ );
18685
+ const visibleItems = visibleArea / this.$.monthScroller.itemHeight;
18686
+ const scrolledBelowViewport = this.$.monthScroller.position + visibleItems - 1 < diff;
18687
+
18688
+ if (scrolledAboveViewport) {
18689
+ this._scrollToPosition(diff, animate);
18690
+ } else if (scrolledBelowViewport) {
18691
+ this._scrollToPosition(diff - visibleItems + 1, animate);
18692
+ }
18693
+ }
18130
18694
 
18131
- if (scrolledAboveViewport) {
18132
- this._scrollToPosition(diff, animate);
18133
- } else if (scrolledBelowViewport) {
18134
- this._scrollToPosition(diff - visibleItems + 1, animate);
18695
+ /**
18696
+ * Calculates an offset to be added to the month scroll position
18697
+ * when using sub-month scrolling, in order ensure that the week
18698
+ * that the date is in is visible even for small scroll areas.
18699
+ * As the month scroller uses a month as minimal scroll unit
18700
+ * (a value of `1` equals one month), we can not exactly identify
18701
+ * the position of a specific week. This is a best effort
18702
+ * implementation based on manual testing.
18703
+ * @param date the date for which to calculate the offset
18704
+ * @returns {number} the offset
18705
+ * @private
18706
+ */
18707
+ _calculateWeekScrollOffset(date) {
18708
+ // Get first day of month
18709
+ const temp = new Date(0, 0);
18710
+ temp.setFullYear(date.getFullYear());
18711
+ temp.setMonth(date.getMonth());
18712
+ temp.setDate(1);
18713
+ // Determine week (=row index) of date within the month
18714
+ let week = 0;
18715
+ while (temp.getDate() < date.getDate()) {
18716
+ temp.setDate(temp.getDate() + 1);
18717
+ if (temp.getDay() === this.i18n.firstDayOfWeek) {
18718
+ week += 1;
18135
18719
  }
18136
18720
  }
18721
+ // Calculate magic number that approximately keeps the week visible
18722
+ return week / 6;
18137
18723
  }
18138
18724
 
18139
18725
  _initialPositionChanged(initialPosition) {
@@ -18162,7 +18748,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18162
18748
 
18163
18749
  _onYearScrollTouchStart() {
18164
18750
  this._notTapping = false;
18165
- setTimeout(() => (this._notTapping = true), 300);
18751
+ setTimeout(() => {
18752
+ this._notTapping = true;
18753
+ }, 300);
18166
18754
 
18167
18755
  this._repositionMonthScroller();
18168
18756
  }
@@ -18173,7 +18761,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18173
18761
 
18174
18762
  _doIgnoreTaps() {
18175
18763
  this._ignoreTaps = true;
18176
- this._debouncer = Debouncer$1.debounce(this._debouncer, timeOut.after(300), () => (this._ignoreTaps = false));
18764
+ this._debouncer = Debouncer$1.debounce(this._debouncer, timeOut.after(300), () => {
18765
+ this._ignoreTaps = false;
18766
+ });
18177
18767
  }
18178
18768
 
18179
18769
  _formatDisplayed(date, formatDate, label) {
@@ -18204,10 +18794,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18204
18794
  this.scrollToDate(new Date(), true);
18205
18795
  }
18206
18796
 
18207
- _showClear(selectedDate) {
18208
- return !!selectedDate;
18209
- }
18210
-
18211
18797
  _onYearTap(e) {
18212
18798
  if (!this._ignoreTaps && !this._notTapping) {
18213
18799
  const scrollDelta =
@@ -18233,6 +18819,11 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18233
18819
 
18234
18820
  this._targetPosition = targetPosition;
18235
18821
 
18822
+ let revealResolve;
18823
+ this._revealPromise = new Promise((resolve) => {
18824
+ revealResolve = resolve;
18825
+ });
18826
+
18236
18827
  // http://gizma.com/easing/
18237
18828
  const easingFunction = (t, b, c, d) => {
18238
18829
  t /= d / 2;
@@ -18273,7 +18864,9 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18273
18864
 
18274
18865
  this.$.monthScroller.position = this._targetPosition;
18275
18866
  this._targetPosition = undefined;
18276
- this.__tryFocusDate();
18867
+
18868
+ revealResolve();
18869
+ this._revealPromise = undefined;
18277
18870
  }
18278
18871
 
18279
18872
  setTimeout(this._repositionYearScroller.bind(this), 1);
@@ -18389,10 +18982,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18389
18982
  return months - date2.getMonth() + date1.getMonth();
18390
18983
  }
18391
18984
 
18392
- _differenceInYears(date1, date2) {
18393
- return this._differenceInMonths(date1, date2) / 12;
18394
- }
18395
-
18396
18985
  _clear() {
18397
18986
  this._selectDate('');
18398
18987
  }
@@ -18483,51 +19072,44 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18483
19072
  switch (section) {
18484
19073
  case 'calendar':
18485
19074
  if (event.shiftKey) {
18486
- // Return focus back to the input field.
18487
19075
  event.preventDefault();
18488
- this.__focusInput();
19076
+
19077
+ if (this.hasAttribute('fullscreen')) {
19078
+ // Trap focus in the overlay
19079
+ this.$.cancelButton.focus();
19080
+ } else {
19081
+ this.__focusInput();
19082
+ }
18489
19083
  }
18490
19084
  break;
18491
19085
  case 'today':
18492
19086
  if (event.shiftKey) {
18493
- // Browser returns focus back to the calendar.
18494
- // We need to move the scroll to focused date.
18495
- setTimeout(() => this.revealDate(this.focusedDate), 1);
19087
+ event.preventDefault();
19088
+ this.focusDateElement();
18496
19089
  }
18497
19090
  break;
18498
19091
  case 'cancel':
18499
19092
  if (!event.shiftKey) {
18500
- // Return focus back to the input field.
18501
19093
  event.preventDefault();
18502
- this.__focusInput();
19094
+
19095
+ if (this.hasAttribute('fullscreen')) {
19096
+ // Trap focus in the overlay
19097
+ this.focusDateElement();
19098
+ } else {
19099
+ this.__focusInput();
19100
+ }
18503
19101
  }
18504
19102
  break;
18505
19103
  }
18506
19104
  }
18507
19105
 
18508
19106
  __onTodayButtonKeyDown(event) {
18509
- if (this.hasAttribute('fullscreen')) {
18510
- // Do not prevent closing on Esc
18511
- if (event.key !== 'Escape') {
18512
- event.stopPropagation();
18513
- }
18514
- return;
18515
- }
18516
-
18517
19107
  if (event.key === 'Tab') {
18518
19108
  this._onTabKeyDown(event, 'today');
18519
19109
  }
18520
19110
  }
18521
19111
 
18522
19112
  __onCancelButtonKeyDown(event) {
18523
- if (this.hasAttribute('fullscreen')) {
18524
- // Do not prevent closing on Esc
18525
- if (event.key !== 'Escape') {
18526
- event.stopPropagation();
18527
- }
18528
- return;
18529
- }
18530
-
18531
19113
  if (event.key === 'Tab') {
18532
19114
  this._onTabKeyDown(event, 'cancel');
18533
19115
  }
@@ -18556,15 +19138,29 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18556
19138
  if (!keepMonth) {
18557
19139
  this._focusedMonthDate = dateToFocus.getDate();
18558
19140
  }
18559
- await this.focusDateElement();
19141
+ await this.focusDateElement(false);
18560
19142
  }
18561
19143
 
18562
- async focusDateElement() {
19144
+ async focusDateElement(reveal = true) {
18563
19145
  this.__pendingDateFocus = this.focusedDate;
18564
19146
 
18565
- await new Promise((resolve) => {
18566
- requestAnimationFrame(resolve);
18567
- });
19147
+ // Wait for `vaadin-month-calendar` elements to be rendered
19148
+ if (!this.calendars.length) {
19149
+ await new Promise((resolve) => {
19150
+ setTimeout(resolve);
19151
+ });
19152
+ }
19153
+
19154
+ // Reveal focused date unless it has been just set,
19155
+ // which triggers `revealDate()` in the observer.
19156
+ if (reveal) {
19157
+ this.revealDate(this.focusedDate);
19158
+ }
19159
+
19160
+ if (this._revealPromise) {
19161
+ // Wait for focused date to be scrolled into view.
19162
+ await this._revealPromise;
19163
+ }
18568
19164
 
18569
19165
  this.__tryFocusDate();
18570
19166
  }
@@ -18662,10 +19258,6 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
18662
19258
  todayMidnight.setDate(today.getDate());
18663
19259
  return this._dateAllowed(todayMidnight, min, max);
18664
19260
  }
18665
-
18666
- _stopPropagation(e) {
18667
- e.stopPropagation();
18668
- }
18669
19261
  }
18670
19262
 
18671
19263
  customElements.define(DatePickerOverlayContent.is, DatePickerOverlayContent);
@@ -18813,260 +19405,88 @@ const DelegateFocusMixin = dedupingMixin(
18813
19405
  /**
18814
19406
  * Focus event does not bubble, so we dispatch it manually
18815
19407
  * on the host element to support adding focus listeners
18816
- * when the focusable element is placed in light DOM.
18817
- * @param {FocusEvent} event
18818
- * @protected
18819
- */
18820
- _onFocus(event) {
18821
- event.stopPropagation();
18822
- this.dispatchEvent(new Event('focus'));
18823
- }
18824
-
18825
- /**
18826
- * Blur event does not bubble, so we dispatch it manually
18827
- * on the host element to support adding blur listeners
18828
- * when the focusable element is placed in light DOM.
18829
- * @param {FocusEvent} event
18830
- * @protected
18831
- */
18832
- _onBlur(event) {
18833
- event.stopPropagation();
18834
- this.dispatchEvent(new Event('blur'));
18835
- }
18836
-
18837
- /**
18838
- * @param {Event} event
18839
- * @return {boolean}
18840
- * @protected
18841
- * @override
18842
- */
18843
- _shouldSetFocus(event) {
18844
- return event.target === this.focusElement;
18845
- }
18846
-
18847
- /**
18848
- * @param {boolean} disabled
18849
- * @param {boolean} oldDisabled
18850
- * @protected
18851
- * @override
18852
- */
18853
- _disabledChanged(disabled, oldDisabled) {
18854
- super._disabledChanged(disabled, oldDisabled);
18855
-
18856
- if (this.focusElement) {
18857
- this.focusElement.disabled = disabled;
18858
- }
18859
-
18860
- if (disabled) {
18861
- this.blur();
18862
- }
18863
- }
18864
-
18865
- /**
18866
- * Override an observer from `TabindexMixin`.
18867
- * Do not call super to remove tabindex attribute
18868
- * from the host after it has been forwarded.
18869
- * @param {string} tabindex
18870
- * @protected
18871
- * @override
18872
- */
18873
- _tabindexChanged(tabindex) {
18874
- this.__forwardTabIndex(tabindex);
18875
- }
18876
-
18877
- /** @private */
18878
- __forwardTabIndex(tabindex) {
18879
- if (tabindex !== undefined && this.focusElement) {
18880
- this.focusElement.tabIndex = tabindex;
18881
-
18882
- // Preserve tabindex="-1" on the host element
18883
- if (tabindex !== -1) {
18884
- this.tabindex = undefined;
18885
- }
18886
- }
18887
-
18888
- if (this.disabled && tabindex) {
18889
- // If tabindex attribute was changed while component was disabled
18890
- if (tabindex !== -1) {
18891
- this._lastTabIndex = tabindex;
18892
- }
18893
- this.tabindex = undefined;
18894
- }
18895
- }
18896
- },
18897
- );
18898
-
18899
- /**
18900
- * @license
18901
- * Copyright (c) 2021 - 2022 Vaadin Ltd.
18902
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
18903
- */
18904
-
18905
- /**
18906
- * A controller for providing content to slot element and observing changes.
18907
- */
18908
- class SlotController extends EventTarget {
18909
- /**
18910
- * Ensure that every instance has unique ID.
18911
- *
18912
- * @param {string} slotName
18913
- * @param {HTMLElement} host
18914
- * @return {string}
18915
- * @protected
18916
- */
18917
- static generateId(slotName, host) {
18918
- const prefix = slotName || 'default';
18919
-
18920
- // Support dash-case slot names e.g. "error-message"
18921
- const field = `${dashToCamelCase(prefix)}Id`;
18922
-
18923
- // Maintain the unique ID counter for a given prefix.
18924
- this[field] = 1 + this[field] || 0;
18925
-
18926
- return `${prefix}-${host.localName}-${this[field]}`;
18927
- }
18928
-
18929
- constructor(host, slotName, slotFactory, slotInitializer) {
18930
- super();
18931
-
18932
- this.host = host;
18933
- this.slotName = slotName;
18934
- this.slotFactory = slotFactory;
18935
- this.slotInitializer = slotInitializer;
18936
- this.defaultId = SlotController.generateId(slotName, host);
18937
- }
18938
-
18939
- hostConnected() {
18940
- if (!this.initialized) {
18941
- let node = this.getSlotChild();
18942
-
18943
- if (!node) {
18944
- node = this.attachDefaultNode();
18945
- } else {
18946
- this.node = node;
18947
- this.initCustomNode(node);
18948
- }
18949
-
18950
- this.initNode(node);
18951
-
18952
- // TODO: Consider making this behavior opt-in to improve performance.
18953
- this.observe();
18954
-
18955
- this.initialized = true;
18956
- }
18957
- }
18958
-
18959
- /**
18960
- * Create and attach default node using the slot factory.
18961
- * @return {Node | undefined}
18962
- * @protected
18963
- */
18964
- attachDefaultNode() {
18965
- const { host, slotName, slotFactory } = this;
18966
-
18967
- // Check if the node was created previously and if so, reuse it.
18968
- let node = this.defaultNode;
18969
-
18970
- // Slot factory is optional, some slots don't have default content.
18971
- if (!node && slotFactory) {
18972
- node = slotFactory(host);
18973
- if (node instanceof Element) {
18974
- if (slotName !== '') {
18975
- node.setAttribute('slot', slotName);
18976
- }
18977
- this.node = node;
18978
- this.defaultNode = node;
18979
- }
18980
- }
18981
-
18982
- if (node) {
18983
- host.appendChild(node);
18984
- }
18985
-
18986
- return node;
18987
- }
18988
-
18989
- /**
18990
- * Return a reference to the node managed by the controller.
18991
- * @return {Node}
18992
- */
18993
- getSlotChild() {
18994
- const { slotName } = this;
18995
- return Array.from(this.host.childNodes).find((node) => {
18996
- // Either an element (any slot) or a text node (only un-named slot).
18997
- return (
18998
- (node.nodeType === Node.ELEMENT_NODE && node.slot === slotName) ||
18999
- (node.nodeType === Node.TEXT_NODE && node.textContent.trim() && slotName === '')
19000
- );
19001
- });
19002
- }
19003
-
19004
- /**
19005
- * @param {Node} node
19006
- * @protected
19007
- */
19008
- initNode(node) {
19009
- const { slotInitializer } = this;
19010
- // Don't try to bind `this` to initializer (normally it's arrow function).
19011
- // Instead, pass the host as a first argument to access component's state.
19012
- if (slotInitializer) {
19013
- slotInitializer(this.host, node);
19014
- }
19015
- }
19408
+ * when the focusable element is placed in light DOM.
19409
+ * @param {FocusEvent} event
19410
+ * @protected
19411
+ */
19412
+ _onFocus(event) {
19413
+ event.stopPropagation();
19414
+ this.dispatchEvent(new Event('focus'));
19415
+ }
19016
19416
 
19017
- /**
19018
- * Override to initialize the newly added custom node.
19019
- *
19020
- * @param {Node} _node
19021
- * @protected
19022
- */
19023
- initCustomNode(_node) {}
19417
+ /**
19418
+ * Blur event does not bubble, so we dispatch it manually
19419
+ * on the host element to support adding blur listeners
19420
+ * when the focusable element is placed in light DOM.
19421
+ * @param {FocusEvent} event
19422
+ * @protected
19423
+ */
19424
+ _onBlur(event) {
19425
+ event.stopPropagation();
19426
+ this.dispatchEvent(new Event('blur'));
19427
+ }
19024
19428
 
19025
- /**
19026
- * Override to teardown slotted node when it's removed.
19027
- *
19028
- * @param {Node} _node
19029
- * @protected
19030
- */
19031
- teardownNode(_node) {}
19429
+ /**
19430
+ * @param {Event} event
19431
+ * @return {boolean}
19432
+ * @protected
19433
+ * @override
19434
+ */
19435
+ _shouldSetFocus(event) {
19436
+ return event.target === this.focusElement;
19437
+ }
19032
19438
 
19033
- /**
19034
- * Setup the observer to manage slot content changes.
19035
- * @protected
19036
- */
19037
- observe() {
19038
- const { slotName } = this;
19039
- const selector = slotName === '' ? 'slot:not([name])' : `slot[name=${slotName}]`;
19040
- const slot = this.host.shadowRoot.querySelector(selector);
19439
+ /**
19440
+ * @param {boolean} disabled
19441
+ * @param {boolean} oldDisabled
19442
+ * @protected
19443
+ * @override
19444
+ */
19445
+ _disabledChanged(disabled, oldDisabled) {
19446
+ super._disabledChanged(disabled, oldDisabled);
19041
19447
 
19042
- this.__slotObserver = new FlattenedNodesObserver(slot, (info) => {
19043
- // TODO: support default slot with multiple nodes (e.g. confirm-dialog)
19044
- const current = this.node;
19045
- const newNode = info.addedNodes.find((node) => node !== current);
19448
+ if (this.focusElement) {
19449
+ this.focusElement.disabled = disabled;
19450
+ }
19046
19451
 
19047
- if (info.removedNodes.length) {
19048
- info.removedNodes.forEach((node) => {
19049
- this.teardownNode(node);
19050
- });
19452
+ if (disabled) {
19453
+ this.blur();
19454
+ }
19051
19455
  }
19052
19456
 
19053
- if (newNode) {
19054
- // Custom node is added, remove the current one.
19055
- if (current && current.isConnected) {
19056
- this.host.removeChild(current);
19057
- }
19457
+ /**
19458
+ * Override an observer from `TabindexMixin`.
19459
+ * Do not call super to remove tabindex attribute
19460
+ * from the host after it has been forwarded.
19461
+ * @param {string} tabindex
19462
+ * @protected
19463
+ * @override
19464
+ */
19465
+ _tabindexChanged(tabindex) {
19466
+ this.__forwardTabIndex(tabindex);
19467
+ }
19058
19468
 
19059
- this.node = newNode;
19469
+ /** @private */
19470
+ __forwardTabIndex(tabindex) {
19471
+ if (tabindex !== undefined && this.focusElement) {
19472
+ this.focusElement.tabIndex = tabindex;
19060
19473
 
19061
- if (newNode !== this.defaultNode) {
19062
- this.initCustomNode(newNode);
19474
+ // Preserve tabindex="-1" on the host element
19475
+ if (tabindex !== -1) {
19476
+ this.tabindex = undefined;
19477
+ }
19478
+ }
19063
19479
 
19064
- this.initNode(newNode);
19480
+ if (this.disabled && tabindex) {
19481
+ // If tabindex attribute was changed while component was disabled
19482
+ if (tabindex !== -1) {
19483
+ this._lastTabIndex = tabindex;
19484
+ }
19485
+ this.tabindex = undefined;
19065
19486
  }
19066
19487
  }
19067
- });
19068
- }
19069
- }
19488
+ },
19489
+ );
19070
19490
 
19071
19491
  /**
19072
19492
  * @license
@@ -19088,6 +19508,7 @@ class ErrorController extends SlotController {
19088
19508
 
19089
19509
  this.__updateHasError();
19090
19510
  },
19511
+ true,
19091
19512
  );
19092
19513
  }
19093
19514
 
@@ -19123,7 +19544,7 @@ class ErrorController extends SlotController {
19123
19544
  }
19124
19545
 
19125
19546
  /**
19126
- * Override to initialize the newly added custom label.
19547
+ * Override to initialize the newly added custom error message.
19127
19548
  *
19128
19549
  * @param {Node} errorNode
19129
19550
  * @protected
@@ -19141,7 +19562,7 @@ class ErrorController extends SlotController {
19141
19562
  }
19142
19563
 
19143
19564
  /**
19144
- * Override to cleanup label node when it's removed.
19565
+ * Override to cleanup error message node when it's removed.
19145
19566
  *
19146
19567
  * @param {Node} node
19147
19568
  * @protected
@@ -19154,7 +19575,7 @@ class ErrorController extends SlotController {
19154
19575
  if (!errorNode && node !== this.defaultNode) {
19155
19576
  errorNode = this.attachDefaultNode();
19156
19577
 
19157
- // Run initializer to update default label and ID.
19578
+ // Run initializer to update default error message ID.
19158
19579
  this.initNode(errorNode);
19159
19580
  }
19160
19581
 
@@ -19202,63 +19623,6 @@ class ErrorController extends SlotController {
19202
19623
  }
19203
19624
  }
19204
19625
 
19205
- /**
19206
- * @license
19207
- * Copyright (c) 2021 - 2022 Vaadin Ltd.
19208
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
19209
- */
19210
-
19211
- /**
19212
- * @param {string} value
19213
- * @return {Set<string>}
19214
- */
19215
- function deserializeAttributeValue(value) {
19216
- if (!value) {
19217
- return new Set();
19218
- }
19219
-
19220
- return new Set(value.split(' '));
19221
- }
19222
-
19223
- /**
19224
- * @param {Set<string>} values
19225
- * @return {string}
19226
- */
19227
- function serializeAttributeValue(values) {
19228
- return [...values].join(' ');
19229
- }
19230
-
19231
- /**
19232
- * Adds a value to an attribute containing space-delimited values.
19233
- *
19234
- * @param {HTMLElement} element
19235
- * @param {string} attr
19236
- * @param {string} value
19237
- */
19238
- function addValueToAttribute(element, attr, value) {
19239
- const values = deserializeAttributeValue(element.getAttribute(attr));
19240
- values.add(value);
19241
- element.setAttribute(attr, serializeAttributeValue(values));
19242
- }
19243
-
19244
- /**
19245
- * Removes a value from an attribute containing space-delimited values.
19246
- * If the value is the last one, the whole attribute is removed.
19247
- *
19248
- * @param {HTMLElement} element
19249
- * @param {string} attr
19250
- * @param {string} value
19251
- */
19252
- function removeValueFromAttribute(element, attr, value) {
19253
- const values = deserializeAttributeValue(element.getAttribute(attr));
19254
- values.delete(value);
19255
- if (values.size === 0) {
19256
- element.removeAttribute(attr);
19257
- return;
19258
- }
19259
- element.setAttribute(attr, serializeAttributeValue(values));
19260
- }
19261
-
19262
19626
  /**
19263
19627
  * @license
19264
19628
  * Copyright (c) 2021 - 2022 Vaadin Ltd.
@@ -19433,7 +19797,7 @@ class FieldAriaController {
19433
19797
 
19434
19798
  /**
19435
19799
  * @license
19436
- * Copyright (c) 2021 Vaadin Ltd.
19800
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
19437
19801
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
19438
19802
  */
19439
19803
 
@@ -19443,7 +19807,7 @@ class FieldAriaController {
19443
19807
  class HelperController extends SlotController {
19444
19808
  constructor(host) {
19445
19809
  // Do not provide slot factory, as only create helper lazily.
19446
- super(host, 'helper');
19810
+ super(host, 'helper', null, null, true);
19447
19811
  }
19448
19812
 
19449
19813
  get helperId() {
@@ -19514,7 +19878,11 @@ class HelperController extends SlotController {
19514
19878
  return false;
19515
19879
  }
19516
19880
 
19517
- return helperNode.children.length > 0 || this.__isNotEmpty(helperNode.textContent);
19881
+ return (
19882
+ helperNode.children.length > 0 ||
19883
+ (helperNode.nodeType === Node.ELEMENT_NODE && customElements.get(helperNode.localName)) ||
19884
+ this.__isNotEmpty(helperNode.textContent)
19885
+ );
19518
19886
  }
19519
19887
 
19520
19888
  /**
@@ -19640,6 +20008,7 @@ class LabelController extends SlotController {
19640
20008
 
19641
20009
  this.__observeLabel(node);
19642
20010
  },
20011
+ true,
19643
20012
  );
19644
20013
  }
19645
20014
 
@@ -19814,7 +20183,7 @@ class LabelController extends SlotController {
19814
20183
  * A mixin to provide label via corresponding property or named slot.
19815
20184
  *
19816
20185
  * @polymerMixin
19817
- * @mixes SlotMixin
20186
+ * @mixes ControllerMixin
19818
20187
  */
19819
20188
  const LabelMixin = dedupingMixin(
19820
20189
  (superclass) =>
@@ -19846,6 +20215,12 @@ const LabelMixin = dedupingMixin(
19846
20215
  super();
19847
20216
 
19848
20217
  this._labelController = new LabelController(this);
20218
+ }
20219
+
20220
+ /** @protected */
20221
+ ready() {
20222
+ super.ready();
20223
+
19849
20224
  this.addController(this._labelController);
19850
20225
  }
19851
20226
 
@@ -19893,12 +20268,17 @@ const ValidateMixin = dedupingMixin(
19893
20268
  }
19894
20269
 
19895
20270
  /**
19896
- * Returns true if field is valid, and sets `invalid` based on the field validity.
20271
+ * Validates the field and sets the `invalid` property based on the result.
20272
+ *
20273
+ * The method fires a `validated` event with the result of the validation.
19897
20274
  *
19898
20275
  * @return {boolean} True if the value is valid.
19899
20276
  */
19900
20277
  validate() {
19901
- return !(this.invalid = !this.checkValidity());
20278
+ const isValid = this.checkValidity();
20279
+ this._setInvalid(!isValid);
20280
+ this.dispatchEvent(new CustomEvent('validated', { detail: { valid: isValid } }));
20281
+ return isValid;
19902
20282
  }
19903
20283
 
19904
20284
  /**
@@ -19909,6 +20289,35 @@ const ValidateMixin = dedupingMixin(
19909
20289
  checkValidity() {
19910
20290
  return !this.required || !!this.value;
19911
20291
  }
20292
+
20293
+ /**
20294
+ * @param {boolean} invalid
20295
+ * @protected
20296
+ */
20297
+ _setInvalid(invalid) {
20298
+ if (this._shouldSetInvalid(invalid)) {
20299
+ this.invalid = invalid;
20300
+ }
20301
+ }
20302
+
20303
+ /**
20304
+ * Override this method to define whether the given `invalid` state should be set.
20305
+ *
20306
+ * @param {boolean} _invalid
20307
+ * @return {boolean}
20308
+ * @protected
20309
+ */
20310
+ _shouldSetInvalid(_invalid) {
20311
+ return true;
20312
+ }
20313
+
20314
+ /**
20315
+ * Fired whenever the field is validated.
20316
+ *
20317
+ * @event validated
20318
+ * @param {Object} detail
20319
+ * @param {boolean} detail.valid the result of the validation.
20320
+ */
19912
20321
  },
19913
20322
  );
19914
20323
 
@@ -19997,10 +20406,6 @@ const FieldMixin = (superclass) =>
19997
20406
  this._helperController = new HelperController(this);
19998
20407
  this._errorController = new ErrorController(this);
19999
20408
 
20000
- this.addController(this._fieldAriaController);
20001
- this.addController(this._helperController);
20002
- this.addController(this._errorController);
20003
-
20004
20409
  this._labelController.addEventListener('label-changed', (event) => {
20005
20410
  const { hasLabel, node } = event.detail;
20006
20411
  this.__labelChanged(hasLabel, node);
@@ -20012,6 +20417,15 @@ const FieldMixin = (superclass) =>
20012
20417
  });
20013
20418
  }
20014
20419
 
20420
+ /** @protected */
20421
+ ready() {
20422
+ super.ready();
20423
+
20424
+ this.addController(this._fieldAriaController);
20425
+ this.addController(this._helperController);
20426
+ this.addController(this._errorController);
20427
+ }
20428
+
20015
20429
  /** @private */
20016
20430
  __helperChanged(hasHelper, helperNode) {
20017
20431
  if (hasHelper) {
@@ -20067,7 +20481,7 @@ const FieldMixin = (superclass) =>
20067
20481
  }
20068
20482
 
20069
20483
  /**
20070
- * @param {boolean} required
20484
+ * @param {boolean} invalid
20071
20485
  * @protected
20072
20486
  */
20073
20487
  _invalidChanged(invalid) {
@@ -20267,13 +20681,23 @@ const InputMixin = dedupingMixin(
20267
20681
  observer: '_valueChanged',
20268
20682
  notify: true,
20269
20683
  },
20684
+
20685
+ /**
20686
+ * When true, the input element has a non-empty value entered by the user.
20687
+ * @protected
20688
+ */
20689
+ _hasInputValue: {
20690
+ type: Boolean,
20691
+ value: false,
20692
+ observer: '_hasInputValueChanged',
20693
+ },
20270
20694
  };
20271
20695
  }
20272
20696
 
20273
20697
  constructor() {
20274
20698
  super();
20275
20699
 
20276
- this._boundOnInput = this._onInput.bind(this);
20700
+ this._boundOnInput = this.__onInput.bind(this);
20277
20701
  this._boundOnChange = this._onChange.bind(this);
20278
20702
  }
20279
20703
 
@@ -20288,6 +20712,7 @@ const InputMixin = dedupingMixin(
20288
20712
  * Add event listeners to the input element instance.
20289
20713
  * Override this method to add custom listeners.
20290
20714
  * @param {!HTMLElement} input
20715
+ * @protected
20291
20716
  */
20292
20717
  _addInputListeners(input) {
20293
20718
  input.addEventListener('input', this._boundOnInput);
@@ -20297,6 +20722,7 @@ const InputMixin = dedupingMixin(
20297
20722
  /**
20298
20723
  * Remove event listeners from the input element instance.
20299
20724
  * @param {!HTMLElement} input
20725
+ * @protected
20300
20726
  */
20301
20727
  _removeInputListeners(input) {
20302
20728
  input.removeEventListener('input', this._boundOnInput);
@@ -20310,7 +20736,6 @@ const InputMixin = dedupingMixin(
20310
20736
  * for example to skip this in certain conditions.
20311
20737
  * @param {string} value
20312
20738
  * @protected
20313
- * @override
20314
20739
  */
20315
20740
  _forwardInputValue(value) {
20316
20741
  // Value might be set before an input element is initialized.
@@ -20327,7 +20752,11 @@ const InputMixin = dedupingMixin(
20327
20752
  }
20328
20753
  }
20329
20754
 
20330
- /** @protected */
20755
+ /**
20756
+ * @param {HTMLElement | undefined} input
20757
+ * @param {HTMLElement | undefined} oldInput
20758
+ * @protected
20759
+ */
20331
20760
  _inputElementChanged(input, oldInput) {
20332
20761
  if (input) {
20333
20762
  this._addInputListeners(input);
@@ -20336,17 +20765,43 @@ const InputMixin = dedupingMixin(
20336
20765
  }
20337
20766
  }
20338
20767
 
20768
+ /**
20769
+ * Observer to notify about the change of private property.
20770
+ *
20771
+ * @private
20772
+ */
20773
+ _hasInputValueChanged(hasValue, oldHasValue) {
20774
+ if (hasValue || oldHasValue) {
20775
+ this.dispatchEvent(new CustomEvent('has-input-value-changed'));
20776
+ }
20777
+ }
20778
+
20779
+ /**
20780
+ * An input event listener used to update `_hasInputValue` property.
20781
+ * Do not override this method.
20782
+ *
20783
+ * @param {Event} event
20784
+ * @private
20785
+ */
20786
+ __onInput(event) {
20787
+ this._setHasInputValue(event);
20788
+ this._onInput(event);
20789
+ }
20790
+
20339
20791
  /**
20340
20792
  * An input event listener used to update the field value.
20341
- * Override this method with an actual implementation.
20342
- * @param {Event} _event
20793
+ *
20794
+ * @param {Event} event
20343
20795
  * @protected
20344
- * @override
20345
20796
  */
20346
20797
  _onInput(event) {
20798
+ // In the case a custom web component is passed as `inputElement`,
20799
+ // the actual native input element, on which the event occurred,
20800
+ // can be inside shadow trees.
20801
+ const target = event.composedPath()[0];
20347
20802
  // Ignore fake input events e.g. used by clear button.
20348
20803
  this.__userInput = event.isTrusted;
20349
- this.value = event.target.value;
20804
+ this.value = target.value;
20350
20805
  this.__userInput = false;
20351
20806
  }
20352
20807
 
@@ -20355,12 +20810,12 @@ const InputMixin = dedupingMixin(
20355
20810
  * Override this method with an actual implementation.
20356
20811
  * @param {Event} _event
20357
20812
  * @protected
20358
- * @override
20359
20813
  */
20360
20814
  _onChange(_event) {}
20361
20815
 
20362
20816
  /**
20363
20817
  * Toggle the has-value attribute based on the value property.
20818
+ *
20364
20819
  * @param {boolean} hasValue
20365
20820
  * @protected
20366
20821
  */
@@ -20373,10 +20828,9 @@ const InputMixin = dedupingMixin(
20373
20828
  * @param {string | undefined} newVal
20374
20829
  * @param {string | undefined} oldVal
20375
20830
  * @protected
20376
- * @override
20377
20831
  */
20378
20832
  _valueChanged(newVal, oldVal) {
20379
- this._toggleHasValue(newVal !== '' && newVal != null);
20833
+ this._toggleHasValue(this._hasValue);
20380
20834
 
20381
20835
  // Setting initial value to empty string, do nothing.
20382
20836
  if (newVal === '' && oldVal === undefined) {
@@ -20391,6 +20845,30 @@ const InputMixin = dedupingMixin(
20391
20845
  // Setting a value programmatically, sync it to input element.
20392
20846
  this._forwardInputValue(newVal);
20393
20847
  }
20848
+
20849
+ /**
20850
+ * Indicates whether the value is different from the default one.
20851
+ * Override if the `value` property has a type other than `string`.
20852
+ *
20853
+ * @protected
20854
+ */
20855
+ get _hasValue() {
20856
+ return this.value != null && this.value !== '';
20857
+ }
20858
+
20859
+ /**
20860
+ * Sets the `_hasInputValue` property based on the `input` event.
20861
+ *
20862
+ * @param {InputEvent} event
20863
+ * @protected
20864
+ */
20865
+ _setHasInputValue(event) {
20866
+ // In the case a custom web component is passed as `inputElement`,
20867
+ // the actual native input element, on which the event occurred,
20868
+ // can be inside shadow trees.
20869
+ const target = event.composedPath()[0];
20870
+ this._hasInputValue = target.value.length > 0;
20871
+ }
20394
20872
  },
20395
20873
  );
20396
20874
 
@@ -20462,26 +20940,32 @@ const InputConstraintsMixin = dedupingMixin(
20462
20940
  _createConstraintsObserver() {
20463
20941
  // This complex observer needs to be added dynamically instead of using `static get observers()`
20464
20942
  // to make it possible to tweak this behavior in classes that apply this mixin.
20465
- this._createMethodObserver(`_constraintsChanged(${this.constructor.constraints.join(', ')})`);
20943
+ this._createMethodObserver(`_constraintsChanged(stateTarget, ${this.constructor.constraints.join(', ')})`);
20466
20944
  }
20467
20945
 
20468
20946
  /**
20469
20947
  * Override this method to implement custom validation constraints.
20948
+ * @param {HTMLElement | undefined} stateTarget
20470
20949
  * @param {unknown[]} constraints
20471
20950
  * @protected
20472
20951
  */
20473
- _constraintsChanged(...constraints) {
20474
- // Prevent marking field as invalid when setting required state
20475
- // or any other constraint before a user has entered the value.
20476
- if (!this.invalid) {
20952
+ _constraintsChanged(stateTarget, ...constraints) {
20953
+ // The input element's validity cannot be determined until
20954
+ // all the necessary constraint attributes aren't set on it.
20955
+ if (!stateTarget) {
20477
20956
  return;
20478
20957
  }
20479
20958
 
20480
- if (this._hasValidConstraints(constraints)) {
20959
+ const hasConstraints = this._hasValidConstraints(constraints);
20960
+ const isLastConstraintRemoved = this.__previousHasConstraints && !hasConstraints;
20961
+
20962
+ if ((this._hasValue || this.invalid) && hasConstraints) {
20481
20963
  this.validate();
20482
- } else {
20483
- this.invalid = false;
20964
+ } else if (isLastConstraintRemoved) {
20965
+ this._setInvalid(false);
20484
20966
  }
20967
+
20968
+ this.__previousHasConstraints = hasConstraints;
20485
20969
  }
20486
20970
 
20487
20971
  /**
@@ -20516,6 +21000,82 @@ const InputConstraintsMixin = dedupingMixin(
20516
21000
  },
20517
21001
  );
20518
21002
 
21003
+ /**
21004
+ * @license
21005
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
21006
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
21007
+ */
21008
+
21009
+ const stylesMap = new WeakMap();
21010
+
21011
+ /**
21012
+ * Get all the styles inserted into root.
21013
+ * @param {DocumentOrShadowRoot} root
21014
+ * @return {Set<string>}
21015
+ */
21016
+ function getRootStyles(root) {
21017
+ if (!stylesMap.has(root)) {
21018
+ stylesMap.set(root, new Set());
21019
+ }
21020
+
21021
+ return stylesMap.get(root);
21022
+ }
21023
+
21024
+ /**
21025
+ * Insert styles into the root.
21026
+ * @param {string} styles
21027
+ * @param {DocumentOrShadowRoot} root
21028
+ */
21029
+ function insertStyles(styles, root) {
21030
+ const style = document.createElement('style');
21031
+ style.textContent = styles;
21032
+
21033
+ if (root === document) {
21034
+ document.head.appendChild(style);
21035
+ } else {
21036
+ root.insertBefore(style, root.firstChild);
21037
+ }
21038
+ }
21039
+
21040
+ /**
21041
+ * Mixin to insert styles into the outer scope to handle slotted components.
21042
+ * This is useful e.g. to hide native `<input type="number">` controls.
21043
+ *
21044
+ * @polymerMixin
21045
+ */
21046
+ const SlotStylesMixin = dedupingMixin(
21047
+ (superclass) =>
21048
+ class SlotStylesMixinClass extends superclass {
21049
+ /**
21050
+ * List of styles to insert into root.
21051
+ * @protected
21052
+ */
21053
+ get slotStyles() {
21054
+ return {};
21055
+ }
21056
+
21057
+ /** @protected */
21058
+ connectedCallback() {
21059
+ super.connectedCallback();
21060
+
21061
+ this.__applySlotStyles();
21062
+ }
21063
+
21064
+ /** @private */
21065
+ __applySlotStyles() {
21066
+ const root = this.getRootNode();
21067
+ const rootStyles = getRootStyles(root);
21068
+
21069
+ this.slotStyles.forEach((styles) => {
21070
+ if (!rootStyles.has(styles)) {
21071
+ insertStyles(styles, root);
21072
+ rootStyles.add(styles);
21073
+ }
21074
+ });
21075
+ }
21076
+ },
21077
+ );
21078
+
20519
21079
  /**
20520
21080
  * @license
20521
21081
  * Copyright (c) 2021 - 2022 Vaadin Ltd.
@@ -20530,13 +21090,31 @@ const InputConstraintsMixin = dedupingMixin(
20530
21090
  * @mixes FieldMixin
20531
21091
  * @mixes InputConstraintsMixin
20532
21092
  * @mixes KeyboardMixin
21093
+ * @mixes SlotStylesMixin
20533
21094
  */
20534
21095
  const InputControlMixin = (superclass) =>
20535
- class InputControlMixinClass extends DelegateFocusMixin(
20536
- InputConstraintsMixin(FieldMixin(KeyboardMixin(superclass))),
21096
+ class InputControlMixinClass extends SlotStylesMixin(
21097
+ DelegateFocusMixin(InputConstraintsMixin(FieldMixin(KeyboardMixin(superclass)))),
20537
21098
  ) {
20538
21099
  static get properties() {
20539
21100
  return {
21101
+ /**
21102
+ * A pattern matched against individual characters the user inputs.
21103
+ *
21104
+ * When set, the field will prevent:
21105
+ * - `keydown` events if the entered key doesn't match `/^allowedCharPattern$/`
21106
+ * - `paste` events if the pasted text doesn't match `/^allowedCharPattern*$/`
21107
+ * - `drop` events if the dropped text doesn't match `/^allowedCharPattern*$/`
21108
+ *
21109
+ * For example, to allow entering only numbers and minus signs, use:
21110
+ * `allowedCharPattern = "[\\d-]"`
21111
+ * @attr {string} allowed-char-pattern
21112
+ */
21113
+ allowedCharPattern: {
21114
+ type: String,
21115
+ observer: '_allowedCharPatternChanged',
21116
+ },
21117
+
20540
21118
  /**
20541
21119
  * If true, the input text gets fully selected when the field is focused using click or touch / tap.
20542
21120
  */
@@ -20594,6 +21172,14 @@ const InputControlMixin = (superclass) =>
20594
21172
  return [...super.delegateAttrs, 'name', 'type', 'placeholder', 'readonly', 'invalid', 'title'];
20595
21173
  }
20596
21174
 
21175
+ constructor() {
21176
+ super();
21177
+
21178
+ this._boundOnPaste = this._onPaste.bind(this);
21179
+ this._boundOnDrop = this._onDrop.bind(this);
21180
+ this._boundOnBeforeInput = this._onBeforeInput.bind(this);
21181
+ }
21182
+
20597
21183
  /**
20598
21184
  * Any element extending this mixin is required to implement this getter.
20599
21185
  * It returns the reference to the clear button element.
@@ -20605,12 +21191,26 @@ const InputControlMixin = (superclass) =>
20605
21191
  return null;
20606
21192
  }
20607
21193
 
21194
+ /** @protected */
21195
+ get slotStyles() {
21196
+ // Needed for Safari, where ::slotted(...)::placeholder does not work
21197
+ return [
21198
+ `
21199
+ :is(input[slot='input'], textarea[slot='textarea'])::placeholder {
21200
+ font: inherit;
21201
+ color: inherit;
21202
+ }
21203
+ `,
21204
+ ];
21205
+ }
21206
+
20608
21207
  /** @protected */
20609
21208
  ready() {
20610
21209
  super.ready();
20611
21210
 
20612
21211
  if (this.clearElement) {
20613
21212
  this.clearElement.addEventListener('click', (e) => this._onClearButtonClick(e));
21213
+ this.clearElement.addEventListener('mousedown', (e) => this._onClearButtonMouseDown(e));
20614
21214
  }
20615
21215
  }
20616
21216
 
@@ -20620,10 +21220,20 @@ const InputControlMixin = (superclass) =>
20620
21220
  */
20621
21221
  _onClearButtonClick(event) {
20622
21222
  event.preventDefault();
20623
- this.inputElement.focus();
20624
21223
  this.__clear();
20625
21224
  }
20626
21225
 
21226
+ /**
21227
+ * @param {Event} event
21228
+ * @protected
21229
+ */
21230
+ _onClearButtonMouseDown(event) {
21231
+ event.preventDefault();
21232
+ if (!isTouch) {
21233
+ this.inputElement.focus();
21234
+ }
21235
+ }
21236
+
20627
21237
  /**
20628
21238
  * Override an event listener from `DelegateFocusMixin`.
20629
21239
  * @param {FocusEvent} event
@@ -20686,6 +21296,115 @@ const InputControlMixin = (superclass) =>
20686
21296
  this.inputElement.dispatchEvent(new Event('change', { bubbles: true }));
20687
21297
  }
20688
21298
 
21299
+ /**
21300
+ * Override a method from `InputMixin`.
21301
+ * @param {!HTMLElement} input
21302
+ * @protected
21303
+ * @override
21304
+ */
21305
+ _addInputListeners(input) {
21306
+ super._addInputListeners(input);
21307
+
21308
+ input.addEventListener('paste', this._boundOnPaste);
21309
+ input.addEventListener('drop', this._boundOnDrop);
21310
+ input.addEventListener('beforeinput', this._boundOnBeforeInput);
21311
+ }
21312
+
21313
+ /**
21314
+ * Override a method from `InputMixin`.
21315
+ * @param {!HTMLElement} input
21316
+ * @protected
21317
+ * @override
21318
+ */
21319
+ _removeInputListeners(input) {
21320
+ super._removeInputListeners(input);
21321
+
21322
+ input.removeEventListener('paste', this._boundOnPaste);
21323
+ input.removeEventListener('drop', this._boundOnDrop);
21324
+ input.removeEventListener('beforeinput', this._boundOnBeforeInput);
21325
+ }
21326
+
21327
+ /**
21328
+ * Override an event listener from `KeyboardMixin`.
21329
+ * @param {!KeyboardEvent} event
21330
+ * @protected
21331
+ * @override
21332
+ */
21333
+ _onKeyDown(event) {
21334
+ super._onKeyDown(event);
21335
+
21336
+ if (this.allowedCharPattern && !this.__shouldAcceptKey(event)) {
21337
+ event.preventDefault();
21338
+ this._markInputPrevented();
21339
+ }
21340
+ }
21341
+
21342
+ /** @protected */
21343
+ _markInputPrevented() {
21344
+ // Add input-prevented attribute for 200ms
21345
+ this.setAttribute('input-prevented', '');
21346
+ this._preventInputDebouncer = Debouncer$1.debounce(this._preventInputDebouncer, timeOut.after(200), () => {
21347
+ this.removeAttribute('input-prevented');
21348
+ });
21349
+ }
21350
+
21351
+ /** @private */
21352
+ __shouldAcceptKey(event) {
21353
+ return (
21354
+ event.metaKey ||
21355
+ event.ctrlKey ||
21356
+ !event.key || // Allow typing anything if event.key is not supported
21357
+ event.key.length !== 1 || // Allow "Backspace", "ArrowLeft" etc.
21358
+ this.__allowedCharRegExp.test(event.key)
21359
+ );
21360
+ }
21361
+
21362
+ /** @private */
21363
+ _onPaste(e) {
21364
+ if (this.allowedCharPattern) {
21365
+ const pastedText = e.clipboardData.getData('text');
21366
+ if (!this.__allowedTextRegExp.test(pastedText)) {
21367
+ e.preventDefault();
21368
+ this._markInputPrevented();
21369
+ }
21370
+ }
21371
+ }
21372
+
21373
+ /** @private */
21374
+ _onDrop(e) {
21375
+ if (this.allowedCharPattern) {
21376
+ const draggedText = e.dataTransfer.getData('text');
21377
+ if (!this.__allowedTextRegExp.test(draggedText)) {
21378
+ e.preventDefault();
21379
+ this._markInputPrevented();
21380
+ }
21381
+ }
21382
+ }
21383
+
21384
+ /** @private */
21385
+ _onBeforeInput(e) {
21386
+ // The `beforeinput` event covers all the cases for `allowedCharPattern`: keyboard, pasting and dropping,
21387
+ // but it is still experimental technology so we can't rely on it. It's used here just as an additional check,
21388
+ // because it seems to be the only way to detect and prevent specific keys on mobile devices.
21389
+ // See https://github.com/vaadin/vaadin-text-field/issues/429
21390
+ if (this.allowedCharPattern && e.data && !this.__allowedTextRegExp.test(e.data)) {
21391
+ e.preventDefault();
21392
+ this._markInputPrevented();
21393
+ }
21394
+ }
21395
+
21396
+ /** @private */
21397
+ _allowedCharPatternChanged(charPattern) {
21398
+ if (charPattern) {
21399
+ try {
21400
+ this.__allowedCharRegExp = new RegExp(`^${charPattern}$`);
21401
+ this.__allowedTextRegExp = new RegExp(`^${charPattern}*$`);
21402
+ } catch (e) {
21403
+ console.error(e);
21404
+ }
21405
+ }
21406
+ }
21407
+
20689
21408
  /**
20690
21409
  * Fired when the user commits a value change.
20691
21410
  *
@@ -20724,14 +21443,13 @@ class InputController extends SlotController {
20724
21443
  }
20725
21444
 
20726
21445
  // Ensure every instance has unique ID
20727
- const uniqueId = (InputController._uniqueInputId = 1 + InputController._uniqueInputId || 0);
20728
- host._inputId = `${host.localName}-${uniqueId}`;
20729
- node.id = host._inputId;
21446
+ node.id = this.defaultId;
20730
21447
 
20731
21448
  if (typeof callback === 'function') {
20732
21449
  callback(node);
20733
21450
  }
20734
21451
  },
21452
+ true,
20735
21453
  );
20736
21454
  }
20737
21455
  }
@@ -20913,7 +21631,9 @@ class VirtualKeyboardController {
20913
21631
  * @param {function(new:HTMLElement)} subclass
20914
21632
  */
20915
21633
  const DatePickerMixin = (subclass) =>
20916
- class VaadinDatePickerMixin extends ControllerMixin(DelegateFocusMixin(InputMixin(KeyboardMixin(subclass)))) {
21634
+ class VaadinDatePickerMixin extends ControllerMixin(
21635
+ DelegateFocusMixin(InputConstraintsMixin(KeyboardMixin(subclass))),
21636
+ ) {
20917
21637
  static get properties() {
20918
21638
  return {
20919
21639
  /**
@@ -20942,7 +21662,6 @@ const DatePickerMixin = (subclass) =>
20942
21662
  */
20943
21663
  value: {
20944
21664
  type: String,
20945
- observer: '_valueChanged',
20946
21665
  notify: true,
20947
21666
  value: '',
20948
21667
  },
@@ -20998,13 +21717,6 @@ const DatePickerMixin = (subclass) =>
20998
21717
  value: '(max-width: 420px), (max-height: 420px)',
20999
21718
  },
21000
21719
 
21001
- /**
21002
- * An array of ancestor elements whose -webkit-overflow-scrolling is forced from value
21003
- * 'touch' to value 'auto' in order to prevent them from clipping the dropdown. iOS only.
21004
- * @private
21005
- */
21006
- _touchPrevented: Array,
21007
-
21008
21720
  /**
21009
21721
  * The object used to localize this component.
21010
21722
  * To change the default localization, replace the entire
@@ -21060,6 +21772,16 @@ const DatePickerMixin = (subclass) =>
21060
21772
  * // Translation of the Cancel button text.
21061
21773
  * cancel: 'Cancel',
21062
21774
  *
21775
+ * // Used for adjusting the year value when parsing dates with short years.
21776
+ * // The year values between 0 and 99 are evaluated and adjusted.
21777
+ * // Example: for a referenceDate of 1970-10-30;
21778
+ * // dateToBeParsed: 40-10-30, result: 1940-10-30
21779
+ * // dateToBeParsed: 80-10-30, result: 1980-10-30
21780
+ * // dateToBeParsed: 10-10-30, result: 2010-10-30
21781
+ * // Supported date format: ISO 8601 `"YYYY-MM-DD"` (default)
21782
+ * // The default value is the current date.
21783
+ * referenceDate: '',
21784
+ *
21063
21785
  * // A function to format given `Object` as
21064
21786
  * // date string. Object is in the format `{ day: ..., month: ..., year: ... }`
21065
21787
  * // Note: The argument month is 0-based. This means that January = 0 and December = 11.
@@ -21113,11 +21835,12 @@ const DatePickerMixin = (subclass) =>
21113
21835
  calendar: 'Calendar',
21114
21836
  today: 'Today',
21115
21837
  cancel: 'Cancel',
21116
- formatDate: (d) => {
21838
+ referenceDate: '',
21839
+ formatDate(d) {
21117
21840
  const yearStr = String(d.year).replace(/\d+/, (y) => '0000'.substr(y.length) + y);
21118
21841
  return [d.month + 1, d.day, yearStr].join('/');
21119
21842
  },
21120
- parseDate: (text) => {
21843
+ parseDate(text) {
21121
21844
  const parts = text.split('/');
21122
21845
  const today = new Date();
21123
21846
  let date,
@@ -21125,12 +21848,13 @@ const DatePickerMixin = (subclass) =>
21125
21848
  year = today.getFullYear();
21126
21849
 
21127
21850
  if (parts.length === 3) {
21851
+ month = parseInt(parts[0]) - 1;
21852
+ date = parseInt(parts[1]);
21128
21853
  year = parseInt(parts[2]);
21129
21854
  if (parts[2].length < 3 && year >= 0) {
21130
- year += year < 50 ? 2000 : 1900;
21855
+ const usedReferenceDate = this.referenceDate ? parseDate(this.referenceDate) : new Date();
21856
+ year = getAdjustedYear(usedReferenceDate, year, month, date);
21131
21857
  }
21132
- month = parseInt(parts[0]) - 1;
21133
- date = parseInt(parts[1]);
21134
21858
  } else if (parts.length === 2) {
21135
21859
  month = parseInt(parts[0]) - 1;
21136
21860
  date = parseInt(parts[1]);
@@ -21160,7 +21884,6 @@ const DatePickerMixin = (subclass) =>
21160
21884
  */
21161
21885
  min: {
21162
21886
  type: String,
21163
- observer: '_minChanged',
21164
21887
  },
21165
21888
 
21166
21889
  /**
@@ -21174,28 +21897,26 @@ const DatePickerMixin = (subclass) =>
21174
21897
  */
21175
21898
  max: {
21176
21899
  type: String,
21177
- observer: '_maxChanged',
21178
21900
  },
21179
21901
 
21180
21902
  /**
21181
21903
  * The earliest date that can be selected. All earlier dates will be disabled.
21182
- * @type {Date | string}
21904
+ * @type {Date | undefined}
21183
21905
  * @protected
21184
21906
  */
21185
21907
  _minDate: {
21186
21908
  type: Date,
21187
- // Null does not work here because minimizer passes undefined to overlay (#351)
21188
- value: '',
21909
+ computed: '__computeMinOrMaxDate(min)',
21189
21910
  },
21190
21911
 
21191
21912
  /**
21192
21913
  * The latest date that can be selected. All later dates will be disabled.
21193
- * @type {Date | string}
21914
+ * @type {Date | undefined}
21194
21915
  * @protected
21195
21916
  */
21196
21917
  _maxDate: {
21197
21918
  type: Date,
21198
- value: '',
21919
+ computed: '__computeMinOrMaxDate(max)',
21199
21920
  },
21200
21921
 
21201
21922
  /** @private */
@@ -21210,12 +21931,6 @@ const DatePickerMixin = (subclass) =>
21210
21931
  value: isIOS,
21211
21932
  },
21212
21933
 
21213
- /** @private */
21214
- _webkitOverflowScroll: {
21215
- type: Boolean,
21216
- value: document.createElement('div').style.webkitOverflowScrolling === '',
21217
- },
21218
-
21219
21934
  /** @private */
21220
21935
  _focusOverlayOnOpen: Boolean,
21221
21936
 
@@ -21231,6 +21946,10 @@ const DatePickerMixin = (subclass) =>
21231
21946
  ];
21232
21947
  }
21233
21948
 
21949
+ static get constraints() {
21950
+ return [...super.constraints, 'min', 'max'];
21951
+ }
21952
+
21234
21953
  /**
21235
21954
  * Override a getter from `InputControlMixin` to make it optional
21236
21955
  * and to prevent warning when a clear button is missing,
@@ -21291,18 +22010,13 @@ const DatePickerMixin = (subclass) =>
21291
22010
 
21292
22011
  if (!this.opened) {
21293
22012
  if (this.autoOpenDisabled) {
21294
- const parsedDate = this._getParsedDate();
21295
- if (this._isValidDate(parsedDate)) {
21296
- this._selectDate(parsedDate);
21297
- }
22013
+ this._selectParsedOrFocusedDate();
21298
22014
  }
21299
22015
 
21300
- if (this.inputElement.value === '' && this.__dispatchChange) {
21301
- this.validate();
22016
+ this.validate();
22017
+
22018
+ if (this._inputValue === '' && this.value !== '') {
21302
22019
  this.value = '';
21303
- this.__dispatchChange = false;
21304
- } else {
21305
- this.validate();
21306
22020
  }
21307
22021
  }
21308
22022
  }
@@ -21371,14 +22085,19 @@ const DatePickerMixin = (subclass) =>
21371
22085
  this.$.overlay.removeAttribute('disable-upgrade');
21372
22086
  this._overlayInitialized = true;
21373
22087
 
21374
- this.$.overlay.addEventListener('opened-changed', (e) => (this.opened = e.detail.value));
22088
+ this.$.overlay.addEventListener('opened-changed', (e) => {
22089
+ this.opened = e.detail.value;
22090
+ });
21375
22091
 
21376
22092
  this.$.overlay.addEventListener('vaadin-overlay-escape-press', () => {
21377
22093
  this._focusedDate = this._selectedDate;
21378
22094
  this._close();
21379
22095
  });
21380
22096
 
21381
- this._overlayContent.addEventListener('close', this._close.bind(this));
22097
+ this._overlayContent.addEventListener('close', () => {
22098
+ this._close();
22099
+ });
22100
+
21382
22101
  this._overlayContent.addEventListener('focus-input', this._focusAndSelect.bind(this));
21383
22102
 
21384
22103
  // User confirmed selected date by clicking the calendar.
@@ -21387,34 +22106,29 @@ const DatePickerMixin = (subclass) =>
21387
22106
 
21388
22107
  this._selectDate(e.detail.date);
21389
22108
 
21390
- this._close(e);
22109
+ this._close();
21391
22110
  });
21392
22111
 
21393
- // User confirmed selected date by pressing Enter or Today.
22112
+ // User confirmed selected date by pressing Enter, Space, or Today.
21394
22113
  this._overlayContent.addEventListener('date-selected', (e) => {
21395
- this.__userConfirmedDate = true;
22114
+ // Reset if a date is deselected.
22115
+ this.__userConfirmedDate = !!e.detail.date;
21396
22116
 
21397
22117
  this._selectDate(e.detail.date);
21398
22118
  });
21399
22119
 
21400
- // Keep focus attribute in focusElement for styling
22120
+ // Set focus-ring attribute when moving focus to the overlay
22121
+ // by pressing Tab or arrow key, after opening it on click.
21401
22122
  this._overlayContent.addEventListener('focusin', () => {
21402
- this._setFocused(true);
22123
+ if (this._keyboardActive) {
22124
+ this._setFocused(true);
22125
+ }
21403
22126
  });
21404
22127
 
21405
22128
  this.addEventListener('mousedown', () => this.__bringToFront());
21406
22129
  this.addEventListener('touchstart', () => this.__bringToFront());
21407
22130
  }
21408
22131
 
21409
- /**
21410
- * Returns true if `value` is valid, and sets the `invalid` flag appropriately.
21411
- *
21412
- * @return {boolean} True if the value is valid and sets the `invalid` flag appropriately
21413
- */
21414
- validate() {
21415
- return !(this.invalid = !this.checkValidity());
21416
- }
21417
-
21418
22132
  /**
21419
22133
  * Returns true if the current input value satisfies all constraints (if any)
21420
22134
  *
@@ -21425,7 +22139,7 @@ const DatePickerMixin = (subclass) =>
21425
22139
  checkValidity() {
21426
22140
  const inputValid =
21427
22141
  !this._inputValue ||
21428
- (this._selectedDate && this._inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
22142
+ (!!this._selectedDate && this._inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
21429
22143
  const minMaxValid = !this._selectedDate || dateAllowed(this._selectedDate, this._minDate, this._maxDate);
21430
22144
 
21431
22145
  let inputValidity = true;
@@ -21441,6 +22155,51 @@ const DatePickerMixin = (subclass) =>
21441
22155
  return inputValid && minMaxValid && inputValidity;
21442
22156
  }
21443
22157
 
22158
+ /**
22159
+ * Override method inherited from `FocusMixin`
22160
+ * to not call `_setFocused(true)` when focus
22161
+ * is restored after closing overlay on click,
22162
+ * and to avoid removing `focus-ring` attribute.
22163
+ *
22164
+ * @param {!FocusEvent} _event
22165
+ * @return {boolean}
22166
+ * @protected
22167
+ * @override
22168
+ */
22169
+ _shouldSetFocus(_event) {
22170
+ return !this._shouldKeepFocusRing;
22171
+ }
22172
+
22173
+ /**
22174
+ * Override method inherited from `FocusMixin`
22175
+ * to prevent removing the `focused` attribute:
22176
+ * - when moving focus to the overlay content,
22177
+ * - when closing on date click / outside click.
22178
+ *
22179
+ * @param {!FocusEvent} _event
22180
+ * @return {boolean}
22181
+ * @protected
22182
+ * @override
22183
+ */
22184
+ _shouldRemoveFocus(_event) {
22185
+ return !this.opened;
22186
+ }
22187
+
22188
+ /**
22189
+ * Override method inherited from `FocusMixin`
22190
+ * to store the `focus-ring` state to restore
22191
+ * it later when closing on outside click.
22192
+ *
22193
+ * @param {boolean} focused
22194
+ * @protected
22195
+ * @override
22196
+ */
22197
+ _setFocused(focused) {
22198
+ super._setFocused(focused);
22199
+
22200
+ this._shouldKeepFocusRing = focused && this._keyboardActive;
22201
+ }
22202
+
21444
22203
  /**
21445
22204
  * Select date on user interaction and set the flag
21446
22205
  * to fire change event if necessary.
@@ -21460,10 +22219,7 @@ const DatePickerMixin = (subclass) =>
21460
22219
  }
21461
22220
 
21462
22221
  /** @private */
21463
- _close(e) {
21464
- if (e) {
21465
- e.stopPropagation();
21466
- }
22222
+ _close() {
21467
22223
  this._focus();
21468
22224
  this.close();
21469
22225
  }
@@ -21475,21 +22231,6 @@ const DatePickerMixin = (subclass) =>
21475
22231
  });
21476
22232
  }
21477
22233
 
21478
- /** @private */
21479
- _parseDate(str) {
21480
- // Parsing with RegExp to ensure correct format
21481
- const parts = /^([-+]\d{1}|\d{2,4}|[-+]\d{6})-(\d{1,2})-(\d{1,2})$/.exec(str);
21482
- if (!parts) {
21483
- return;
21484
- }
21485
-
21486
- const date = new Date(0, 0); // Wrong date (1900-01-01), but with midnight in local time
21487
- date.setFullYear(parseInt(parts[1], 10));
21488
- date.setMonth(parseInt(parts[2], 10) - 1);
21489
- date.setDate(parseInt(parts[3], 10));
21490
- return date;
21491
- }
21492
-
21493
22234
  /** @private */
21494
22235
  // eslint-disable-next-line max-params
21495
22236
  _isNoInput(inputElement, fullscreen, ios, i18n, opened, autoOpenDisabled) {
@@ -21592,48 +22333,47 @@ const DatePickerMixin = (subclass) =>
21592
22333
  }
21593
22334
  }
21594
22335
 
21595
- /** @private */
21596
- _handleDateChange(property, value, oldValue) {
21597
- if (!value) {
21598
- this[property] = '';
21599
- return;
21600
- }
22336
+ /**
22337
+ * Override the value observer from `InputMixin` to implement custom
22338
+ * handling of the `value` property. The date-picker doesn't forward
22339
+ * the value directly to the input like the default implementation of `InputMixin`.
22340
+ * Instead, it parses the value into a date, puts it in `_selectedDate` which
22341
+ * is then displayed in the input with respect to the specified date format.
22342
+ *
22343
+ * @param {string | undefined} value
22344
+ * @param {string | undefined} oldValue
22345
+ * @protected
22346
+ * @override
22347
+ */
22348
+ _valueChanged(value, oldValue) {
22349
+ const newDate = parseDate(value);
21601
22350
 
21602
- const date = this._parseDate(value);
21603
- if (!date) {
22351
+ if (value && !newDate) {
22352
+ // The new value cannot be parsed, revert the old value.
21604
22353
  this.value = oldValue;
21605
22354
  return;
21606
22355
  }
21607
- if (!dateEquals(this[property], date)) {
21608
- this[property] = date;
21609
- if (this.value) {
21610
- this.validate();
21611
- }
21612
- }
21613
- }
21614
-
21615
- /** @private */
21616
- _valueChanged(value, oldValue) {
21617
- this._handleDateChange('_selectedDate', value, oldValue);
21618
22356
 
21619
- this._toggleHasValue(!!value);
21620
- }
22357
+ if (value) {
22358
+ if (!dateEquals(this._selectedDate, newDate)) {
22359
+ // Update the date instance only if the date has actually changed.
22360
+ this._selectedDate = newDate;
21621
22361
 
21622
- /** @private */
21623
- _minChanged(value, oldValue) {
21624
- this._handleDateChange('_minDate', value, oldValue);
21625
- }
22362
+ if (oldValue !== undefined) {
22363
+ // Validate only if `value` changes after initialization.
22364
+ this.validate();
22365
+ }
22366
+ }
22367
+ } else {
22368
+ this._selectedDate = null;
22369
+ }
21626
22370
 
21627
- /** @private */
21628
- _maxChanged(value, oldValue) {
21629
- this._handleDateChange('_maxDate', value, oldValue);
22371
+ this._toggleHasValue(this._hasValue);
21630
22372
  }
21631
22373
 
21632
22374
  /** @protected */
21633
22375
  _onOverlayOpened() {
21634
- this._openedWithFocusRing = this.hasAttribute('focus-ring');
21635
-
21636
- const parsedInitialPosition = this._parseDate(this.initialPosition);
22376
+ const parsedInitialPosition = parseDate(this.initialPosition);
21637
22377
 
21638
22378
  const initialPosition =
21639
22379
  this._selectedDate || this._overlayContent.initialPosition || parsedInitialPosition || new Date();
@@ -21652,10 +22392,6 @@ const DatePickerMixin = (subclass) =>
21652
22392
 
21653
22393
  window.addEventListener('scroll', this._boundOnScroll, true);
21654
22394
 
21655
- if (this._webkitOverflowScroll) {
21656
- this._touchPrevented = this._preventWebkitOverflowScrollingTouch(this.parentElement);
21657
- }
21658
-
21659
22395
  if (this._focusOverlayOnOpen) {
21660
22396
  this._overlayContent.focusDateElement();
21661
22397
  this._focusOverlayOnOpen = false;
@@ -21669,25 +22405,6 @@ const DatePickerMixin = (subclass) =>
21669
22405
  }
21670
22406
  }
21671
22407
 
21672
- // A hack needed for iOS to prevent dropdown from being clipped in an
21673
- // ancestor container with -webkit-overflow-scrolling: touch;
21674
- /** @private */
21675
- _preventWebkitOverflowScrollingTouch(element) {
21676
- const result = [];
21677
- while (element) {
21678
- if (window.getComputedStyle(element).webkitOverflowScrolling === 'touch') {
21679
- const oldInlineValue = element.style.webkitOverflowScrolling;
21680
- element.style.webkitOverflowScrolling = 'auto';
21681
- result.push({
21682
- element,
21683
- oldInlineValue,
21684
- });
21685
- }
21686
- element = element.parentElement;
21687
- }
21688
- return result;
21689
- }
21690
-
21691
22408
  /** @private */
21692
22409
  _selectParsedOrFocusedDate() {
21693
22410
  // Select the parsed input or focused date
@@ -21714,13 +22431,6 @@ const DatePickerMixin = (subclass) =>
21714
22431
  _onOverlayClosed() {
21715
22432
  window.removeEventListener('scroll', this._boundOnScroll, true);
21716
22433
 
21717
- if (this._touchPrevented) {
21718
- this._touchPrevented.forEach(
21719
- (prevented) => (prevented.element.style.webkitOverflowScrolling = prevented.oldInlineValue),
21720
- );
21721
- this._touchPrevented = [];
21722
- }
21723
-
21724
22434
  // No need to select date on close if it was confirmed by the user.
21725
22435
  if (this.__userConfirmedDate) {
21726
22436
  this.__userConfirmedDate = false;
@@ -21736,11 +22446,6 @@ const DatePickerMixin = (subclass) =>
21736
22446
  if (!this.value) {
21737
22447
  this.validate();
21738
22448
  }
21739
-
21740
- // If the input isn't focused when overlay closes (fullscreen mode), clear focused state
21741
- if (this.getRootNode().activeElement !== this.inputElement) {
21742
- this._setFocused(false);
21743
- }
21744
22449
  }
21745
22450
 
21746
22451
  /** @private */
@@ -21793,10 +22498,7 @@ const DatePickerMixin = (subclass) =>
21793
22498
  _onChange(event) {
21794
22499
  // For change event on the native <input> blur, after the input is cleared,
21795
22500
  // we schedule change event to be dispatched on date-picker blur.
21796
- if (
21797
- this.inputElement.value === '' &&
21798
- !(event.detail && event.detail.sourceEvent && event.detail.sourceEvent.__fromClearButton)
21799
- ) {
22501
+ if (this._inputValue === '') {
21800
22502
  this.__dispatchChange = true;
21801
22503
  }
21802
22504
 
@@ -21883,7 +22585,7 @@ const DatePickerMixin = (subclass) =>
21883
22585
  if (e.shiftKey) {
21884
22586
  this._overlayContent.focusCancel();
21885
22587
  } else {
21886
- this._overlayContent.focusDate(this._focusedDate);
22588
+ this._overlayContent.focusDateElement();
21887
22589
  }
21888
22590
  }
21889
22591
  break;
@@ -21898,21 +22600,15 @@ const DatePickerMixin = (subclass) =>
21898
22600
  * @override
21899
22601
  */
21900
22602
  _onEnter(_event) {
21901
- const parsedDate = this._getParsedDate();
21902
- const isValidDate = this._isValidDate(parsedDate);
22603
+ const oldValue = this.value;
21903
22604
  if (this.opened) {
21904
- if (this._overlayInitialized && this._overlayContent.focusedDate && isValidDate) {
21905
- this._selectDate(this._overlayContent.focusedDate);
21906
- }
22605
+ // Closing will implicitly select parsed or focused date
21907
22606
  this.close();
21908
- } else if (!isValidDate && this.inputElement.value !== '') {
21909
- this.validate();
21910
22607
  } else {
21911
- const oldValue = this.value;
21912
22608
  this._selectParsedOrFocusedDate();
21913
- if (oldValue === this.value) {
21914
- this.validate();
21915
- }
22609
+ }
22610
+ if (oldValue === this.value) {
22611
+ this.validate();
21916
22612
  }
21917
22613
  }
21918
22614
 
@@ -21954,7 +22650,7 @@ const DatePickerMixin = (subclass) =>
21954
22650
  /** @private */
21955
22651
  _getParsedDate(inputValue = this._inputValue) {
21956
22652
  const dateObject = this.i18n.parseDate && this.i18n.parseDate(inputValue);
21957
- const parsedDate = dateObject && this._parseDate(`${dateObject.year}-${dateObject.month + 1}-${dateObject.day}`);
22653
+ const parsedDate = dateObject && parseDate(`${dateObject.year}-${dateObject.month + 1}-${dateObject.day}`);
21958
22654
  return parsedDate;
21959
22655
  }
21960
22656
 
@@ -21976,7 +22672,7 @@ const DatePickerMixin = (subclass) =>
21976
22672
 
21977
22673
  /** @private */
21978
22674
  _userInputValueChanged() {
21979
- if (this.opened && this._inputValue) {
22675
+ if (this._inputValue) {
21980
22676
  const parsedDate = this._getParsedDate();
21981
22677
 
21982
22678
  if (this._isValidDate(parsedDate)) {
@@ -21994,6 +22690,11 @@ const DatePickerMixin = (subclass) =>
21994
22690
  return this.$.overlay.content.querySelector('#overlay-content');
21995
22691
  }
21996
22692
 
22693
+ /** @private */
22694
+ __computeMinOrMaxDate(dateString) {
22695
+ return parseDate(dateString);
22696
+ }
22697
+
21997
22698
  /**
21998
22699
  * Fired when the user commits a value change.
21999
22700
  *
@@ -22105,12 +22806,13 @@ registerStyles('vaadin-date-picker', [inputFieldShared, datePickerStyles], { mod
22105
22806
  * Note: the `theme` attribute value set on `<vaadin-date-picker>` is
22106
22807
  * propagated to the internal components listed above.
22107
22808
  *
22108
- * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
22809
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
22109
22810
  *
22110
22811
  * @fires {Event} change - Fired when the user commits a value change.
22111
22812
  * @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
22112
22813
  * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
22113
22814
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
22815
+ * @fires {CustomEvent} validated - Fired whenever the field is validated.
22114
22816
  *
22115
22817
  * @extends HTMLElement
22116
22818
  * @mixes ElementMixin
@@ -22164,7 +22866,7 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22164
22866
  fullscreen$="[[_fullscreen]]"
22165
22867
  theme$="[[__getOverlayTheme(_theme, _overlayInitialized)]]"
22166
22868
  on-vaadin-overlay-open="_onOverlayOpened"
22167
- on-vaadin-overlay-close="_onOverlayClosed"
22869
+ on-vaadin-overlay-closing="_onOverlayClosed"
22168
22870
  restore-focus-on-close
22169
22871
  restore-focus-node="[[inputElement]]"
22170
22872
  disable-upgrade
@@ -22185,6 +22887,8 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22185
22887
  ></vaadin-date-picker-overlay-content>
22186
22888
  </template>
22187
22889
  </vaadin-date-picker-overlay>
22890
+
22891
+ <slot name="tooltip"></slot>
22188
22892
  `;
22189
22893
  }
22190
22894
 
@@ -22211,6 +22915,11 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22211
22915
  );
22212
22916
  this.addController(new LabelledInputController(this.inputElement, this._labelController));
22213
22917
 
22918
+ this._tooltipController = new TooltipController(this);
22919
+ this.addController(this._tooltipController);
22920
+ this._tooltipController.setPosition('top');
22921
+ this._tooltipController.setShouldShow((target) => !target.opened);
22922
+
22214
22923
  const toggleButton = this.shadowRoot.querySelector('[part="toggle-button"]');
22215
22924
  toggleButton.addEventListener('mousedown', (e) => e.preventDefault());
22216
22925
  }
@@ -22224,11 +22933,6 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22224
22933
 
22225
22934
  /** @private */
22226
22935
  _onVaadinOverlayClose(e) {
22227
- if (this._openedWithFocusRing && this.hasAttribute('focused')) {
22228
- this.setAttribute('focus-ring', '');
22229
- } else if (!this.hasAttribute('focused')) {
22230
- this.blur();
22231
- }
22232
22936
  if (e.detail.sourceEvent && e.detail.sourceEvent.composedPath().includes(this)) {
22233
22937
  e.preventDefault();
22234
22938
  }
@@ -22358,6 +23062,7 @@ const HelperFilters$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLEleme
22358
23062
  this.modalCloseEvent();
22359
23063
  this.showClearButton = true;
22360
23064
  this.filterSelectionHandler(this.filterData);
23065
+ console.log(this.showClearButton);
22361
23066
  }
22362
23067
  resetSearch() {
22363
23068
  this.showClearButton = false;
@@ -22374,23 +23079,23 @@ const HelperFilters$1 = /*@__PURE__*/ proxyCustomElement(class extends HTMLEleme
22374
23079
  this.filterData.filterToCalendar = new Date(event.target.value).toISOString();
22375
23080
  }
22376
23081
  render() {
22377
- return (h$2("div", { class: "HelperFilters", ref: el => this.stylingContainer = el }, h$2("div", { class: "FilterButtonsWrapper" }, h$2("button", { class: "FilterOpen", onClick: () => this.toggleFilterModal() }, translate$1('filterOpen', this.language)), (this.showClearButton || this.quickFiltersActive) ?
23082
+ return (h$2("div", { class: "HelperFilters", ref: el => this.stylingContainer = el }, h$2("div", { class: "FilterButtonsWrapper" }, h$2("button", { class: "FilterOpen", onClick: () => this.toggleFilterModal() }, translate$1('filterOpen', this.language)), console.log('in filter Open', this.showClearButton, this.quickFiltersActive), (this.showClearButton || this.quickFiltersActive) ?
22378
23083
  h$2("button", { class: "FilterClear", onClick: () => this.resetSearch() }, translate$1('filterClear', this.language))
22379
23084
  :
22380
23085
  null), h$2("helper-modal", { "title-modal": "Filter Modal", visible: this.showFilterModal, "client-styling": this.clientStyling, "client-styling-url-content": this.clientStylingUrlContent }, 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))))));
22381
23086
  }
22382
23087
  static get style() { return helperFiltersCss; }
22383
23088
  }, [1, "helper-filters", {
22384
- "showFilterId": [4, "show-filter-id"],
22385
- "activateTicketSearch": [4, "activate-ticket-search"],
22386
- "gameId": [1, "game-id"],
22387
- "playerId": [1, "player-id"],
22388
- "session": [1],
22389
- "postMessage": [4, "post-message"],
22390
- "language": [1],
22391
- "quickFiltersActive": [4, "quick-filters-active"],
22392
- "clientStyling": [1, "client-styling"],
22393
- "clientStylingUrlContent": [1, "client-styling-url-content"],
23089
+ "showFilterId": [516, "show-filter-id"],
23090
+ "activateTicketSearch": [516, "activate-ticket-search"],
23091
+ "gameId": [513, "game-id"],
23092
+ "playerId": [513, "player-id"],
23093
+ "session": [513],
23094
+ "postMessage": [516, "post-message"],
23095
+ "language": [513],
23096
+ "quickFiltersActive": [516, "quick-filters-active"],
23097
+ "clientStyling": [513, "client-styling"],
23098
+ "clientStylingUrlContent": [513, "client-styling-url-content"],
22394
23099
  "showFilterModal": [32],
22395
23100
  "showClearButton": [32],
22396
23101
  "filterData": [32],