@skem9/dselect
Version:
Dropdown select box for bootstrap 5 with search, multiple, tag and image support
35 lines (34 loc) • 7.24 kB
JavaScript
function dselectUpdate(e,t,i){const m=e.dataset.dselectValue,c=e.closest(`.${t}`).previousElementSibling,g=c.nextElementSibling.getElementsByClassName(i)[0],d=c.nextElementSibling.querySelector("input");c.multiple?Array.from(c.options).filter(u=>u.value===m)[0].selected=!0:c.value=m,c.multiple&&g.click(),c.dispatchEvent(new Event("change")),g.focus(),d&&(d.value="")}function dselectRemoveTag(e,t,i){const m=e.parentNode.dataset.dselectValue,c=e.closest(`.${t}`).previousElementSibling,g=c.nextElementSibling.getElementsByClassName(i)[0],d=c.nextElementSibling.querySelector("input");Array.from(c.options).filter(u=>u.value===m)[0].selected=!1,c.dispatchEvent(new Event("change")),g.click(),d&&(d.value="")}function dselectSearch(e,t,i,m,c,g){const d=t.value.toLowerCase().trim(),u=t.nextElementSibling,x=Array.from(u.querySelectorAll(".dropdown-header")),E=Array.from(u.querySelectorAll(".dropdown-item")),b=u.nextElementSibling;if(x.forEach(l=>l.classList.add("d-none")),E.forEach(l=>{const f=l.textContent.toLowerCase().includes(d);if(l.classList.toggle("d-none",!f),f){let r=l.previousElementSibling;for(;r&&!r.classList.contains("dropdown-header");)r.classList.remove("d-none"),r=r.previousElementSibling}}),x.forEach(l=>{const f=l.textContent.toLowerCase().includes(d);if(l.classList.toggle("d-none",!f),f){let r=l.nextElementSibling;for(;r&&!r.classList.contains("dropdown-header");)r.classList.remove("d-none"),r=r.nextElementSibling}}),E.filter(l=>!l.classList.contains("d-none")&&!l.hasAttribute("hidden")).length===0){if(b.classList.remove("d-none"),u.classList.add("d-none"),c&&(b.innerHTML=g.replace("[searched-term]",t.value),e.key==="Enter")){const l=t.closest(`.${i}`).previousElementSibling,h=l.nextElementSibling.querySelector(`.${m}`);l.insertAdjacentHTML("afterbegin",`<option value="${t.value}" selected>${t.value}</option>`),l.dispatchEvent(new Event("change")),t.value="",t.dispatchEvent(new Event("keyup")),h.click(),h.focus()}}else b.classList.add("d-none"),u.classList.remove("d-none")}function dselectClear(e,t){const i=e.closest(`.${t}`).previousElementSibling;Array.from(i.options).forEach(m=>m.selected=!1),i.dispatchEvent(new Event("change"))}function dselect(e,t={}){e.style.display="none";const i="dselect-wrapper",m="dselect-no-results",c="dselect-tag-remove",g="dselect-tag",d="dselect-placeholder",u="dselect-clear",x="dselect-clearable",E="text-bg-dark bg-gradient",b=!1,A=!1,l=!1,h="360px",f="",r="",R="Search..",q="Press Enter to add "<strong>[searched-term]</strong>"",z="No results found",O=y("search")||t.search||b,B=y("creatable")||t.creatable||A,P=y("clearable")||t.clearable||l,N=e.dataset.dselectMaxHeight||t.maxHeight||h,j=e.dataset.dselectClassTag||t.classTag||E,V=`${g} ${j}`,U=e.dataset.dselectSearchPlaceholder||t.searchPlaceholder||R,D=e.dataset.dselectNoResultsPlaceholder||t.noResultsPlaceholder||z,G=e.dataset.dselectAddOptionPlaceholder||t.addOptionPlaceholder||q,k=e.dataset.dselectItemClass||t.ItemClass||r,S=e.dataset.dselectSize||t.size||f,$=`form-select${S!==""?` form-select-${S}`:""}`,F=O?`<input onkeydown="return event.key !== 'Enter'" onkeyup="dselectSearch(event, this, '${i}', '${$}', ${B}, '${G}')" type="text" class="form-control" placeholder="${U}" autofocus>`:"";function y(n){const a=`data-dselect-${n}`;return e.hasAttribute(a)?e.getAttribute(a).toLowerCase()==="true":null}function J(){e.nextElementSibling&&e.nextElementSibling.classList&&e.nextElementSibling.classList.contains(i)&&e.nextElementSibling.remove()}function w(n){return n.getAttribute("value")===""}function H(n,a){if(a){const s=Array.from(n).filter(o=>o.selected&&!w(o)),p=Array.from(n).filter(o=>w(o));let v=[];if(s.length===0){const o=p.length?p[0].textContent:" ";v.push(`<span class="${d}">${o}</span>`)}else for(const o of s)v.push(`
<div class="${V}" data-dselect-value="${o.value}">
${o.text}
<svg onclick="dselectRemoveTag(this, '${i}', '${$}')" class="${c}" 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 v.join("")}else{const s=n[n.selectedIndex];return w(s)?`<span class="${d}">${s.innerHTML}</span>`:s.innerHTML}}function M(n){const a=n[n.selectedIndex];return w(a)?"":a.textContent}function I(n){let a=[];for(const s of n)if(s.tagName==="OPTGROUP")a.push(`<h6 class="dropdown-header">${s.getAttribute("label")}</h6>`);else{const p=w(s)?" hidden":"",v=s.selected?" active":"",o=s.selected?" disabled":"",W=s.getAttribute("disabled"),X=k===""?"":" "+k;let C="";W!==null?C="disabled='true'":C="";const Y=s.value;let L=s.textContent;if(s.hasAttribute("data-dselect-img")&&s.getAttribute("data-dselect-img").trim()!==""){const Z=s.getAttribute("data-dselect-img").trim();let T="1rem";S=="sm"?T=".7rem":S=="lg"&&(T="1.2rem"),L=`<span class="d-flex align-items-center">
<img src="${Z}" style="max-height:${T}; width:auto;">
<span class="ps-2">${L}</span>
</span>`}a.push(`<button${p} class="dropdown-item${v}${X}" ${C} data-dselect-value="${Y}" type="button" onclick="dselectUpdate(this, '${i}', '${$}')" ${o}>
${L}
</button>`)}return a=a.join(""),a}function K(){const n=e.multiple?' data-bs-auto-close="outside"':"",a=Array.from(e.classList).filter(o=>o!=="form-select"&&o!=="form-select-sm"&&o!=="form-select-lg").join(" "),s=P&&!e.multiple?`
<button type="button" class="btn ${u}" title="Clear selection" onclick="dselectClear(this, '${i}')">
<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>
`:"",p=`
<div class="dropdown ${i} ${a}">
<button class="${$} ${!e.multiple&&P?x:""}" data-dselect-text="${!e.multiple&&M(e.options)}" type="button" data-bs-toggle="dropdown" aria-expanded="false"${n}>
${H(e.options,e.multiple)}
</button>
<div class="dropdown-menu">
<div class="d-flex flex-column">
${F}
<div class="dselect-items" style="max-height:${N};overflow:auto">
${I(e.querySelectorAll("*"))}
</div>
<div class="${m} d-none">${D}</div>
</div>
</div>
${s}
</div>
`;J(),e.insertAdjacentHTML("afterend",p),e.nextElementSibling.addEventListener("shown.bs.dropdown",function(){const o=this.querySelector('input[type="text"]');o&&o.focus()})}K();function Q(){const n=e.nextElementSibling,a=n.getElementsByClassName($)[0],s=n.getElementsByClassName("dselect-items")[0];a.innerHTML=H(e.options,e.multiple),s.innerHTML=I(e.querySelectorAll("*")),e.multiple||(a.dataset.dselectText=M(e.options))}e.addEventListener("change",Q)}typeof window!="undefined"&&(window.dselectUpdate=dselectUpdate,window.dselectRemoveTag=dselectRemoveTag,window.dselectSearch=dselectSearch,window.dselectClear=dselectClear,window.dselect=dselect);