@everymatrix/helper-filters 0.1.24 → 1.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.14';
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};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;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("finalized"))return !1;this.finalized=!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){}}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.6.1");
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 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=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.
@@ -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.14';
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;
12998
+ }
12999
+ }
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;
13052
13239
  }
13053
13240
  }
13054
- return ta;
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
@@ -13718,100 +14137,38 @@ register({
13718
14137
  * @param {TouchEvent} e
13719
14138
  * @return {void}
13720
14139
  */
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
- }
13789
-
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
- }
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%;
@@ -17847,27 +18390,14 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
17847
18390
  z-index: 2;
17848
18391
  flex-shrink: 0;
17849
18392
  }
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;
17862
- }
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;
18129
18686
 
18130
- if (scrolledAboveViewport) {
18131
- this._scrollToPosition(diff, animate);
18132
- } else if (scrolledBelowViewport) {
18133
- this._scrollToPosition(diff - visibleItems + 1, animate);
18687
+ if (scrolledAboveViewport) {
18688
+ this._scrollToPosition(diff, animate);
18689
+ } else if (scrolledBelowViewport) {
18690
+ this._scrollToPosition(diff - visibleItems + 1, animate);
18691
+ }
18692
+ }
18693
+
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);
@@ -18795,277 +19387,105 @@ const DelegateFocusMixin = dedupingMixin(
18795
19387
  * @param {HTMLElement} element
18796
19388
  * @protected
18797
19389
  */
18798
- _addFocusListeners(element) {
18799
- element.addEventListener('blur', this._boundOnBlur);
18800
- element.addEventListener('focus', this._boundOnFocus);
18801
- }
18802
-
18803
- /**
18804
- * @param {HTMLElement} element
18805
- * @protected
18806
- */
18807
- _removeFocusListeners(element) {
18808
- element.removeEventListener('blur', this._boundOnBlur);
18809
- element.removeEventListener('focus', this._boundOnFocus);
18810
- }
18811
-
18812
- /**
18813
- * Focus event does not bubble, so we dispatch it manually
18814
- * 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
- }
19015
-
19016
- /**
19017
- * Override to initialize the newly added custom node.
19018
- *
19019
- * @param {Node} _node
19020
- * @protected
19021
- */
19022
- initCustomNode(_node) {}
19390
+ _addFocusListeners(element) {
19391
+ element.addEventListener('blur', this._boundOnBlur);
19392
+ element.addEventListener('focus', this._boundOnFocus);
19393
+ }
19023
19394
 
19024
- /**
19025
- * Override to teardown slotted node when it's removed.
19026
- *
19027
- * @param {Node} _node
19028
- * @protected
19029
- */
19030
- teardownNode(_node) {}
19395
+ /**
19396
+ * @param {HTMLElement} element
19397
+ * @protected
19398
+ */
19399
+ _removeFocusListeners(element) {
19400
+ element.removeEventListener('blur', this._boundOnBlur);
19401
+ element.removeEventListener('focus', this._boundOnFocus);
19402
+ }
19031
19403
 
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);
19404
+ /**
19405
+ * Focus event does not bubble, so we dispatch it manually
19406
+ * on the host element to support adding focus listeners
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
+ }
19040
19415
 
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);
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
+ }
19045
19427
 
19046
- if (info.removedNodes.length) {
19047
- info.removedNodes.forEach((node) => {
19048
- this.teardownNode(node);
19049
- });
19428
+ /**
19429
+ * @param {Event} event
19430
+ * @return {boolean}
19431
+ * @protected
19432
+ * @override
19433
+ */
19434
+ _shouldSetFocus(event) {
19435
+ return event.target === this.focusElement;
19050
19436
  }
19051
19437
 
