From a532927adf40c8a1bb0680da5c8773b34efb6c93 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 7 Feb 2024 00:09:41 +0200 Subject: [PATCH 1/3] Update README.md --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index 8291e27..73cb0ad 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Features - All / None optgroup selection buttons... - **Translatable**. - **Zero dependencies**. And written in vanilla JavaScript... +- **Ajax request** - **Free to use**. Because it's MIT licensed <3 Install & Embed @@ -197,3 +198,39 @@ tail.select("select", { cbLoopGroup: undefined // [0.4.0] Function }); ``` + +### Ajax functionality +``` + tail.select('#tail-select', {ajaxUrl: url}); +``` +Widget makes a request to the backend and waits for object like this +``` +{ + "results": [ + { + "id": 1, + "text": "Boston" + }, + { + "id": 2, + "text": "Seatle" + }, + { + "id": 3, + "text": "New York" + }, + { + "id": 4, + "text": "Chicago" + }, + { + "id": 5, + "text": "Washington" + }, + { + "id": 6, + "text": "San Diego" + } + ] +} +``` From db4f2b754979fc63fc0a1fb2de78ac80c030d6d8 Mon Sep 17 00:00:00 2001 From: Igor Date: Tue, 5 Mar 2024 18:37:30 +0200 Subject: [PATCH 2/3] Update tail.select.js ajax functionality --- js/tail.select.js | 208 +++++++++++++++++++++++++++++----------------- 1 file changed, 130 insertions(+), 78 deletions(-) diff --git a/js/tail.select.js b/js/tail.select.js index 07c9574..c4bee27 100644 --- a/js/tail.select.js +++ b/js/tail.select.js @@ -1,4 +1,12 @@ /** + * Added functionality for AJAX requests, ensuring that checked items are not removed after an AJAX request + * and are left at the top of the result list. Additionally, the list scrolls to the last checked item, + * making it easier to review newly received items. + * + * @modified_by Igor Kremin + * @modified_date 2024-01-06 + * @forked_repo https://github.com/igor-kremin/tail.select.js/ + * * tail.select - The vanilla JavaScript solution to make your fields awesome! * * @author Ciprian Popescu @@ -8,4 +16,4 @@ * @license MIT License * @copyright Copyright 2020 - 2024 Ciprian Popescu */ -const tail={select:function(selector,options={}){const defaultOptions={multiTags:!1,multiCounter:!0,theme:"light",classNames:"tail-default",strings:{all:"All",none:"None",placeholder:"Select an option...",search:"Type in to search..."}},opts={...defaultOptions,...options},{multiTags:multiTags,multiCounter:multiCounter,theme:theme,classNames:classNames,strings:strings}=opts,originalSelects=document.querySelectorAll(selector);originalSelects.forEach(originalSelect=>{originalSelect.style.display="none";const customDropdown=document.createElement("div");customDropdown.classList.add("tail-select"),customDropdown.classList.add(originalSelect.id),customDropdown.classList.add(opts.classNames),customDropdown.dataset.theme=`tail-theme--${opts.theme}`,originalSelect.multiple?customDropdown.classList.add("tail--multiple"):customDropdown.classList.add("tail--single");const searchInput=document.createElement("input");searchInput.type="text",searchInput.classList.add("tail--search"),searchInput.placeholder=strings.placeholder||"Select an option...",searchInput.addEventListener("focus",()=>{searchInput.placeholder=strings.search||"Type in to search..."}),searchInput.addEventListener("blur",()=>{searchInput.placeholder=strings.placeholder||"Select an option..."}),searchInput.addEventListener("input",()=>filterOptions(originalSelect,searchInput));const tailFloatingToolbar=document.createElement("div");tailFloatingToolbar.classList.add("tail--toolbar");const toggleAllCheckbox=document.createElement("input");toggleAllCheckbox.type="checkbox",toggleAllCheckbox.value=strings.all||"All",toggleAllCheckbox.addEventListener("change",()=>toggleAll(originalSelect,toggleAllCheckbox));const toggleAllLabel=document.createElement("label");toggleAllLabel.textContent=strings.all||"All",toggleAllLabel.classList.add("all"),toggleAllLabel.appendChild(toggleAllCheckbox);const uncheckAllButton=document.createElement("button");if(uncheckAllButton.type="button",uncheckAllButton.textContent=strings.none||"None",uncheckAllButton.classList.add("uncheck"),uncheckAllButton.addEventListener("click",()=>uncheckAll(originalSelect)),opts.multiCounter){const counter=document.createElement("span");counter.textContent="0",counter.classList.add("tail--counter"),customDropdown.appendChild(counter)}const nestedList=document.createElement("div");nestedList.classList.add("tail--nested-dropdown"),nestedList.style.display="none",customDropdown.appendChild(searchInput),customDropdown.appendChild(tailFloatingToolbar),customDropdown.appendChild(nestedList),tailFloatingToolbar.appendChild(toggleAllLabel),tailFloatingToolbar.appendChild(uncheckAllButton),originalSelect.insertAdjacentElement("afterend",customDropdown);const selectedOptionsList=document.createElement("ul");function buildNestedList(){const fragment=document.createDocumentFragment(),optgroups=originalSelect.getElementsByTagName("optgroup");if(optgroups.length>0)for(let i=0;itoggleOptgroup(optgroupCheckbox)),optgroupLabel.appendChild(optgroupCheckbox);const optgroupLabelText=document.createElement("span");optgroupLabelText.textContent=optgroup.label,optgroupLabelText.classList.add("tail--optgroup-label"),optgroupLabel.appendChild(optgroupLabelText),optgroupItem.appendChild(optgroupLabel);const nestedOptionsList=document.createElement("div");nestedOptionsList.setAttribute("role","listbox"),nestedOptionsList.classList.add("tail--nested-dropdown-list");const options=optgroup.getElementsByTagName("option");for(let j=0;j${option.dataset.description}`),option.selected&&option.hasAttribute("selected")&&(optionCheckbox.checked=!0,updateCounter(originalSelect),updateCustomTextInput(originalSelect)),optionLabel.appendChild(optionCheckbox),optionLabel.appendChild(optionLabelText),optionItem.appendChild(optionLabel),nestedOptionsList.appendChild(optionItem)}optgroupItem.appendChild(nestedOptionsList),nestedList.appendChild(optgroupItem)}else{const options=originalSelect.getElementsByTagName("option");for(let j=0;j${option.dataset.description}`),option.selected&&option.hasAttribute("selected")&&(optionCheckbox.checked=!0,updateCounter(originalSelect),updateCustomTextInput(originalSelect)),optionLabel.appendChild(optionCheckbox),optionLabel.appendChild(optionLabelText),optionItem.appendChild(optionLabel),nestedList.appendChild(optionItem)}}customDropdown.setAttribute("role","combobox"),customDropdown.setAttribute("aria-haspopup","true"),customDropdown.setAttribute("aria-expanded","false"),attachOptionCheckboxListeners(),nestedList.appendChild(fragment)}function toggleAll(originalSelect,toggleAllCheckbox){const isChecked=toggleAllCheckbox.checked,optionCheckboxes=nestedList.querySelectorAll('input[type="checkbox"]');optionCheckboxes.forEach(checkbox=>{checkbox.checked=isChecked,updateOriginalOptionState(originalSelect,checkbox)})}function uncheckAll(originalSelect){const optionCheckboxes=nestedList.querySelectorAll('input[type="checkbox"]');optionCheckboxes.forEach(checkbox=>{checkbox.checked=!1,updateOriginalOptionState(originalSelect,checkbox)});const originalOptions=originalSelect.getElementsByTagName("option");for(let i=0;icb.checked=!1),checkbox.checked=!0,updateOriginalOptionState(originalSelect,checkbox)}}function toggleOptgroup(optgroupCheckbox){const isChecked=optgroupCheckbox.checked,nestedOptionsList=optgroupCheckbox.closest(".tail--optgroup").querySelector(".tail--nested-dropdown-list"),optionCheckboxes=nestedOptionsList.querySelectorAll('input[type="checkbox"]');if(optionCheckboxes.forEach(checkbox=>{checkbox.checked=isChecked,toggleOption(checkbox)}),!originalSelect.multiple){const customDropdown=originalSelect.closest(".tail-select");if(customDropdown){const otherOptgroupCheckboxes=customDropdown.querySelectorAll('.tail--nested-dropdown-item input[type="checkbox"]');otherOptgroupCheckboxes.forEach(cb=>{cb!==optgroupCheckbox&&(cb.checked=!1,updateOriginalOptionState(originalSelect,cb))})}}updateOriginalOptionState(originalSelect,optgroupCheckbox)}function attachOptionCheckboxListeners(){const optionCheckboxes=nestedList.querySelectorAll('.tail--nested-dropdown-item input[type="checkbox"]');optionCheckboxes.forEach(checkbox=>{checkbox.addEventListener("change",()=>toggleOption(checkbox))})}function updateOriginalOptionState(originalSelect,checkbox,customDropdown){const optionValue=checkbox.value,option=Array.from(originalSelect.options).find(opt=>opt.value===optionValue||opt.textContent===optionValue);if(option){checkbox.checked?option.selected=!0:option.selected=!1;const event=new Event("change",{bubbles:!0});originalSelect.dispatchEvent(event)}const selectedOptions=Array.from(originalSelect.options).filter(opt=>opt.selected);originalSelect.multiple?searchInput.value=selectedOptions.map(opt=>opt.textContent).join(", "):selectedOptions.length>0&&searchInput?searchInput.value=selectedOptions[0].textContent:searchInput.value="",opts.multiTags&&originalSelect.multiple&&updateSelectedOptionsList(selectedOptionsList,selectedOptions);const selectedValues=selectedOptions.map(opt=>opt.value);for(var options=originalSelect.options,count=0,i=0;i{const optionCheckbox=optionItem.querySelector('input[type="checkbox"]'),optionLabel=optionCheckbox.nextElementSibling.textContent.toLowerCase(),optgroupItem=optionItem.closest("div");optionCheckbox.style.display=optionLabel.includes(searchTerm)?"inline-block":"none"}),optionItems.forEach(optionItem=>{const optionCheckbox=optionItem.querySelector('input[type="checkbox"]'),optgroupItem=optionItem.closest("div"),nestedCheckboxes=optionItem.querySelectorAll('div input[type="checkbox"]:not([style="display: none;"])'),hasVisibleNestedCheckboxes=nestedCheckboxes.length>0;optgroupItem.style.display="inline-block"===optionCheckbox.style.display||hasVisibleNestedCheckboxes?"block":"none"})}function updateSelectedOptionsList(selectedOptionsList,selectedOptions){selectedOptionsList.innerHTML="",selectedOptions.forEach(opt=>{const listItem=document.createElement("li");listItem.textContent=opt.textContent,selectedOptionsList.appendChild(listItem)})}function updateCustomTextInput(originalSelect){const selectedOptions=Array.from(originalSelect.options).filter(opt=>opt.selected&&opt.hasAttribute("selected"));originalSelect.multiple?searchInput.value=selectedOptions.map(opt=>opt.textContent).join(", "):selectedOptions.length>0&&searchInput?searchInput.value=selectedOptions[0].textContent:searchInput.value="",opts.multiTags&&originalSelect.multiple&&updateSelectedOptionsList(selectedOptionsList,selectedOptions)}function updateCounter(originalSelect){let customId=originalSelect.id;if(customId){let counterElement=document.querySelector(`.${customId}`).querySelector(".tail--counter");if(counterElement){const count=Array.from(originalSelect.options).filter(opt=>opt.selected).length;counterElement.textContent=count}}}function toggleDropdownVisibility(){nestedList.style.display="block",customDropdown.setAttribute("aria-expanded","true")}function hideDropdown(){nestedList.style.display="none",customDropdown.setAttribute("aria-expanded","false")}function handleClickOutside(event){customDropdown.contains(event.target)||hideDropdown()}function handleKeyDown(event){"Escape"===event.key&&hideDropdown()}selectedOptionsList.classList.add("tail--selected-options-list"),opts.multiTags&&originalSelect.multiple&&customDropdown.insertAdjacentElement("afterend",selectedOptionsList),searchInput.addEventListener("focus",toggleDropdownVisibility),document.addEventListener("click",handleClickOutside),document.addEventListener("keydown",handleKeyDown),buildNestedList()})}}; \ No newline at end of file +const tail={select:function(e,t={}){const I={multiTags:!1,multiCounter:!0,ajaxUrl:null,theme:"light",classNames:"tail-default",strings:{all:"All",none:"None",placeholder:"Select an option...",search:"Type in to search..."},...t},{ajaxUrl:M,strings:$}=I;document.querySelectorAll(e).forEach(o=>{o.style.display="none";const t=document.createElement("div"),d=(t.classList.add("tail-select"),t.classList.add(o.id),t.classList.add(I.classNames),t.dataset.theme="tail-theme--"+I.theme,o.multiple?t.classList.add("tail--multiple"):t.classList.add("tail--single"),document.createElement("input"));d.type="text",d.classList.add("tail--search"),d.placeholder=$.placeholder||"Select an option...",d.addEventListener("focus",()=>{d.placeholder=$.search||"Type in to search...",d.select()}),d.addEventListener("blur",()=>{d.placeholder=$.placeholder||"Select an option..."}),d.addEventListener("input",()=>{{var e;const l=d.value.trim().toLowerCase(),t=i.querySelectorAll("div");t.forEach(e=>{var t=e.querySelector('input[type="checkbox"]'),n=t.nextElementSibling.textContent.toLowerCase().includes(l),t=t.checked;e.style.display=t||n?"":"none"}),t.forEach(e=>{var t=e.querySelector('input[type="checkbox"]'),n=e.closest("div"),e=0{{var t=o,e;const n=a.checked,l=i.querySelectorAll('input[type="checkbox"]');l.forEach(e=>{e.checked=n,c(t,e)})}});var n=document.createElement("label"),l=(n.textContent=$.all||"All",n.classList.add("all"),n.appendChild(a),document.createElement("button"));l.type="button",l.textContent=$.none||"None",l.classList.add("uncheck"),l.addEventListener("click",()=>{var t=o,n=(i.querySelectorAll('input[type="checkbox"]').forEach(e=>{e.checked=!1,c(t,e)}),t.getElementsByTagName("option"));for(let e=0;ee.checked=!1),e.checked=!0),c(o,e)}function c(e,t){const n=t.value;for(var l=Array.from(e.options).find(e=>e.value===n||e.textContent===n),t=(l&&(l.selected=t.checked,l=new Event("change",{bubbles:!0}),e.dispatchEvent(l)),Array.from(e.options).filter(e=>e.selected)),a=(e.multiple?d.value=t.map(e=>e.textContent).join(", "):0e.value),e.options),c=0;c{var t=document.createElement("li");t.textContent=e.textContent,n.appendChild(t)})}function u(e){var t=Array.from(e.options).filter(e=>e.selected&&e.hasAttribute("selected"));e.multiple?d.value=t.map(e=>e.textContent).join(", "):0e.selected).length,t.textContent=e)}function m(){i.style.display="none",t.setAttribute("aria-expanded","false")}r.classList.add("tail--selected-options-list"),I.multiTags&&o.multiple&&t.insertAdjacentElement("afterend",r),d.addEventListener("focus",function(){i.style.display="block",t.setAttribute("aria-expanded","true")}),document.addEventListener("click",function(e){t.contains(e.target)||m()}),document.addEventListener("keydown",function(e){"Escape"===e.key&&m()});var v=document.createDocumentFragment(),y=o.getElementsByTagName("optgroup");if(0{{var t=U,e;const n=t.checked,l=t.closest(".tail--optgroup").querySelector(".tail--nested-dropdown-list"),a=l.querySelectorAll('input[type="checkbox"]');a.forEach(e=>{e.checked=n,s(e)}),o.multiple||(e=o.closest(".tail-select"))&&e.querySelectorAll('.tail--nested-dropdown-item input[type="checkbox"]').forEach(e=>{e!==t&&(e.checked=!1,c(o,e))}),c(o,t)}}),x.appendChild(U);var b=document.createElement("span"),f=(b.textContent=E.label,b.classList.add("tail--optgroup-label"),x.appendChild(b),C.appendChild(x),document.createElement("div")),g=(f.setAttribute("role","listbox"),f.classList.add("tail--nested-dropdown-list"),E.getElementsByTagName("option"));for(let e=0;e${k.dataset.description}`),k.selected&&k.hasAttribute("selected")&&(A.checked=!0,h(o),u(o)),S.appendChild(A),S.appendChild(q),L.appendChild(S),f.appendChild(L)}C.appendChild(f),i.appendChild(C)}else{var w=o.getElementsByTagName("option");for(let e=0;e${T.dataset.description}`),T.selected&&T.hasAttribute("selected")&&(N.checked=!0,h(o),u(o)),B.appendChild(N),B.appendChild(H),j.appendChild(B),i.appendChild(j)}}t.setAttribute("role","combobox"),t.setAttribute("aria-haspopup","true"),t.setAttribute("aria-expanded","false"),i.querySelectorAll('.tail--nested-dropdown-item input[type="checkbox"]').forEach(e=>{e.addEventListener("change",()=>s(e))}),i.appendChild(v),M&&d.addEventListener("input",function(){var e,t;[e=""]=[this.value],(t=I.ajaxUrl)&&e&&(t+="?term="+encodeURIComponent(e),fetch(t).then(e=>e.json()).then(e=>{{e=e.results;const d=[];i.querySelectorAll('input[type="checkbox"]:checked').forEach(e=>{d.push(parseInt(e.value,10))}),i.querySelectorAll(".tail--nested-dropdown-item").forEach(e=>{const t=e.querySelector('input[type="checkbox"]');var n;t.checked||((n=Array.from(o.options).find(e=>e.value===t.value))&&o.remove(n.index),e.remove())}),e.forEach(e=>{var t=parseInt(e.id,10);if(!d.includes(t)){var n=document.createElement("div");n.classList.add("tail--nested-dropdown-item");const c=document.createElement("input");c.type="checkbox",c.value=t,c.addEventListener("change",()=>{s(c)});var l=document.createElement("label"),a=document.createElement("span"),a=(a.textContent=e.text,l.appendChild(c),l.appendChild(a),n.appendChild(l),i.appendChild(n),new Option(e.text,t,!1,!1));o.add(a),0console.error("Error loading data:",e)))})})}};