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