UNPKG

@skem9/dselect

Version:

Dropdown select box for bootstrap 5 with search, multiple, tag and image support

34 lines (33 loc) 8.97 kB
function dselectUpdate(e,t,n){const c=e.dataset.dselectValue,s=e.closest(`.${t}`).previousElementSibling,d=s.nextElementSibling.getElementsByClassName(n)[0],l=s.nextElementSibling.querySelector("input");s&&s.dispatchEvent(new CustomEvent("update",{detail:{button:e,classElement:t,classToggler:n}})),s.multiple?Array.from(s.options).filter(h=>h.value===c)[0].selected=!0:s.value=c,s.multiple&&d.click(),s.dispatchEvent(new Event("change")),d.focus(),l&&(l.value="")}function dselectRemoveTag(e,t,n){const c=e.parentNode.dataset.dselectValue,s=e.closest(`.${t}`).previousElementSibling,d=s.nextElementSibling.getElementsByClassName(n)[0],l=s.nextElementSibling.querySelector("input");s&&s.dispatchEvent(new CustomEvent("removeTag",{detail:{button:e,classElement:t,classToggler:n}})),Array.from(s.options).filter(h=>h.value===c)[0].selected=!1,s.dispatchEvent(new Event("change")),d.click(),l&&(l.value="")}function dselectSearch(e,t,n,c,s,d){const l=t.value.toLowerCase().trim(),h=t.nextElementSibling,C=Array.from(h.querySelectorAll(".dropdown-header")),$=Array.from(h.querySelectorAll(".dropdown-item")),E=h.nextElementSibling,p=t.closest(`.${n}`).previousElementSibling;if(p&&p.dispatchEvent(new CustomEvent("search",{detail:{event:e,input:t,classElement:n,classToggler:c,creatable:s,localization:d}})),e.key==="ArrowDown"||e.key==="ArrowUp"){e.preventDefault(),dselectNavigate(e.key,h,n);return}if(e.key==="Enter"){e.preventDefault();const o=h.querySelector(".dropdown-item.dselect-highlighted");if(o&&!o.classList.contains("d-none")){dselectUpdate(o,n,c);return}else if(s&&l!==""&&$.filter(g=>!g.classList.contains("d-none")).length===0){const g=p.nextElementSibling.querySelector(`.${c}`),u=Array.from(p.options).find(A=>A.value===t.value.trim());u?u.selected=!0:p.insertAdjacentHTML("afterbegin",`<option value="${t.value.trim()}" selected>${t.value.trim()}</option>`),p.dispatchEvent(new Event("change")),t.value="";const m=new Event("keyup");m.key="",t.dispatchEvent(m),g.click(),g.focus();return}return}C.forEach(o=>o.classList.add("d-none")),$.forEach(o=>o.classList.remove("dselect-highlighted")),$.forEach(o=>{const g=o.textContent.toLowerCase(),u=l===""||g.includes(l);if(o.classList.toggle("d-none",!u),u){let m=o.previousElementSibling;for(;m;){if(m.classList.contains("dropdown-header")){m.classList.remove("d-none");break}m=m.previousElementSibling}}}),C.forEach(o=>{const g=o.textContent.toLowerCase();if(l!==""&&g.includes(l)){o.classList.remove("d-none");let u=o.nextElementSibling;for(;u&&!u.classList.contains("dropdown-header");)u.classList.contains("dropdown-item")&&u.classList.remove("d-none"),u=u.nextElementSibling}});const S=$.filter(o=>!o.classList.contains("d-none")&&!o.hasAttribute("hidden"));S.length===0&&l!==""?(E.classList.remove("d-none"),h.classList.add("d-none"),s&&(E.innerHTML=d.replace("[searched-term]",`<strong>${t.value}</strong>`))):(E.classList.add("d-none"),h.classList.remove("d-none"),S.length>0&&l!==""&&S[0].classList.add("dselect-highlighted"))}function dselectNavigate(e,t,n){const c=Array.from(t.querySelectorAll(".dropdown-item")).filter(l=>!l.classList.contains("d-none")&&!l.hasAttribute("hidden"));if(c.length===0)return;const s=t.querySelector(".dropdown-item.dselect-highlighted");let d=0;if(s){const l=c.indexOf(s);s.classList.remove("dselect-highlighted"),e==="ArrowDown"?d=l<c.length-1?l+1:0:e==="ArrowUp"&&(d=l>0?l-1:c.length-1)}else d=e==="ArrowUp"?c.length-1:0;c[d].classList.add("dselect-highlighted"),c[d].scrollIntoView({block:"nearest",behavior:"smooth"})}function dselectClear(e,t){const n=e.closest(`.${t}`).previousElementSibling;n&&n.dispatchEvent(new CustomEvent("clear",{detail:{event:e,input:t}})),Array.from(n.options).forEach(c=>c.selected=!1),n.dispatchEvent(new Event("change"))}function dselect(e,t={}){e.style.display="none";const n="dselect-wrapper",c="dselect-no-results",s="dselect-tag-remove",d="dselect-tag",l="dselect-placeholder",h="dselect-clear",C="dselect-clearable",$="text-bg-dark bg-gradient",E=!1,p=!1,S=!1,o="360px",g="",u="",m="Search..",A="",U="Press Enter to add '<b>[searched-term]</b>'",D="No results found",j=P("search")||t.search||E,T=P("creatable")||t.creatable||p,O=P("clearable")||t.clearable||S,V=e.dataset.dselectMaxHeight||t.maxHeight||o,G=e.dataset.dselectClassTag||t.classTag||$,F=`${d} ${G}`,q=e.dataset.dselectSearchPlaceholder||t.searchPlaceholder||m,J=e.dataset.dselectSearchExtraClass||t.searchExtraClass||A,K=e.dataset.dselectNoResultsPlaceholder||t.noResultsPlaceholder||D,k=e.dataset.dselectAddOptionPlaceholder||t.addOptionPlaceholder||U,R=e.dataset.dselectItemClass||t.itemClass||u,L=e.dataset.dselectSize||t.size||g,w=`form-select${L!==""?` form-select-${L}`:""}`,Q=j?`<input onkeydown="dselectSearch(event, this, '${n}', '${w}', ${T}, '${k.replace(/'/g,"\\'")}');" onkeyup="dselectSearch(event, this, '${n}', '${w}', ${T}, '${k.replace(/'/g,"\\'")}')" oninput="dselectSearch(event, this, '${n}', '${w}', ${T}, '${k.replace(/'/g,"\\'")}')" type="text" class="form-control ${J}" placeholder="${q}" autofocus>`:"";function P(i){const r=`data-dselect-${i}`;return e.hasAttribute(r)?e.getAttribute(r).toLowerCase()==="true":null}function W(){e.nextElementSibling&&e.nextElementSibling.classList&&e.nextElementSibling.classList.contains(n)&&e.nextElementSibling.remove()}function y(i){return i?i.getAttribute("value")==="":!0}function N(i,r){if(r){const a=Array.from(i).filter(f=>f.selected&&!y(f)),x=Array.from(i).filter(f=>y(f));let b=[];if(a.length===0){const f=x.length?x[0].textContent:"&nbsp;";b.push(`<span class="${l}">${f}</span>`)}else for(const f of a)b.push(` <div class="${F}" data-dselect-value="${f.value}"> ${f.text} <svg onclick="dselectRemoveTag(this, '${n}', '${w}')" class="${s}" width="14" height="14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/></svg> </div> `);return b.join("")}else{const a=i[i.selectedIndex];return a?y(a)?`<span class="${l}">${a.innerHTML}</span>`:a.innerHTML:`<span class="${l}">${q}</span>`}}function z(i){const r=i[i.selectedIndex];return y(r)?"":r.textContent}function B(i){let r=[];for(const a of i)if(a.tagName==="OPTGROUP")r.push(`<h6 class="dropdown-header">${a.getAttribute("label")}</h6>`);else{const x=y(a)?" hidden":"",b=a.selected?" active":"",f=a.selected?" disabled":"",v=a.getAttribute("disabled"),Z=R===""?"":" "+R;let I="";v!==null?I="disabled='true'":I="";const _=a.value;let H=a.textContent;if(a.hasAttribute("data-dselect-img")&&a.getAttribute("data-dselect-img").trim()!==""){const ee=a.getAttribute("data-dselect-img").trim();let M="1rem";L=="sm"?M=".7rem":L=="lg"&&(M="1.2rem"),H=`<span class="d-flex align-items-center"> <img src="${ee}" style="max-height:${M}; width:auto;"> <span class="ps-2">${H}</span> </span>`}r.push(`<button${x} class="dropdown-item${b}${Z}" ${I} data-dselect-value="${_}" type="button" onclick="dselectUpdate(this, '${n}', '${w}')" ${f}> ${H} </button>`)}return r=r.join(""),r}function X(){const i=e.multiple?' data-bs-auto-close="outside"':"",r=Array.from(e.classList).filter(v=>v!=="form-select"&&v!=="form-select-sm"&&v!=="form-select-lg").join(" "),a=O&&!e.multiple?` <button type="button" class="btn ${h}" title="Clear selection" onclick="dselectClear(this, '${n}')"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" fill="none"> <path d="M13 1L0.999999 13" stroke-width="2" stroke="currentColor"></path> <path d="M1 1L13 13" stroke-width="2" stroke="currentColor"></path> </svg> </button> `:"",x=B(e.querySelectorAll("*")),b=` <div class="dropdown ${n} ${r}"> <button class="${w} ${!e.multiple&&O?C:""}" data-dselect-text="${!e.multiple&&z(e.options)}" type="button" data-bs-toggle="dropdown" aria-expanded="false"${i}> ${N(e.options,e.multiple)} </button> <div class="dropdown-menu"> <div class="d-flex flex-column"> ${Q} <div class="dselect-items" style="max-height:${V};overflow:auto"> ${x} </div> <div class="${c} d-none">${K}</div> </div> </div> ${a} </div>`;W(),e.insertAdjacentHTML("afterend",b),e.nextElementSibling.addEventListener("shown.bs.dropdown",function(){const v=this.querySelector('input[type="text"]');v&&v.focus()})}X();function Y(){const i=e.nextElementSibling,r=i.getElementsByClassName(w)[0],a=i.getElementsByClassName("dselect-items")[0];r.innerHTML=N(e.options,e.multiple),a.innerHTML=B(e.querySelectorAll("*")),e.multiple||(r.dataset.dselectText=z(e.options))}e.addEventListener("change",Y)}typeof window!="undefined"&&(window.dselectUpdate=dselectUpdate,window.dselectRemoveTag=dselectRemoveTag,window.dselectSearch=dselectSearch,window.dselectNavigate=dselectNavigate,window.dselectClear=dselectClear,window.dselect=dselect);