19052
- if (newNode) {
19053
- // Custom node is added, remove the current one.
19054
- if (current && current.isConnected) {
19055
- this.host.removeChild(current);
19438
+ /**
19439
+ * @param {boolean} disabled
19440
+ * @param {boolean} oldDisabled
19441
+ * @protected
19442
+ * @override
19443
+ */
19444
+ _disabledChanged(disabled, oldDisabled) {
19445
+ super._disabledChanged(disabled, oldDisabled);
19446
+
19447
+ if (this.focusElement) {
19448
+ this.focusElement.disabled = disabled;
19056
19449
  }
19057
19450
 
19058
- this.node = newNode;
19451
+ if (disabled) {
19452
+ this.blur();
19453
+ }
19454
+ }
19059
19455
 
19060
- if (newNode !== this.defaultNode) {
19061
- this.initCustomNode(newNode);
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
+ }
19062
19467
 
19063
- this.initNode(newNode);
19468
+ /** @private */
19469
+ __forwardTabIndex(tabindex) {
19470
+ if (tabindex !== undefined && this.focusElement) {
19471
+ this.focusElement.tabIndex = tabindex;
19472
+
19473
+ // Preserve tabindex="-1" on the host element
19474
+ if (tabindex !== -1) {
19475
+ this.tabindex = undefined;
19476
+ }
19477
+ }
19478
+
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,6 +21190,19 @@ 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();
@@ -20685,6 +21284,115 @@ const InputControlMixin = (superclass) =>
20685
21284
  this.inputElement.dispatchEvent(new Event('change', { bubbles: true }));
20686
21285
  }
20687
21286
 
21287
+ /**
21288
+ * Override a method from `InputMixin`.
21289
+ * @param {!HTMLElement} input
21290
+ * @protected
21291
+ * @override
21292
+ */
21293
+ _addInputListeners(input) {
21294
+ super._addInputListeners(input);
21295
+
21296
+ input.addEventListener('paste', this._boundOnPaste);
21297
+ input.addEventListener('drop', this._boundOnDrop);
21298
+ input.addEventListener('beforeinput', this._boundOnBeforeInput);
21299
+ }
21300
+
21301
+ /**
21302
+ * Override a method from `InputMixin`.
21303
+ * @param {!HTMLElement} input
21304
+ * @protected
21305
+ * @override
21306
+ */
21307
+ _removeInputListeners(input) {
21308
+ super._removeInputListeners(input);
21309
+
21310
+ input.removeEventListener('paste', this._boundOnPaste);
21311
+ input.removeEventListener('drop', this._boundOnDrop);
21312
+ input.removeEventListener('beforeinput', this._boundOnBeforeInput);
21313
+ }
21314
+
21315
+ /**
21316
+ * Override an event listener from `KeyboardMixin`.
21317
+ * @param {!KeyboardEvent} event
21318
+ * @protected
21319
+ * @override
21320
+ */
21321
+ _onKeyDown(event) {
21322
+ super._onKeyDown(event);
21323
+
21324
+ if (this.allowedCharPattern && !this.__shouldAcceptKey(event)) {
21325
+ event.preventDefault();
21326
+ this._markInputPrevented();
21327
+ }
21328
+ }
21329
+
21330
+ /** @protected */
21331
+ _markInputPrevented() {
21332
+ // Add input-prevented attribute for 200ms
21333
+ this.setAttribute('input-prevented', '');
21334
+ this._preventInputDebouncer = Debouncer$1.debounce(this._preventInputDebouncer, timeOut.after(200), () => {
21335
+ this.removeAttribute('input-prevented');
21336
+ });
21337
+ }
21338
+
21339
+ /** @private */
21340
+ __shouldAcceptKey(event) {
21341
+ return (
21342
+ event.metaKey ||
21343
+ event.ctrlKey ||
21344
+ !event.key || // Allow typing anything if event.key is not supported
21345
+ event.key.length !== 1 || // Allow "Backspace", "ArrowLeft" etc.
21346
+ this.__allowedCharRegExp.test(event.key)
21347
+ );
21348
+ }
21349
+
21350
+ /** @private */
21351
+ _onPaste(e) {
21352
+ if (this.allowedCharPattern) {
21353
+ const pastedText = e.clipboardData.getData('text');
21354
+ if (!this.__allowedTextRegExp.test(pastedText)) {
21355
+ e.preventDefault();
21356
+ this._markInputPrevented();
21357
+ }
21358
+ }
21359
+ }
21360
+
21361
+ /** @private */
21362
+ _onDrop(e) {
21363
+ if (this.allowedCharPattern) {
21364
+ const draggedText = e.dataTransfer.getData('text');
21365
+ if (!this.__allowedTextRegExp.test(draggedText)) {
21366
+ e.preventDefault();
21367
+ this._markInputPrevented();
21368
+ }
21369
+ }
21370
+ }
21371
+
21372
+ /** @private */
21373
+ _onBeforeInput(e) {
21374
+ // The `beforeinput` event covers all the cases for `allowedCharPattern`: keyboard, pasting and dropping,
21375
+ // but it is still experimental technology so we can't rely on it. It's used here just as an additional check,
21376
+ // because it seems to be the only way to detect and prevent specific keys on mobile devices.
21377
+ // See https://github.com/vaadin/vaadin-text-field/issues/429
21378
+ if (this.allowedCharPattern && e.data && !this.__allowedTextRegExp.test(e.data)) {
21379
+ e.preventDefault();
21380
+ this._markInputPrevented();
21381
+ }
21382
+ }
21383
+
21384
+ /** @private */
21385
+ _allowedCharPatternChanged(charPattern) {
21386
+ if (charPattern) {
21387
+ try {
21388
+ this.__allowedCharRegExp = new RegExp(`^${charPattern}$`);
21389
+ this.__allowedTextRegExp = new RegExp(`^${charPattern}*$`);
21390
+ } catch (e) {
21391
+ console.error(e);
21392
+ }
21393
+ }
21394
+ }
21395
+
20688
21396
  /**
20689
21397
  * Fired when the user commits a value change.
20690
21398
  *
@@ -20723,14 +21431,13 @@ class InputController extends SlotController {
20723
21431
  }
20724
21432
 
20725
21433
  // 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;
21434
+ node.id = this.defaultId;
20729
21435
 
20730
21436
  if (typeof callback === 'function') {
20731
21437
  callback(node);
20732
21438
  }
20733
21439
  },
21440
+ true,
20734
21441
  );
20735
21442
  }
20736
21443
  }
@@ -20912,7 +21619,9 @@ class VirtualKeyboardController {
20912
21619
  * @param {function(new:HTMLElement)} subclass
20913
21620
  */
20914
21621
  const DatePickerMixin = (subclass) =>
20915
- class VaadinDatePickerMixin extends ControllerMixin(DelegateFocusMixin(InputMixin(KeyboardMixin(subclass)))) {
21622
+ class VaadinDatePickerMixin extends ControllerMixin(
21623
+ DelegateFocusMixin(InputConstraintsMixin(KeyboardMixin(subclass))),
21624
+ ) {
20916
21625
  static get properties() {
20917
21626
  return {
20918
21627
  /**
@@ -20941,7 +21650,6 @@ const DatePickerMixin = (subclass) =>
20941
21650
  */
20942
21651
  value: {
20943
21652
  type: String,
20944
- observer: '_valueChanged',
20945
21653
  notify: true,
20946
21654
  value: '',
20947
21655
  },
@@ -20997,13 +21705,6 @@ const DatePickerMixin = (subclass) =>
20997
21705
  value: '(max-width: 420px), (max-height: 420px)',
20998
21706
  },
20999
21707
 
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
21708
  /**
21008
21709
  * The object used to localize this component.
21009
21710
  * To change the default localization, replace the entire
@@ -21059,6 +21760,16 @@ const DatePickerMixin = (subclass) =>
21059
21760
  * // Translation of the Cancel button text.
21060
21761
  * cancel: 'Cancel',
21061
21762
  *
21763
+ * // Used for adjusting the year value when parsing dates with short years.
21764
+ * // The year values between 0 and 99 are evaluated and adjusted.
21765
+ * // Example: for a referenceDate of 1970-10-30;
21766
+ * // dateToBeParsed: 40-10-30, result: 1940-10-30
21767
+ * // dateToBeParsed: 80-10-30, result: 1980-10-30
21768
+ * // dateToBeParsed: 10-10-30, result: 2010-10-30
21769
+ * // Supported date format: ISO 8601 `"YYYY-MM-DD"` (default)
21770
+ * // The default value is the current date.
21771
+ * referenceDate: '',
21772
+ *
21062
21773
  * // A function to format given `Object` as
21063
21774
  * // date string. Object is in the format `{ day: ..., month: ..., year: ... }`
21064
21775
  * // Note: The argument month is 0-based. This means that January = 0 and December = 11.
@@ -21112,11 +21823,12 @@ const DatePickerMixin = (subclass) =>
21112
21823
  calendar: 'Calendar',
21113
21824
  today: 'Today',
21114
21825
  cancel: 'Cancel',
21115
- formatDate: (d) => {
21826
+ referenceDate: '',
21827
+ formatDate(d) {
21116
21828
  const yearStr = String(d.year).replace(/\d+/, (y) => '0000'.substr(y.length) + y);
21117
21829
  return [d.month + 1, d.day, yearStr].join('/');
21118
21830
  },
21119
- parseDate: (text) => {
21831
+ parseDate(text) {
21120
21832
  const parts = text.split('/');
21121
21833
  const today = new Date();
21122
21834
  let date,
@@ -21124,12 +21836,13 @@ const DatePickerMixin = (subclass) =>
21124
21836
  year = today.getFullYear();
21125
21837
 
21126
21838
  if (parts.length === 3) {
21839
+ month = parseInt(parts[0]) - 1;
21840
+ date = parseInt(parts[1]);
21127
21841
  year = parseInt(parts[2]);
21128
21842
  if (parts[2].length < 3 && year >= 0) {
21129
- year += year < 50 ? 2000 : 1900;
21843
+ const usedReferenceDate = this.referenceDate ? parseDate(this.referenceDate) : new Date();
21844
+ year = getAdjustedYear(usedReferenceDate, year, month, date);
21130
21845
  }
21131
- month = parseInt(parts[0]) - 1;
21132
- date = parseInt(parts[1]);
21133
21846
  } else if (parts.length === 2) {
21134
21847
  month = parseInt(parts[0]) - 1;
21135
21848
  date = parseInt(parts[1]);
@@ -21159,7 +21872,6 @@ const DatePickerMixin = (subclass) =>
21159
21872
  */
21160
21873
  min: {
21161
21874
  type: String,
21162
- observer: '_minChanged',
21163
21875
  },
21164
21876
 
21165
21877
  /**
@@ -21173,28 +21885,26 @@ const DatePickerMixin = (subclass) =>
21173
21885
  */
21174
21886
  max: {
21175
21887
  type: String,
21176
- observer: '_maxChanged',
21177
21888
  },
21178
21889
 
21179
21890
  /**
21180
21891
  * The earliest date that can be selected. All earlier dates will be disabled.
21181
- * @type {Date | string}
21892
+ * @type {Date | undefined}
21182
21893
  * @protected
21183
21894
  */
21184
21895
  _minDate: {
21185
21896
  type: Date,
21186
- // Null does not work here because minimizer passes undefined to overlay (#351)
21187
- value: '',
21897
+ computed: '__computeMinOrMaxDate(min)',
21188
21898
  },
21189
21899
 
21190
21900
  /**
21191
21901
  * The latest date that can be selected. All later dates will be disabled.
21192
- * @type {Date | string}
21902
+ * @type {Date | undefined}
21193
21903
  * @protected
21194
21904
  */
21195
21905
  _maxDate: {
21196
21906
  type: Date,
21197
- value: '',
21907
+ computed: '__computeMinOrMaxDate(max)',
21198
21908
  },
21199
21909
 
21200
21910
  /** @private */
@@ -21209,12 +21919,6 @@ const DatePickerMixin = (subclass) =>
21209
21919
  value: isIOS,
21210
21920
  },
21211
21921
 
21212
- /** @private */
21213
- _webkitOverflowScroll: {
21214
- type: Boolean,
21215
- value: document.createElement('div').style.webkitOverflowScrolling === '',
21216
- },
21217
-
21218
21922
  /** @private */
21219
21923
  _focusOverlayOnOpen: Boolean,
21220
21924
 
@@ -21230,6 +21934,10 @@ const DatePickerMixin = (subclass) =>
21230
21934
  ];
21231
21935
  }
21232
21936
 
21937
+ static get constraints() {
21938
+ return [...super.constraints, 'min', 'max'];
21939
+ }
21940
+
21233
21941
  /**
21234
21942
  * Override a getter from `InputControlMixin` to make it optional
21235
21943
  * and to prevent warning when a clear button is missing,
@@ -21290,18 +21998,13 @@ const DatePickerMixin = (subclass) =>
21290
21998
 
21291
21999
  if (!this.opened) {
21292
22000
  if (this.autoOpenDisabled) {
21293
- const parsedDate = this._getParsedDate();
21294
- if (this._isValidDate(parsedDate)) {
21295
- this._selectDate(parsedDate);
21296
- }
22001
+ this._selectParsedOrFocusedDate();
21297
22002
  }
21298
22003
 
21299
- if (this.inputElement.value === '' && this.__dispatchChange) {
21300
- this.validate();
22004
+ this.validate();
22005
+
22006
+ if (this._inputValue === '' && this.value !== '') {
21301
22007
  this.value = '';
21302
- this.__dispatchChange = false;
21303
- } else {
21304
- this.validate();
21305
22008
  }
21306
22009
  }
21307
22010
  }
@@ -21370,14 +22073,19 @@ const DatePickerMixin = (subclass) =>
21370
22073
  this.$.overlay.removeAttribute('disable-upgrade');
21371
22074
  this._overlayInitialized = true;
21372
22075
 
21373
- this.$.overlay.addEventListener('opened-changed', (e) => (this.opened = e.detail.value));
22076
+ this.$.overlay.addEventListener('opened-changed', (e) => {
22077
+ this.opened = e.detail.value;
22078
+ });
21374
22079
 
21375
22080
  this.$.overlay.addEventListener('vaadin-overlay-escape-press', () => {
21376
22081
  this._focusedDate = this._selectedDate;
21377
22082
  this._close();
21378
22083
  });
21379
22084
 
21380
- this._overlayContent.addEventListener('close', this._close.bind(this));
22085
+ this._overlayContent.addEventListener('close', () => {
22086
+ this._close();
22087
+ });
22088
+
21381
22089
  this._overlayContent.addEventListener('focus-input', this._focusAndSelect.bind(this));
21382
22090
 
21383
22091
  // User confirmed selected date by clicking the calendar.
@@ -21386,34 +22094,29 @@ const DatePickerMixin = (subclass) =>
21386
22094
 
21387
22095
  this._selectDate(e.detail.date);
21388
22096
 
21389
- this._close(e);
22097
+ this._close();
21390
22098
  });
21391
22099
 
21392
- // User confirmed selected date by pressing Enter or Today.
22100
+ // User confirmed selected date by pressing Enter, Space, or Today.
21393
22101
  this._overlayContent.addEventListener('date-selected', (e) => {
21394
- this.__userConfirmedDate = true;
22102
+ // Reset if a date is deselected.
22103
+ this.__userConfirmedDate = !!e.detail.date;
21395
22104
 
21396
22105
  this._selectDate(e.detail.date);
21397
22106
  });
21398
22107
 
21399
- // Keep focus attribute in focusElement for styling
22108
+ // Set focus-ring attribute when moving focus to the overlay
22109
+ // by pressing Tab or arrow key, after opening it on click.
21400
22110
  this._overlayContent.addEventListener('focusin', () => {
21401
- this._setFocused(true);
22111
+ if (this._keyboardActive) {
22112
+ this._setFocused(true);
22113
+ }
21402
22114
  });
21403
22115
 
21404
22116
  this.addEventListener('mousedown', () => this.__bringToFront());
21405
22117
  this.addEventListener('touchstart', () => this.__bringToFront());
21406
22118
  }
21407
22119
 
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
22120
  /**
21418
22121
  * Returns true if the current input value satisfies all constraints (if any)
21419
22122
  *
@@ -21424,7 +22127,7 @@ const DatePickerMixin = (subclass) =>
21424
22127
  checkValidity() {
21425
22128
  const inputValid =
21426
22129
  !this._inputValue ||
21427
- (this._selectedDate && this._inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
22130
+ (!!this._selectedDate && this._inputValue === this._getFormattedDate(this.i18n.formatDate, this._selectedDate));
21428
22131
  const minMaxValid = !this._selectedDate || dateAllowed(this._selectedDate, this._minDate, this._maxDate);
21429
22132
 
21430
22133
  let inputValidity = true;
@@ -21440,6 +22143,51 @@ const DatePickerMixin = (subclass) =>
21440
22143
  return inputValid && minMaxValid && inputValidity;
21441
22144
  }
21442
22145
 
22146
+ /**
22147
+ * Override method inherited from `FocusMixin`
22148
+ * to not call `_setFocused(true)` when focus
22149
+ * is restored after closing overlay on click,
22150
+ * and to avoid removing `focus-ring` attribute.
22151
+ *
22152
+ * @param {!FocusEvent} _event
22153
+ * @return {boolean}
22154
+ * @protected
22155
+ * @override
22156
+ */
22157
+ _shouldSetFocus(_event) {
22158
+ return !this._shouldKeepFocusRing;
22159
+ }
22160
+
22161
+ /**
22162
+ * Override method inherited from `FocusMixin`
22163
+ * to prevent removing the `focused` attribute:
22164
+ * - when moving focus to the overlay content,
22165
+ * - when closing on date click / outside click.
22166
+ *
22167
+ * @param {!FocusEvent} _event
22168
+ * @return {boolean}
22169
+ * @protected
22170
+ * @override
22171
+ */
22172
+ _shouldRemoveFocus(_event) {
22173
+ return !this.opened;
22174
+ }
22175
+
22176
+ /**
22177
+ * Override method inherited from `FocusMixin`
22178
+ * to store the `focus-ring` state to restore
22179
+ * it later when closing on outside click.
22180
+ *
22181
+ * @param {boolean} focused
22182
+ * @protected
22183
+ * @override
22184
+ */
22185
+ _setFocused(focused) {
22186
+ super._setFocused(focused);
22187
+
22188
+ this._shouldKeepFocusRing = focused && this._keyboardActive;
22189
+ }
22190
+
21443
22191
  /**
21444
22192
  * Select date on user interaction and set the flag
21445
22193
  * to fire change event if necessary.
@@ -21459,10 +22207,7 @@ const DatePickerMixin = (subclass) =>
21459
22207
  }
21460
22208
 
21461
22209
  /** @private */
21462
- _close(e) {
21463
- if (e) {
21464
- e.stopPropagation();
21465
- }
22210
+ _close() {
21466
22211
  this._focus();
21467
22212
  this.close();
21468
22213
  }
@@ -21474,21 +22219,6 @@ const DatePickerMixin = (subclass) =>
21474
22219
  });
21475
22220
  }
