diff --git a/components/dist/select.js b/components/dist/select.js index 749a40f..2c6f450 100644 --- a/components/dist/select.js +++ b/components/dist/select.js @@ -206,7 +206,7 @@ customElements.define('sm-select', class extends HTMLElement { reset(fire = true) { if (this.availableOptions[0] && this.previousOption !== this.availableOptions[0]) { - const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0]; + const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0]; this.value = selectedOption.getAttribute('value') if (fire) { this.fireEvent() @@ -215,9 +215,9 @@ customElements.define('sm-select', class extends HTMLElement { } selectOption(selectedOption) { if (this.previousOption !== selectedOption) { - this.querySelectorAll('[selected]').forEach(option => option.removeAttribute('selected')) + this.querySelectorAll('[selected="true"]').forEach(option => option.removeAttribute('selected')) this.selectedOptionText.textContent = `${this.label}${selectedOption.textContent}`; - selectedOption.setAttribute('selected', '') + selectedOption.setAttribute('selected', 'true') this.previousOption = selectedOption } } @@ -247,7 +247,7 @@ customElements.define('sm-select', class extends HTMLElement { ], this.animationOptions) this.setAttribute('open', ''); this.style.zIndex = 1000; - (this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0]).focus() + (this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0]).focus() document.addEventListener('mousedown', this.handleClickOutside) this.isOpen = true } @@ -325,7 +325,7 @@ customElements.define('sm-select', class extends HTMLElement { if (e.target === this) { if (this.isOpen && e.key === 'ArrowDown') { e.preventDefault(); - (this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0]).focus() + (this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0]).focus() this.handleOptionSelection(e) } else if (e.key === ' ') { e.preventDefault() @@ -373,7 +373,7 @@ customElements.define('sm-select', class extends HTMLElement { } }); if (attributesChanged) { - const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0]; + const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0]; this.selectedOptionText.textContent = `${this.label}${selectedOption.textContent}`; this.setAttribute('value', selectedOption.getAttribute('value')); } @@ -462,7 +462,7 @@ smOption.innerHTML = ` :host(:focus) .option::before{ opacity: 1 } -:host([selected]) .option::before{ +:host([selected="true"]) .option::before{ opacity: 1; background: var(--accent-color, teal); } @@ -470,7 +470,7 @@ smOption.innerHTML = ` .option:hover{ background: rgba(var(--text-color,(17,17,17)), 0.1); } - :host(:not([selected]):hover) .option::before{ + :host(:not([selected="true"]):hover) .option::before{ opacity: 1 } } diff --git a/components/dist/select.min.js b/components/dist/select.min.js index 471cb32..ce59843 100644 --- a/components/dist/select.min.js +++ b/components/dist/select.min.js @@ -1 +1 @@ -const smSelect=document.createElement("template");smSelect.innerHTML='\n\n
\n
\n
\n \n \n
\n \n
',customElements.define("sm-select",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smSelect.content.cloneNode(!0)),this.focusIn=this.focusIn.bind(this),this.reset=this.reset.bind(this),this.open=this.open.bind(this),this.collapse=this.collapse.bind(this),this.toggle=this.toggle.bind(this),this.handleOptionsNavigation=this.handleOptionsNavigation.bind(this),this.handleOptionSelection=this.handleOptionSelection.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.handleClickOutside=this.handleClickOutside.bind(this),this.selectOption=this.selectOption.bind(this),this.debounce=this.debounce.bind(this),this.elementsChanged=this.elementsChanged.bind(this),this.availableOptions=[],this.previousOption,this.isOpen=!1,this.label="",this.defaultSelected="",this.isUnderViewport=!1,this.animationOptions={duration:300,fill:"forwards",easing:"ease"},this.optionList=this.shadowRoot.querySelector(".options"),this.selection=this.shadowRoot.querySelector(".selection"),this.selectedOptionText=this.shadowRoot.querySelector(".selected-option-text")}static get observedAttributes(){return["disabled","label","readonly"]}get value(){return this.getAttribute("value")}set value(t){const e=this.availableOptions.find(e=>e.getAttribute("value")===t);e?(this.setAttribute("value",t),this.selectOption(e)):console.warn(`There is no option with ${t} as value`)}debounce(t,e){let n=null;return(...i)=>{window.clearTimeout(n),n=window.setTimeout(()=>{t.apply(null,i)},e)}}reset(t=!0){if(this.availableOptions[0]&&this.previousOption!==this.availableOptions[0]){const e=this.availableOptions.find(t=>t.hasAttribute("selected"))||this.availableOptions[0];this.value=e.getAttribute("value"),t&&this.fireEvent()}}selectOption(t){this.previousOption!==t&&(this.querySelectorAll("[selected]").forEach(t=>t.removeAttribute("selected")),this.selectedOptionText.textContent=`${this.label}${t.textContent}`,t.setAttribute("selected",""),this.previousOption=t)}focusIn(){this.selection.focus()}open(){this.availableOptions.forEach(t=>t.setAttribute("tabindex",0)),this.optionList.classList.remove("hidden"),this.isUnderViewport=this.getBoundingClientRect().bottom+this.optionList.getBoundingClientRect().height>window.innerHeight,this.isUnderViewport?this.setAttribute("isUnder",""):this.removeAttribute("isUnder"),this.optionList.animate([{transform:`translateY(${this.isUnderViewport?"":"-"}0.5rem)`,opacity:0},{transform:"translateY(0)",opacity:1}],this.animationOptions),this.setAttribute("open",""),this.style.zIndex=1e3,(this.availableOptions.find(t=>t.hasAttribute("selected"))||this.availableOptions[0]).focus(),document.addEventListener("mousedown",this.handleClickOutside),this.isOpen=!0}collapse(){this.removeAttribute("open"),this.optionList.animate([{transform:"translateY(0)",opacity:1},{transform:`translateY(${this.isUnderViewport?"":"-"}0.5rem)`,opacity:0}],this.animationOptions).onfinish=(()=>{this.availableOptions.forEach(t=>t.removeAttribute("tabindex")),document.removeEventListener("mousedown",this.handleClickOutside),this.optionList.classList.add("hidden"),this.isOpen=!1,this.style.zIndex="auto"})}toggle(){this.isOpen||this.hasAttribute("disabled")?this.collapse():this.open()}fireEvent(){this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0,detail:{value:this.value}}))}handleOptionsNavigation(t){"ArrowUp"===t.key?(t.preventDefault(),document.activeElement.previousElementSibling?document.activeElement.previousElementSibling.focus():this.availableOptions[this.availableOptions.length-1].focus()):"ArrowDown"===t.key&&(t.preventDefault(),document.activeElement.nextElementSibling?document.activeElement.nextElementSibling.focus():this.availableOptions[0].focus())}handleOptionSelection(t){this.previousOption!==document.activeElement&&(this.value=document.activeElement.getAttribute("value"),this.fireEvent())}handleClick(t){t.target===this?this.toggle():(this.handleOptionSelection(),this.collapse())}handleKeydown(t){t.target===this?this.isOpen&&"ArrowDown"===t.key?(t.preventDefault(),(this.availableOptions.find(t=>t.hasAttribute("selected"))||this.availableOptions[0]).focus(),this.handleOptionSelection(t)):" "===t.key&&(t.preventDefault(),this.toggle()):(this.handleOptionsNavigation(t),this.handleOptionSelection(t),["Enter"," ","Escape","Tab"].includes(t.key)&&(t.preventDefault(),this.collapse(),this.focusIn()))}handleClickOutside(t){this.isOpen&&!this.contains(t.target)&&this.collapse()}elementsChanged(){this.availableOptions=[...this.querySelectorAll("sm-option")],this.reset(!1),this.defaultSelected=this.value}connectedCallback(){this.setAttribute("role","listbox"),this.hasAttribute("disabled")||this.hasAttribute("readonly")||(this.selection.setAttribute("tabindex","0"),this.addEventListener("click",this.handleClick),this.addEventListener("keydown",this.handleKeydown));const t=this.debounce(this.elementsChanged,100);this.shadowRoot.querySelector("slot").addEventListener("slotchange",t),this.mutationObserver=new MutationObserver(e=>{let n=!1;if(e.forEach(e=>{switch(e.type){case"childList":t();break;case"attributes":n=!0}}),n){const t=this.availableOptions.find(t=>t.hasAttribute("selected"))||this.availableOptions[0];this.selectedOptionText.textContent=`${this.label}${t.textContent}`,this.setAttribute("value",t.getAttribute("value"))}}),this.mutationObserver.observe(this,{subtree:!0,childList:!0,attributeFilter:["selected"]}),new IntersectionObserver((t,e)=>{t.forEach(t=>{if(t.isIntersecting){const t=this.selection.getBoundingClientRect().left;t \n*{\n padding: 0;\n margin: 0;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n} \n:host{\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n overflow: hidden;\n border-radius: var(--border-radius, 0.3rem);\n}\n.option{\n position: relative;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n width: 100%;\n gap: 0.5rem;\n padding: var(--padding, 0.6rem 1rem);\n cursor: pointer;\n outline: none;\n user-select: none;\n}\n.option::before{\n position: absolute;\n content: \'\';\n display: block;\n width: 0.2rem;\n height: 1em;\n left: 0;\n border-radius: 0 1em 1em 0;\n background: rgba(var(--text-color,(17,17,17)), 0.5);\n transition: all 0.2s ease-in-out;\n opacity: 0;\n}\n:host(:focus){\n outline: none;\n background: rgba(var(--text-color,(17,17,17)), 0.1);\n}\n:host(:focus) .option::before{\n opacity: 1\n}\n:host([selected]) .option::before{\n opacity: 1;\n background: var(--accent-color, teal);\n}\n@media (hover: hover){\n .option:hover{\n background: rgba(var(--text-color,(17,17,17)), 0.1);\n }\n :host(:not([selected]):hover) .option::before{\n opacity: 1\n }\n}\n\n
\n \n
',customElements.define("sm-option",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smOption.content.cloneNode(!0))}connectedCallback(){this.setAttribute("role","option")}}); \ No newline at end of file +const smSelect=document.createElement("template");smSelect.innerHTML='\n\n
\n
\n
\n \n \n
\n \n
',customElements.define("sm-select",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smSelect.content.cloneNode(!0)),this.focusIn=this.focusIn.bind(this),this.reset=this.reset.bind(this),this.open=this.open.bind(this),this.collapse=this.collapse.bind(this),this.toggle=this.toggle.bind(this),this.handleOptionsNavigation=this.handleOptionsNavigation.bind(this),this.handleOptionSelection=this.handleOptionSelection.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.handleClickOutside=this.handleClickOutside.bind(this),this.selectOption=this.selectOption.bind(this),this.debounce=this.debounce.bind(this),this.elementsChanged=this.elementsChanged.bind(this),this.availableOptions=[],this.previousOption,this.isOpen=!1,this.label="",this.defaultSelected="",this.isUnderViewport=!1,this.animationOptions={duration:300,fill:"forwards",easing:"ease"},this.optionList=this.shadowRoot.querySelector(".options"),this.selection=this.shadowRoot.querySelector(".selection"),this.selectedOptionText=this.shadowRoot.querySelector(".selected-option-text")}static get observedAttributes(){return["disabled","label","readonly"]}get value(){return this.getAttribute("value")}set value(t){const e=this.availableOptions.find(e=>e.getAttribute("value")===t);e?(this.setAttribute("value",t),this.selectOption(e)):console.warn(`There is no option with ${t} as value`)}debounce(t,e){let n=null;return(...i)=>{window.clearTimeout(n),n=window.setTimeout(()=>{t.apply(null,i)},e)}}reset(t=!0){if(this.availableOptions[0]&&this.previousOption!==this.availableOptions[0]){const e=this.availableOptions.find(t=>t.hasAttribute("selected")&&"true"===t.getAttribute("selected"))||this.availableOptions[0];this.value=e.getAttribute("value"),t&&this.fireEvent()}}selectOption(t){this.previousOption!==t&&(this.querySelectorAll('[selected="true"]').forEach(t=>t.removeAttribute("selected")),this.selectedOptionText.textContent=`${this.label}${t.textContent}`,t.setAttribute("selected","true"),this.previousOption=t)}focusIn(){this.selection.focus()}open(){this.availableOptions.forEach(t=>t.setAttribute("tabindex",0)),this.optionList.classList.remove("hidden"),this.isUnderViewport=this.getBoundingClientRect().bottom+this.optionList.getBoundingClientRect().height>window.innerHeight,this.isUnderViewport?this.setAttribute("isUnder",""):this.removeAttribute("isUnder"),this.optionList.animate([{transform:`translateY(${this.isUnderViewport?"":"-"}0.5rem)`,opacity:0},{transform:"translateY(0)",opacity:1}],this.animationOptions),this.setAttribute("open",""),this.style.zIndex=1e3,(this.availableOptions.find(t=>t.hasAttribute("selected")&&"true"===t.getAttribute("selected"))||this.availableOptions[0]).focus(),document.addEventListener("mousedown",this.handleClickOutside),this.isOpen=!0}collapse(){this.removeAttribute("open"),this.optionList.animate([{transform:"translateY(0)",opacity:1},{transform:`translateY(${this.isUnderViewport?"":"-"}0.5rem)`,opacity:0}],this.animationOptions).onfinish=(()=>{this.availableOptions.forEach(t=>t.removeAttribute("tabindex")),document.removeEventListener("mousedown",this.handleClickOutside),this.optionList.classList.add("hidden"),this.isOpen=!1,this.style.zIndex="auto"})}toggle(){this.isOpen||this.hasAttribute("disabled")?this.collapse():this.open()}fireEvent(){this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0,detail:{value:this.value}}))}handleOptionsNavigation(t){"ArrowUp"===t.key?(t.preventDefault(),document.activeElement.previousElementSibling?document.activeElement.previousElementSibling.focus():this.availableOptions[this.availableOptions.length-1].focus()):"ArrowDown"===t.key&&(t.preventDefault(),document.activeElement.nextElementSibling?document.activeElement.nextElementSibling.focus():this.availableOptions[0].focus())}handleOptionSelection(t){this.previousOption!==document.activeElement&&(this.value=document.activeElement.getAttribute("value"),this.fireEvent())}handleClick(t){t.target===this?this.toggle():(this.handleOptionSelection(),this.collapse())}handleKeydown(t){t.target===this?this.isOpen&&"ArrowDown"===t.key?(t.preventDefault(),(this.availableOptions.find(t=>t.hasAttribute("selected")&&"true"===t.getAttribute("selected"))||this.availableOptions[0]).focus(),this.handleOptionSelection(t)):" "===t.key&&(t.preventDefault(),this.toggle()):(this.handleOptionsNavigation(t),this.handleOptionSelection(t),["Enter"," ","Escape","Tab"].includes(t.key)&&(t.preventDefault(),this.collapse(),this.focusIn()))}handleClickOutside(t){this.isOpen&&!this.contains(t.target)&&this.collapse()}elementsChanged(){this.availableOptions=[...this.querySelectorAll("sm-option")],this.reset(!1),this.defaultSelected=this.value}connectedCallback(){this.setAttribute("role","listbox"),this.hasAttribute("disabled")||this.hasAttribute("readonly")||(this.selection.setAttribute("tabindex","0"),this.addEventListener("click",this.handleClick),this.addEventListener("keydown",this.handleKeydown));const t=this.debounce(this.elementsChanged,100);this.shadowRoot.querySelector("slot").addEventListener("slotchange",t),this.mutationObserver=new MutationObserver(e=>{let n=!1;if(e.forEach(e=>{switch(e.type){case"childList":t();break;case"attributes":n=!0}}),n){const t=this.availableOptions.find(t=>t.hasAttribute("selected")&&"true"===t.getAttribute("selected"))||this.availableOptions[0];this.selectedOptionText.textContent=`${this.label}${t.textContent}`,this.setAttribute("value",t.getAttribute("value"))}}),this.mutationObserver.observe(this,{subtree:!0,childList:!0,attributeFilter:["selected"]}),new IntersectionObserver((t,e)=>{t.forEach(t=>{if(t.isIntersecting){const t=this.selection.getBoundingClientRect().left;t \n*{\n padding: 0;\n margin: 0;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n} \n:host{\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n overflow: hidden;\n border-radius: var(--border-radius, 0.3rem);\n}\n.option{\n position: relative;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n width: 100%;\n gap: 0.5rem;\n padding: var(--padding, 0.6rem 1rem);\n cursor: pointer;\n outline: none;\n user-select: none;\n}\n.option::before{\n position: absolute;\n content: \'\';\n display: block;\n width: 0.2rem;\n height: 1em;\n left: 0;\n border-radius: 0 1em 1em 0;\n background: rgba(var(--text-color,(17,17,17)), 0.5);\n transition: all 0.2s ease-in-out;\n opacity: 0;\n}\n:host(:focus){\n outline: none;\n background: rgba(var(--text-color,(17,17,17)), 0.1);\n}\n:host(:focus) .option::before{\n opacity: 1\n}\n:host([selected="true"]) .option::before{\n opacity: 1;\n background: var(--accent-color, teal);\n}\n@media (hover: hover){\n .option:hover{\n background: rgba(var(--text-color,(17,17,17)), 0.1);\n }\n :host(:not([selected="true"]):hover) .option::before{\n opacity: 1\n }\n}\n\n
\n \n
',customElements.define("sm-option",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smOption.content.cloneNode(!0))}connectedCallback(){this.setAttribute("role","option")}}); \ No newline at end of file