1 line
9.7 KiB
JavaScript
1 line
9.7 KiB
JavaScript
const smChips=document.createElement("template");smChips.innerHTML='\n<style>\n *{\n padding: 0;\n margin: 0;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n } \n :host{\n padding: 1rem 0;\n max-width: 100%;\n }\n .hide{\n opacity: 0;\n pointer-events: none;\n }\n input[type="radio"]{\n display: none;\n }\n .scrolling-container{\n position: relative;\n display: grid;\n grid-template-columns: min-content minmax(0,1fr) min-content;\n grid-template-rows: 1fr;\n }\n .sm-chips{\n display: flex;\n position: relative;\n grid-area: 1/1/2/-1;\n gap: var(--gap, 0.5rem);\n overflow: auto hidden;\n }\n :host([multiline]) .sm-chips{\n flex-wrap: wrap;\n }\n :host(:not([multiline])) .sm-chips{\n max-width: 100%; \n align-items: center;\n }\n .nav-button{\n display: flex;\n z-index: 2;\n border: none;\n padding: 0.3rem;\n cursor: pointer;\n align-items: center;\n background: rgba(var(--background-color,(255,255,255)), 1);\n grid-row: 1;\n transition: opacity 0.2s;\n }\n .nav-button--left{\n grid-column: 1;\n justify-self: start;\n }\n .nav-button--right{\n grid-column: 3;\n justify-self: end;\n }\n .cover{\n position: absolute;\n z-index: 1;\n width: 5rem;\n height: 100%;\n pointer-events: none;\n grid-row: 1;\n transition: opacity 0.2s;\n }\n .cover--left{\n grid-column: 1;\n }\n .cover--right{\n grid-column: 3;\n }\n .nav-button--right::before{\n background-color: red;\n }\n .icon{\n height: 1.2rem;\n width: 1.2rem;\n fill: rgba(var(--text-color,(17,17,17)), .8);\n }\n @media (hover: none){\n ::-webkit-scrollbar {\n height: 0;\n }\n .nav-button{\n display: none;\n }\n .sm-chips{\n overflow: auto hidden;\n }\n .cover{\n width: 2rem;\n }\n .cover--left{\n background: linear-gradient(90deg, rgba(var(--background-color,(255,255,255)), 1), transparent);\n }\n .cover--right{\n right: 0;\n background: linear-gradient(90deg, transparent, rgba(var(--background-color,(255,255,255)), 1));\n }\n }\n @media (hover: hover){\n ::-webkit-scrollbar-track {\n background-color: transparent !important;\n }\n ::-webkit-scrollbar {\n height: 0;\n background-color: transparent;\n }\n .sm-chips{\n overflow: hidden;\n }\n .cover--left{\n background: linear-gradient(90deg, rgba(var(--background-color,(255,255,255)), 1) 60%, transparent);\n }\n .cover--right{\n right: 0;\n background: linear-gradient(90deg, transparent 0%, rgba(var(--background-color,(255,255,255)), 1) 40%);\n }\n }\n</style>\n<section class="scrolling-container">\n <div class="cover cover--left hide"></div>\n <button class="nav-button nav-button--left hide">\n <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10.828 12l4.95 4.95-1.414 1.414L8 12l6.364-6.364 1.414 1.414z"/></svg>\n </button>\n <section class="sm-chips" part="chips-wrapper">\n <slot></slot>\n </section>\n <button class="nav-button nav-button--right hide">\n <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M13.172 12l-4.95-4.95 1.414-1.414L16 12l-6.364 6.364-1.414-1.414z"/></svg>\n </button>\n <div class="cover cover--right hide"></div>\n</section>\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=>{e.stopPropagation(),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<style>\n *{\n padding: 0;\n margin: 0;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n } \n :host(:focus-within){\n outline: none;\n }\n :host(:focus-within) .sm-chip{\n box-shadow: 0 0 0 0.1rem var(--accent-color,teal) inset;\n }\n :host(:hover:not([selected])) .sm-chip{\n background-color: rgba(var(--text-color,(17,17,17)), 0.06);\n }\n .sm-chip{\n display: flex;\n flex-shrink: 0;\n cursor: pointer;\n white-space: nowrap;\n padding: var(--padding, 0.5rem 0.8rem);\n transition: background 0.3s;\n border-radius: var(--border-radius, 0.5rem);\n -webkit-tap-highlight-color: transparent;\n background: var(--background,inherit);\n }\n :host([selected]) .sm-chip{\n background-color: var(--accent-color, teal);\n color: rgba(var(--background-color, (255,255,255)), 1);\n }\n</style>\n<span class="sm-chip" part="chip">\n <slot></slot>\n</span>\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.hasAttribute("value")||console.error("sm-chip must have a value attribute"),this.hasAttribute("selected")&&this.fireEvent(),this.addEventListener("click",this.fireEvent),this.addEventListener("keydown",this.handleKeyDown)}attributeChangedCallback(name,oldValue,newValue){"selected"===name?this.hasAttribute("selected")?(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)}}); |