21476
22221
 
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
22222
  /** @private */
21493
22223
  // eslint-disable-next-line max-params
21494
22224
  _isNoInput(inputElement, fullscreen, ios, i18n, opened, autoOpenDisabled) {
@@ -21591,48 +22321,47 @@ const DatePickerMixin = (subclass) =>
21591
22321
  }
21592
22322
  }
21593
22323
 
21594
- /** @private */
21595
- _handleDateChange(property, value, oldValue) {
21596
- if (!value) {
21597
- this[property] = '';
21598
- return;
21599
- }
22324
+ /**
22325
+ * Override the value observer from `InputMixin` to implement custom
22326
+ * handling of the `value` property. The date-picker doesn't forward
22327
+ * the value directly to the input like the default implementation of `InputMixin`.
22328
+ * Instead, it parses the value into a date, puts it in `_selectedDate` which
22329
+ * is then displayed in the input with respect to the specified date format.
22330
+ *
22331
+ * @param {string | undefined} value
22332
+ * @param {string | undefined} oldValue
22333
+ * @protected
22334
+ * @override
22335
+ */
22336
+ _valueChanged(value, oldValue) {
22337
+ const newDate = parseDate(value);
21600
22338
 
21601
- const date = this._parseDate(value);
21602
- if (!date) {
22339
+ if (value && !newDate) {
22340
+ // The new value cannot be parsed, revert the old value.
21603
22341
  this.value = oldValue;
21604
22342
  return;
21605
22343
  }
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
22344
 
21618
- this._toggleHasValue(!!value);
21619
- }
22345
+ if (value) {
22346
+ if (!dateEquals(this._selectedDate, newDate)) {
22347
+ // Update the date instance only if the date has actually changed.
22348
+ this._selectedDate = newDate;
21620
22349
 
21621
- /** @private */
21622
- _minChanged(value, oldValue) {
21623
- this._handleDateChange('_minDate', value, oldValue);
21624
- }
22350
+ if (oldValue !== undefined) {
22351
+ // Validate only if `value` changes after initialization.
22352
+ this.validate();
22353
+ }
22354
+ }
22355
+ } else {
22356
+ this._selectedDate = null;
22357
+ }
21625
22358
 
21626
- /** @private */
21627
- _maxChanged(value, oldValue) {
21628
- this._handleDateChange('_maxDate', value, oldValue);
22359
+ this._toggleHasValue(this._hasValue);
21629
22360
  }
21630
22361
 
21631
22362
  /** @protected */
21632
22363
  _onOverlayOpened() {
21633
- this._openedWithFocusRing = this.hasAttribute('focus-ring');
21634
-
21635
- const parsedInitialPosition = this._parseDate(this.initialPosition);
22364
+ const parsedInitialPosition = parseDate(this.initialPosition);
21636
22365
 
21637
22366
  const initialPosition =
21638
22367
  this._selectedDate || this._overlayContent.initialPosition || parsedInitialPosition || new Date();
@@ -21651,10 +22380,6 @@ const DatePickerMixin = (subclass) =>
21651
22380
 
21652
22381
  window.addEventListener('scroll', this._boundOnScroll, true);
21653
22382
 
21654
- if (this._webkitOverflowScroll) {
21655
- this._touchPrevented = this._preventWebkitOverflowScrollingTouch(this.parentElement);
21656
- }
21657
-
21658
22383
  if (this._focusOverlayOnOpen) {
21659
22384
  this._overlayContent.focusDateElement();
21660
22385
  this._focusOverlayOnOpen = false;
@@ -21668,25 +22393,6 @@ const DatePickerMixin = (subclass) =>
21668
22393
  }
21669
22394
  }
21670
22395
 
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
22396
  /** @private */
21691
22397
  _selectParsedOrFocusedDate() {
21692
22398
  // Select the parsed input or focused date
@@ -21713,13 +22419,6 @@ const DatePickerMixin = (subclass) =>
21713
22419
  _onOverlayClosed() {
21714
22420
  window.removeEventListener('scroll', this._boundOnScroll, true);
21715
22421
 
21716
- if (this._touchPrevented) {
21717
- this._touchPrevented.forEach(
21718
- (prevented) => (prevented.element.style.webkitOverflowScrolling = prevented.oldInlineValue),
21719
- );
21720
- this._touchPrevented = [];
21721
- }
21722
-
21723
22422
  // No need to select date on close if it was confirmed by the user.
21724
22423
  if (this.__userConfirmedDate) {
21725
22424
  this.__userConfirmedDate = false;
@@ -21735,11 +22434,6 @@ const DatePickerMixin = (subclass) =>
21735
22434
  if (!this.value) {
21736
22435
  this.validate();
21737
22436
  }
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
22437
  }
21744
22438
 
21745
22439
  /** @private */
@@ -21792,10 +22486,7 @@ const DatePickerMixin = (subclass) =>
21792
22486
  _onChange(event) {
21793
22487
  // For change event on the native <input> blur, after the input is cleared,
21794
22488
  // 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
- ) {
22489
+ if (this._inputValue === '') {
21799
22490
  this.__dispatchChange = true;
21800
22491
  }
21801
22492
 
@@ -21882,7 +22573,7 @@ const DatePickerMixin = (subclass) =>
21882
22573
  if (e.shiftKey) {
21883
22574
  this._overlayContent.focusCancel();
21884
22575
  } else {
21885
- this._overlayContent.focusDate(this._focusedDate);
22576
+ this._overlayContent.focusDateElement();
21886
22577
  }
21887
22578
  }
21888
22579
  break;
@@ -21897,21 +22588,15 @@ const DatePickerMixin = (subclass) =>
21897
22588
  * @override
21898
22589
  */
21899
22590
  _onEnter(_event) {
21900
- const parsedDate = this._getParsedDate();
21901
- const isValidDate = this._isValidDate(parsedDate);
22591
+ const oldValue = this.value;
21902
22592
  if (this.opened) {
21903
- if (this._overlayInitialized && this._overlayContent.focusedDate && isValidDate) {
21904
- this._selectDate(this._overlayContent.focusedDate);
21905
- }
22593
+ // Closing will implicitly select parsed or focused date
21906
22594
  this.close();
21907
- } else if (!isValidDate && this.inputElement.value !== '') {
21908
- this.validate();
21909
22595
  } else {
21910
- const oldValue = this.value;
21911
22596
  this._selectParsedOrFocusedDate();
21912
- if (oldValue === this.value) {
21913
- this.validate();
21914
- }
22597
+ }
22598
+ if (oldValue === this.value) {
22599
+ this.validate();
21915
22600
  }
21916
22601
  }
21917
22602
 
@@ -21953,7 +22638,7 @@ const DatePickerMixin = (subclass) =>
21953
22638
  /** @private */
21954
22639
  _getParsedDate(inputValue = this._inputValue) {
21955
22640
  const dateObject = this.i18n.parseDate && this.i18n.parseDate(inputValue);
21956
- const parsedDate = dateObject && this._parseDate(`${dateObject.year}-${dateObject.month + 1}-${dateObject.day}`);
22641
+ const parsedDate = dateObject && parseDate(`${dateObject.year}-${dateObject.month + 1}-${dateObject.day}`);
21957
22642
  return parsedDate;
21958
22643
  }
21959
22644
 
@@ -21975,7 +22660,7 @@ const DatePickerMixin = (subclass) =>
21975
22660
 
21976
22661
  /** @private */
21977
22662
  _userInputValueChanged() {
21978
- if (this.opened && this._inputValue) {
22663
+ if (this._inputValue) {
21979
22664
  const parsedDate = this._getParsedDate();
21980
22665
 
21981
22666
  if (this._isValidDate(parsedDate)) {
@@ -21993,6 +22678,11 @@ const DatePickerMixin = (subclass) =>
21993
22678
  return this.$.overlay.content.querySelector('#overlay-content');
21994
22679
  }
21995
22680
 
22681
+ /** @private */
22682
+ __computeMinOrMaxDate(dateString) {
22683
+ return parseDate(dateString);
22684
+ }
22685
+
21996
22686
  /**
21997
22687
  * Fired when the user commits a value change.
21998
22688
  *
@@ -22104,12 +22794,13 @@ registerStyles('vaadin-date-picker', [inputFieldShared, datePickerStyles], { mod
22104
22794
  * Note: the `theme` attribute value set on `<vaadin-date-picker>` is
22105
22795
  * propagated to the internal components listed above.
22106
22796
  *
22107
- * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
22797
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
22108
22798
  *
22109
22799
  * @fires {Event} change - Fired when the user commits a value change.
22110
22800
  * @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
22111
22801
  * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
22112
22802
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
22803
+ * @fires {CustomEvent} validated - Fired whenever the field is validated.
22113
22804
  *
22114
22805
  * @extends HTMLElement
22115
22806
  * @mixes ElementMixin
@@ -22163,7 +22854,7 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22163
22854
  fullscreen$="[[_fullscreen]]"
22164
22855
  theme$="[[__getOverlayTheme(_theme, _overlayInitialized)]]"
22165
22856
  on-vaadin-overlay-open="_onOverlayOpened"
22166
- on-vaadin-overlay-close="_onOverlayClosed"
22857
+ on-vaadin-overlay-closing="_onOverlayClosed"
22167
22858
  restore-focus-on-close
22168
22859
  restore-focus-node="[[inputElement]]"
22169
22860
  disable-upgrade
@@ -22184,6 +22875,8 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22184
22875
  ></vaadin-date-picker-overlay-content>
22185
22876
  </template>
22186
22877
  </vaadin-date-picker-overlay>
22878
+
22879
+ <slot name="tooltip"></slot>
22187
22880
  `;
22188
22881
  }
22189
22882
 
@@ -22210,6 +22903,11 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22210
22903
  );
22211
22904
  this.addController(new LabelledInputController(this.inputElement, this._labelController));
22212
22905
 
22906
+ this._tooltipController = new TooltipController(this);
22907
+ this.addController(this._tooltipController);
22908
+ this._tooltipController.setPosition('top');
22909
+ this._tooltipController.setShouldShow((target) => !target.opened);
22910
+
22213
22911
  const toggleButton = this.shadowRoot.querySelector('[part="toggle-button"]');
22214
22912
  toggleButton.addEventListener('mousedown', (e) => e.preventDefault());
22215
22913
  }
@@ -22223,11 +22921,6 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
22223
22921
 
22224
22922
  /** @private */
22225
22923
  _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
22924
  if (e.detail.sourceEvent && e.detail.sourceEvent.composedPath().includes(this)) {
22232
22925
  e.preventDefault();
22233
22926
  }
@@ -22355,6 +23048,7 @@ const HelperFilters = class {
22355
23048
  this.modalCloseEvent();
22356
23049
  this.showClearButton = true;
22357
23050
  this.filterSelectionHandler(this.filterData);
23051
+ console.log(this.showClearButton);
22358
23052
  }
22359
23053
  resetSearch() {
22360
23054
  this.showClearButton = false;
@@ -22371,7 +23065,7 @@ const HelperFilters = class {
22371
23065
  this.filterData.filterToCalendar = new Date(event.target.value).toISOString();
22372
23066
  }
22373
23067
  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) ?
23068
+ 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
23069
  h$2("button", { class: "FilterClear", onClick: () => this.resetSearch() }, translate$1('filterClear', this.language))
22376
23070
  :
22377
23071
  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))))));