diff --git a/components/dist/chips.js b/components/dist/chips.js index 25fb587..90975d2 100644 --- a/components/dist/chips.js +++ b/components/dist/chips.js @@ -163,6 +163,9 @@ customElements.define('sm-chips', class extends HTMLElement { set value(val) { this.setSelectedOption(val); } + get isValid() { + return this._value !== undefined; + } scrollLeft() { this.chipsWrapper.scrollBy({ left: -this.scrollDistance, diff --git a/components/dist/chips.min.js b/components/dist/chips.min.js index 5ace050..9857712 100644 --- a/components/dist/chips.min.js +++ b/components/dist/chips.min.js @@ -1 +1 @@ -const smChips=document.createElement("template");smChips.innerHTML='\n\n
\n
\n \n
\n \n
\n \n
\n
\n\n',customElements.define("sm-chips",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smChips.content.cloneNode(!0)),this.chipsWrapper=this.shadowRoot.querySelector(".sm-chips"),this.coverLeft=this.shadowRoot.querySelector(".cover--left"),this.coverRight=this.shadowRoot.querySelector(".cover--right"),this.navButtonLeft=this.shadowRoot.querySelector(".nav-button--left"),this.navButtonRight=this.shadowRoot.querySelector(".nav-button--right"),this.slottedOptions=void 0,this._value=void 0,this.scrollDistance=0,this.assignedElements=[],this.scrollLeft=this.scrollLeft.bind(this),this.scrollRight=this.scrollRight.bind(this),this.fireEvent=this.fireEvent.bind(this),this.setSelectedOption=this.setSelectedOption.bind(this)}get value(){return this._value}set value(val){this.setSelectedOption(val)}scrollLeft(){this.chipsWrapper.scrollBy({left:-this.scrollDistance,behavior:"smooth"})}scrollRight(){this.chipsWrapper.scrollBy({left:this.scrollDistance,behavior:"smooth"})}setSelectedOption(value){this._value!==value&&(this._value=value,this.assignedElements.forEach((elem=>{elem.value==value?(elem.setAttribute("selected",""),elem.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center"})):elem.removeAttribute("selected")})))}fireEvent(){this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0,detail:{value:this._value}}))}connectedCallback(){this.setAttribute("role","listbox");const slot=this.shadowRoot.querySelector("slot");slot.addEventListener("slotchange",(e=>{firstOptionObserver.disconnect(),lastOptionObserver.disconnect(),this.observeSelf.disconnect(),clearTimeout(this.slotChangeTimeout),this.slotChangeTimeout=setTimeout((()=>{this.assignedElements=slot.assignedElements(),this.observeSelf.observe(this)}),0)}));new ResizeObserver((entries=>{entries.forEach((entry=>{if(entry.contentBoxSize){const contentBoxSize=Array.isArray(entry.contentBoxSize)?entry.contentBoxSize[0]:entry.contentBoxSize;this.scrollDistance=.6*contentBoxSize.inlineSize}else this.scrollDistance=.6*entry.contentRect.width}))})).observe(this),this.observeSelf=new IntersectionObserver(((entries,observer)=>{entries.forEach((entry=>{entry.isIntersecting&&!this.hasAttribute("multiline")&&this.assignedElements.length>0&&(firstOptionObserver.observe(this.assignedElements[0]),lastOptionObserver.observe(this.assignedElements[this.assignedElements.length-1]),observer.unobserve(this))}))}),{threshold:1}),this.chipsWrapper.addEventListener("option-clicked",(e=>{this._value!==e.detail.value&&(this.setSelectedOption(e.detail.value),this.fireEvent())}));const firstOptionObserver=new IntersectionObserver((entries=>{entries.forEach((entry=>{entry.isIntersecting?(this.navButtonLeft.classList.add("hide"),this.coverLeft.classList.add("hide")):(this.navButtonLeft.classList.remove("hide"),this.coverLeft.classList.remove("hide"))}))}),{threshold:1,root:this}),lastOptionObserver=new IntersectionObserver((entries=>{entries.forEach((entry=>{entry.isIntersecting?(this.navButtonRight.classList.add("hide"),this.coverRight.classList.add("hide")):(this.navButtonRight.classList.remove("hide"),this.coverRight.classList.remove("hide"))}))}),{threshold:1,root:this});this.navButtonLeft.addEventListener("click",this.scrollLeft),this.navButtonRight.addEventListener("click",this.scrollRight)}disconnectedCallback(){this.navButtonLeft.removeEventListener("click",this.scrollLeft),this.navButtonRight.removeEventListener("click",this.scrollRight)}});const smChip=document.createElement("template");smChip.innerHTML='\n\n\n \n\n',customElements.define("sm-chip",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smChip.content.cloneNode(!0)),this._value=this.getAttribute("value"),this.radioButton=this.shadowRoot.querySelector("input"),this.fireEvent=this.fireEvent.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this)}static get observedAttributes(){return["selected"]}get value(){return this._value}fireEvent(){this.dispatchEvent(new CustomEvent("option-clicked",{bubbles:!0,composed:!0,detail:{value:this._value}}))}handleKeyDown(e){"Enter"!==e.key&&"Space"!==e.key||this.fireEvent()}connectedCallback(){this.setAttribute("role","option"),this.setAttribute("tabindex","0"),this.addEventListener("click",this.fireEvent),this.addEventListener("keydown",this.handleKeyDown)}attributeChangedCallback(name,oldValue,newValue){"selected"===name?null!==newValue?(this.fireEvent(),this.setAttribute("aria-selected","true")):this.removeAttribute("aria-selected"):"value"===name&&(this._value=newValue)}disconnectedCallback(){this.removeEventListener("click",this.fireEvent),this.removeEventListener("keydown",this.handleKeyDown)}}); \ No newline at end of file +const smChips=document.createElement("template");smChips.innerHTML='\n\n
\n
\n \n
\n \n
\n \n
\n
\n\n',customElements.define("sm-chips",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smChips.content.cloneNode(!0)),this.chipsWrapper=this.shadowRoot.querySelector(".sm-chips"),this.coverLeft=this.shadowRoot.querySelector(".cover--left"),this.coverRight=this.shadowRoot.querySelector(".cover--right"),this.navButtonLeft=this.shadowRoot.querySelector(".nav-button--left"),this.navButtonRight=this.shadowRoot.querySelector(".nav-button--right"),this.slottedOptions=void 0,this._value=void 0,this.scrollDistance=0,this.assignedElements=[],this.scrollLeft=this.scrollLeft.bind(this),this.scrollRight=this.scrollRight.bind(this),this.fireEvent=this.fireEvent.bind(this),this.setSelectedOption=this.setSelectedOption.bind(this)}get value(){return this._value}set value(val){this.setSelectedOption(val)}get isValid(){return void 0!==this._value}scrollLeft(){this.chipsWrapper.scrollBy({left:-this.scrollDistance,behavior:"smooth"})}scrollRight(){this.chipsWrapper.scrollBy({left:this.scrollDistance,behavior:"smooth"})}setSelectedOption(value){this._value!==value&&(this._value=value,this.assignedElements.forEach((elem=>{elem.value==value?(elem.setAttribute("selected",""),elem.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center"})):elem.removeAttribute("selected")})))}fireEvent(){this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0,detail:{value:this._value}}))}connectedCallback(){this.setAttribute("role","listbox");const slot=this.shadowRoot.querySelector("slot");slot.addEventListener("slotchange",(e=>{firstOptionObserver.disconnect(),lastOptionObserver.disconnect(),this.observeSelf.disconnect(),clearTimeout(this.slotChangeTimeout),this.slotChangeTimeout=setTimeout((()=>{this.assignedElements=slot.assignedElements(),this.observeSelf.observe(this)}),0)}));new ResizeObserver((entries=>{entries.forEach((entry=>{if(entry.contentBoxSize){const contentBoxSize=Array.isArray(entry.contentBoxSize)?entry.contentBoxSize[0]:entry.contentBoxSize;this.scrollDistance=.6*contentBoxSize.inlineSize}else this.scrollDistance=.6*entry.contentRect.width}))})).observe(this),this.observeSelf=new IntersectionObserver(((entries,observer)=>{entries.forEach((entry=>{entry.isIntersecting&&!this.hasAttribute("multiline")&&this.assignedElements.length>0&&(firstOptionObserver.observe(this.assignedElements[0]),lastOptionObserver.observe(this.assignedElements[this.assignedElements.length-1]),observer.unobserve(this))}))}),{threshold:1}),this.chipsWrapper.addEventListener("option-clicked",(e=>{this._value!==e.detail.value&&(this.setSelectedOption(e.detail.value),this.fireEvent())}));const firstOptionObserver=new IntersectionObserver((entries=>{entries.forEach((entry=>{entry.isIntersecting?(this.navButtonLeft.classList.add("hide"),this.coverLeft.classList.add("hide")):(this.navButtonLeft.classList.remove("hide"),this.coverLeft.classList.remove("hide"))}))}),{threshold:1,root:this}),lastOptionObserver=new IntersectionObserver((entries=>{entries.forEach((entry=>{entry.isIntersecting?(this.navButtonRight.classList.add("hide"),this.coverRight.classList.add("hide")):(this.navButtonRight.classList.remove("hide"),this.coverRight.classList.remove("hide"))}))}),{threshold:1,root:this});this.navButtonLeft.addEventListener("click",this.scrollLeft),this.navButtonRight.addEventListener("click",this.scrollRight)}disconnectedCallback(){this.navButtonLeft.removeEventListener("click",this.scrollLeft),this.navButtonRight.removeEventListener("click",this.scrollRight)}});const smChip=document.createElement("template");smChip.innerHTML='\n\n\n \n\n',customElements.define("sm-chip",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smChip.content.cloneNode(!0)),this._value=this.getAttribute("value"),this.radioButton=this.shadowRoot.querySelector("input"),this.fireEvent=this.fireEvent.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this)}static get observedAttributes(){return["selected"]}get value(){return this._value}fireEvent(){this.dispatchEvent(new CustomEvent("option-clicked",{bubbles:!0,composed:!0,detail:{value:this._value}}))}handleKeyDown(e){"Enter"!==e.key&&"Space"!==e.key||this.fireEvent()}connectedCallback(){this.setAttribute("role","option"),this.setAttribute("tabindex","0"),this.addEventListener("click",this.fireEvent),this.addEventListener("keydown",this.handleKeyDown)}attributeChangedCallback(name,oldValue,newValue){"selected"===name?null!==newValue?(this.fireEvent(),this.setAttribute("aria-selected","true")):this.removeAttribute("aria-selected"):"value"===name&&(this._value=newValue)}disconnectedCallback(){this.removeEventListener("click",this.fireEvent),this.removeEventListener("keydown",this.handleKeyDown)}}); \ No newline at end of file diff --git a/components/dist/form.js b/components/dist/form.js index 50b90fb..2e64add 100644 --- a/components/dist/form.js +++ b/components/dist/form.js @@ -52,7 +52,11 @@ customElements.define('sm-form', class extends HTMLElement { }; } _checkValidity = () => { - if (!this.submitButton || this._requiredElements.length === 0) return; + if (!this.submitButton || this._requiredElements.length === 0) { + if (this.submitButton) + this.submitButton.disabled = false; + return; + } this.invalidFieldsCount = 0 this._requiredElements.forEach(([elem, isWC]) => { if (!elem.disabled && isWC && !elem.isValid || !isWC && !elem.checkValidity()) @@ -121,7 +125,8 @@ customElements.define('sm-form', class extends HTMLElement { this._checkValidity(); } elementsChanged = () => { - this.formElements = [...this.querySelectorAll(this.supportedElements)].map(elem => { + this.isFormValid = undefined; + this.formElements = [...this.querySelectorAll(this.supportedElements)].map((elem) => { return [elem, elem.tagName.includes('-')]; }); this._requiredElements = this.formElements.filter(([elem]) => elem.hasAttribute('required')); diff --git a/components/dist/form.min.js b/components/dist/form.min.js index 90fa698..9fde5b7 100644 --- a/components/dist/form.min.js +++ b/components/dist/form.min.js @@ -1 +1 @@ -const smForm=document.createElement("template");smForm.innerHTML='\n \n
\n \n
\n ',customElements.define("sm-form",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smForm.content.cloneNode(!0)),this.form=this.shadowRoot.querySelector("form"),this.invalidFieldsCount,this.skipSubmit=!1,this.isFormValid=void 0,this.supportedElements="input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio",this.formElements=[],this._requiredElements=[]}static get observedAttributes(){return["skip-submit"]}get validity(){return this.isFormValid}debounce=(callback,wait)=>{let timeoutId=null;return(...args)=>{window.clearTimeout(timeoutId),timeoutId=window.setTimeout((()=>{callback.apply(null,args)}),wait)}};_checkValidity=()=>{this.submitButton&&0!==this._requiredElements.length&&(this.invalidFieldsCount=0,this._requiredElements.forEach((([elem,isWC])=>{(!elem.disabled&&isWC&&!elem.isValid||!isWC&&!elem.checkValidity())&&this.invalidFieldsCount++})),this.isFormValid!==(0===this.invalidFieldsCount)&&(this.isFormValid=0===this.invalidFieldsCount,this.dispatchEvent(new CustomEvent(this.isFormValid?"valid":"invalid",{bubbles:!0,composed:!0})),this.skipSubmit||(this.submitButton.disabled=!this.isFormValid)))};handleKeydown=e=>{if("Enter"===e.key&&e.target.tagName.includes("INPUT"))if(0===this.invalidFieldsCount)this.submitButton&&this.submitButton.click(),this.dispatchEvent(new CustomEvent("submit",{bubbles:!0,composed:!0}));else for(const[elem,isWC]of this._requiredElements){if(isWC?!elem.isValid:!elem.checkValidity()){(elem?.shadowRoot?.lastElementChild||elem).animate([{transform:"translateX(-1rem)"},{transform:"translateX(1rem)"},{transform:"translateX(-0.5rem)"},{transform:"translateX(0.5rem)"},{transform:"translateX(0)"}],{duration:300,easing:"ease"}),isWC?(elem.focusIn(),"SM-INPUT"===elem.tagName&&""===elem.value.trim()&&elem.showError()):elem.focus();break}}};reset=()=>{this.formElements.forEach((([elem,isWC])=>{if(isWC)elem.reset();else switch(elem.type){case"checkbox":case"radio":elem.checked=!1;break;default:elem.value=""}})),this._checkValidity()};elementsChanged=()=>{this.formElements=[...this.querySelectorAll(this.supportedElements)].map((elem=>[elem,elem.tagName.includes("-")])),this._requiredElements=this.formElements.filter((([elem])=>elem.hasAttribute("required"))),this.submitButton=this.querySelector('[variant="primary"], [type="submit"]'),this.resetButton=this.querySelector('[type="reset"]'),this.resetButton&&this.resetButton.addEventListener("click",this.reset),this._checkValidity()};checkIfSupported=elem=>1===elem.nodeType&&(elem.tagName.includes("-")||"input"===elem.tagName||elem.querySelector(this.supportedElements));connectedCallback(){const updateFormDecedents=this.debounce(this.elementsChanged,100);this.addEventListener("input",this.debounce(this._checkValidity,100)),this.addEventListener("keydown",this.debounce(this.handleKeydown,100)),this.shadowRoot.querySelector("slot").addEventListener("slotchange",updateFormDecedents),this.mutationObserver=new MutationObserver((mutations=>{mutations.forEach((mutation=>{("childList"===mutation.type&&[...mutation.addedNodes].some((node=>this.checkIfSupported(node)))||[...mutation.removedNodes].some((node=>this.checkIfSupported(node))))&&updateFormDecedents()}))})),this.mutationObserver.observe(this,{childList:!0,subtree:!0})}attributeChangedCallback(name,oldValue,newValue){"skip-submit"===name&&(this.skipSubmit=this.hasAttribute("skip-submit"))}disconnectedCallback(){this.removeEventListener("input",this.debounce(this._checkValidity,100)),this.removeEventListener("keydown",this.debounce(this.handleKeydown,100)),this.mutationObserver.disconnect()}}); \ No newline at end of file +const smForm=document.createElement("template");smForm.innerHTML='\n \n
\n \n
\n ',customElements.define("sm-form",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smForm.content.cloneNode(!0)),this.form=this.shadowRoot.querySelector("form"),this.invalidFieldsCount,this.skipSubmit=!1,this.isFormValid=void 0,this.supportedElements="input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio",this.formElements=[],this._requiredElements=[]}static get observedAttributes(){return["skip-submit"]}get validity(){return this.isFormValid}debounce=(callback,wait)=>{let timeoutId=null;return(...args)=>{window.clearTimeout(timeoutId),timeoutId=window.setTimeout((()=>{callback.apply(null,args)}),wait)}};_checkValidity=()=>{this.submitButton&&0!==this._requiredElements.length?(this.invalidFieldsCount=0,this._requiredElements.forEach((([elem,isWC])=>{(!elem.disabled&&isWC&&!elem.isValid||!isWC&&!elem.checkValidity())&&this.invalidFieldsCount++})),this.isFormValid!==(0===this.invalidFieldsCount)&&(this.isFormValid=0===this.invalidFieldsCount,this.dispatchEvent(new CustomEvent(this.isFormValid?"valid":"invalid",{bubbles:!0,composed:!0})),this.skipSubmit||(this.submitButton.disabled=!this.isFormValid))):this.submitButton&&(this.submitButton.disabled=!1)};handleKeydown=e=>{if("Enter"===e.key&&e.target.tagName.includes("INPUT"))if(0===this.invalidFieldsCount)this.submitButton&&this.submitButton.click(),this.dispatchEvent(new CustomEvent("submit",{bubbles:!0,composed:!0}));else for(const[elem,isWC]of this._requiredElements){if(isWC?!elem.isValid:!elem.checkValidity()){(elem?.shadowRoot?.lastElementChild||elem).animate([{transform:"translateX(-1rem)"},{transform:"translateX(1rem)"},{transform:"translateX(-0.5rem)"},{transform:"translateX(0.5rem)"},{transform:"translateX(0)"}],{duration:300,easing:"ease"}),isWC?(elem.focusIn(),"SM-INPUT"===elem.tagName&&""===elem.value.trim()&&elem.showError()):elem.focus();break}}};reset=()=>{this.formElements.forEach((([elem,isWC])=>{if(isWC)elem.reset();else switch(elem.type){case"checkbox":case"radio":elem.checked=!1;break;default:elem.value=""}})),this._checkValidity()};elementsChanged=()=>{this.isFormValid=void 0,this.formElements=[...this.querySelectorAll(this.supportedElements)].map((elem=>[elem,elem.tagName.includes("-")])),this._requiredElements=this.formElements.filter((([elem])=>elem.hasAttribute("required"))),this.submitButton=this.querySelector('[variant="primary"], [type="submit"]'),this.resetButton=this.querySelector('[type="reset"]'),this.resetButton&&this.resetButton.addEventListener("click",this.reset),this._checkValidity()};checkIfSupported=elem=>1===elem.nodeType&&(elem.tagName.includes("-")||"input"===elem.tagName||elem.querySelector(this.supportedElements));connectedCallback(){const updateFormDecedents=this.debounce(this.elementsChanged,100);this.addEventListener("input",this.debounce(this._checkValidity,100)),this.addEventListener("keydown",this.debounce(this.handleKeydown,100)),this.shadowRoot.querySelector("slot").addEventListener("slotchange",updateFormDecedents),this.mutationObserver=new MutationObserver((mutations=>{mutations.forEach((mutation=>{("childList"===mutation.type&&[...mutation.addedNodes].some((node=>this.checkIfSupported(node)))||[...mutation.removedNodes].some((node=>this.checkIfSupported(node))))&&updateFormDecedents()}))})),this.mutationObserver.observe(this,{childList:!0,subtree:!0})}attributeChangedCallback(name,oldValue,newValue){"skip-submit"===name&&(this.skipSubmit=this.hasAttribute("skip-submit"))}disconnectedCallback(){this.removeEventListener("input",this.debounce(this._checkValidity,100)),this.removeEventListener("keydown",this.debounce(this.handleKeydown,100)),this.mutationObserver.disconnect()}}); \ No newline at end of file diff --git a/components/dist/notifications.js b/components/dist/notifications.js index dbec264..0e1897c 100644 --- a/components/dist/notifications.js +++ b/components/dist/notifications.js @@ -213,7 +213,7 @@ customElements.define('sm-notifications', class extends HTMLElement { notification.id = this.randString(8) notification.className = `notification ${pinned ? 'pinned' : ''}` notification.style.setProperty('--timeout', `${timeout}ms`); - notification.innerHTML = ` + notification.innerHTML = /*html*/` ${icon ? `
${icon}
` : ''} ${message} ${action ? `` : ''}