@jthomae1/dselect
Version:
Dropdown select box for bootstrap 5.3
30 lines (29 loc) • 5.87 kB
JavaScript
function dselectUpdate(e,t,a){const d=e.dataset.dselectValue,o=e.closest(`.${t}`).previousElementSibling,u=o.nextElementSibling.getElementsByClassName(a)[0],i=o.nextElementSibling.querySelector("input"),h=e.parentNode.nextElementSibling;o.multiple?Array.from(o.options).filter(m=>m.value===d)[0].selected=!0:o.value=d,o.multiple&&u.click(),o.dispatchEvent(new Event("change")),u.focus(),i&&(i.value="",h.classList.add("d-none"))}function dselectRemoveTag(e,t,a){const d=e.parentNode.dataset.dselectValue,o=e.closest(`.${t}`).previousElementSibling,u=o.nextElementSibling.getElementsByClassName(a)[0],i=o.nextElementSibling.querySelector("input");Array.from(o.options).filter(v=>v.value===d)[0].selected=!1,o.dispatchEvent(new Event("change")),u.click(),i&&(i.value="")}function dselectSearch(e,t,a,d,o){const u=t.value.toLowerCase().trim(),i=t.nextElementSibling,v=i.querySelectorAll(".dropdown-header"),h=i.querySelectorAll(".dropdown-item"),m=i.nextElementSibling;v.forEach(c=>c.classList.add("d-none"));let E=!1;h.forEach(c=>{const g=c.textContent.toLowerCase();if(g.indexOf(u)>-1){c.classList.remove("d-none"),g===u&&(E=!0);let p=c;for(;p=p.previousElementSibling;)if(p.classList.contains("dropdown-header")){p.classList.remove("d-none");break}}else c.classList.add("d-none")});const y=Array.from(h).filter(c=>!c.classList.contains("d-none")&&!c.hasAttribute("hidden"));if(o&&!E&&u.length>=3){if(m.classList.remove("d-none"),m.innerHTML=`Press Enter to add "<strong>${t.value}</strong>"`,e.key==="Enter"){const c=t.closest(`.${a}`).previousElementSibling,g=c.nextElementSibling.getElementsByClassName(d)[0];c.insertAdjacentHTML("afterbegin",`<option value="${t.value}" selected>${t.value}</option>`),c.dispatchEvent(new Event("change")),t.value="",t.dispatchEvent(new Event("keyup")),g.click(),g.focus(),i.classList.remove("d-none")}}else m.classList.add("d-none")}function dselectClear(e,t){const a=e.closest(`.${t}`).previousElementSibling;Array.from(a.options).forEach(d=>d.selected=!1),a.dispatchEvent(new Event("change"))}function dselect(e,t={}){e.style.display="none";const a="dselect-wrapper",d="dselect-no-results",o="dselect-tag",u="dselect-tag-remove",i="dselect-placeholder",v="dselect-clear",h="dselect-clearable",m=!1,E=!1,y=!1,c="360px",g="",p=S("search")||t.search||m,k=S("creatable")||t.creatable||E,C=S("clearable")||t.clearable||y,M=e.dataset.dselectMaxHeight||t.maxHeight||c;let w=e.dataset.dselectSize||t.size||g;w=w!==""?` form-select-${w}`:"";const $=`form-select${w}`,H=p?`<input onkeydown="return event.key !== 'Enter'" onkeyup="dselectSearch(event, this, '${a}', '${$}', ${k})" type="text" class="form-control" placeholder="Search" autofocus>`:"";function S(n){const l=`data-dselect-${n}`;return e.hasAttribute(l)?e.getAttribute(l).toLowerCase()==="true":null}function B(){e.nextElementSibling&&e.nextElementSibling.classList&&e.nextElementSibling.classList.contains(a)&&e.nextElementSibling.remove()}function x(n){return n.getAttribute("value")===""}function L(n,l){if(l){const s=Array.from(n).filter(r=>r.selected&&!x(r)),b=Array.from(n).filter(r=>x(r));let f=[];if(s.length===0){const r=b.length?b[0].textContent:" ";f.push(`<span class="${i}">${r}</span>`)}else for(const r of s)f.push(`
<div class="${o}" data-dselect-value="${r.value}">
${r.text}
<svg onclick="dselectRemoveTag(this, '${a}', '${$}')" class="${u}" 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 f.join("")}else{const s=n[n.selectedIndex];return x(s)?`<span class="${i}">${s.innerHTML}</span>`:s.innerHTML}}function T(n){const l=n[n.selectedIndex];return x(l)?"":l.textContent}function A(n){let l=[];for(const s of n)if(s.tagName==="OPTGROUP")l.push(`<h6 class="dropdown-header">${s.getAttribute("label")}</h6>`);else{const b=x(s)?" hidden":"",f=s.selected?" active":"",r=e.multiple&&s.selected?" disabled":"",O=s.value,R=s.textContent;l.push(`<button${b} class="dropdown-item${f}" data-dselect-value="${O}" type="button" onclick="dselectUpdate(this, '${a}', '${$}')"${r}>${R}</button>`)}return l=l.join(""),l}function N(){const n=e.multiple?' data-bs-auto-close="outside"':"",l=Array.from(e.classList).filter(f=>f!=="form-select"&&f!=="form-select-sm"&&f!=="form-select-lg").join(" "),s=C&&!e.multiple?`
<button type="button" class="btn ${v}" title="Clear selection" onclick="dselectClear(this, '${a}')">
<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>
`:"",b=`
<div class="dropdown ${a} ${l}">
<button class="${$} ${!e.multiple&&C?h:""}" data-dselect-text="${!e.multiple&&T(e.options)}" type="button" data-bs-toggle="dropdown" aria-expanded="false"${n}>
${L(e.options,e.multiple)}
</button>
<div class="dropdown-menu">
<div class="d-flex flex-column">
${H}
<div class="dselect-items" style="max-height:${M};overflow:auto">
${A(e.querySelectorAll("*"))}
</div>
<div class="${d} d-none">No results found</div>
</div>
</div>
${s}
</div>
`;B(),e.insertAdjacentHTML("afterend",b)}N();function z(){const n=e.nextElementSibling,l=n.getElementsByClassName($)[0],s=n.getElementsByClassName("dselect-items")[0];l.innerHTML=L(e.options,e.multiple),s.innerHTML=A(e.querySelectorAll("*")),e.multiple||(l.dataset.dselectText=T(e.options))}e.addEventListener("change",z)}