advanced-filter-system 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -108
- package/dist/AFS.esm.js +15 -6
- package/dist/AFS.esm.js.map +1 -1
- package/dist/AFS.js +15 -6
- package/dist/AFS.js.map +1 -1
- package/dist/AFS.min.js +1 -1
- package/dist/AFS.min.js.map +1 -1
- package/package.json +1 -1
package/dist/AFS.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).AFS={})}(this,(function(t){"use strict";function e(t,e){let s;return function(){for(var i=arguments.length,r=new Array(i),o=0;o<i;o++)r[o]=arguments[o];clearTimeout(s),s=setTimeout((()=>{clearTimeout(s),t(...r)}),e)}}t.AFS=class{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.options={containerSelector:".filter-container",itemSelector:".filter-item",filterButtonSelector:".btn-filter",searchInputSelector:".filter-search",counterSelector:".filter-counter",activeClass:"active",hiddenClass:"hidden",animationDuration:300,filterMode:"OR",searchKeys:["title"],debounceTime:300,...t},this.container=document.querySelector(this.options.containerSelector),this.items=document.querySelectorAll(this.options.itemSelector),this.filterButtons=document.querySelectorAll(this.options.filterButtonSelector),this.searchInput=document.querySelector(this.options.searchInputSelector),this.counter=document.querySelector(this.options.counterSelector),this.currentFilters=new Set(["*"]),this.currentSearch="",this.visibleItems=new Set(this.items),this.filterGroups=new Map,this.groupMode="OR",this.init()}init(){this.addStyles(),this.bindEvents(),this.loadFromURL(),this.updateCounter()}addStyles(){const t=`\n .${this.options.hiddenClass} {\n display: none !important;\n }\n\n ${this.options.itemSelector} {\n opacity: 1;\n transform: scale(1);\n transition: opacity ${this.options.animationDuration}ms ease-out,\n transform ${this.options.animationDuration}ms ease-out;\n }\n\n ${this.options.filterButtonSelector} {\n opacity: 0.5;\n transition: opacity ${this.options.animationDuration}ms ease;\n }\n\n ${this.options.filterButtonSelector}.${this.options.activeClass} {\n opacity: 1;\n }\n `,e=document.createElement("style");e.textContent=t,document.head.appendChild(e)}bindEvents(){this.filterButtons.forEach((t=>{t.addEventListener("click",(()=>this.handleFilterClick(t)))})),this.searchInput&&this.searchInput.addEventListener("input",e((t=>{this.search(t.target.value)}),this.options.debounceTime)),window.addEventListener("popstate",(()=>this.loadFromURL()))}handleFilterClick(t){const e=t.dataset.filter;"*"===e?this.resetFilters():this.toggleFilter(e,t),this.filter(),this.updateURL()}resetFilters(){this.filterButtons.forEach((t=>t.classList.remove(this.options.activeClass))),this.currentFilters.clear(),this.currentFilters.add("*"),this.filterButtons[0].classList.add(this.options.activeClass),this.resetCounter()}resetCounter(){this.visibleItems=new Set(this.items),this.updateCounter()}toggleFilter(t,e){this.currentFilters.delete("*"),this.filterButtons[0].classList.remove(this.options.activeClass),e.classList.contains(this.options.activeClass)?(e.classList.remove(this.options.activeClass),this.currentFilters.delete(t),0===this.currentFilters.size&&this.resetFilters()):(e.classList.add(this.options.activeClass),this.currentFilters.add(t))}filter(){const t=()=>{this.visibleItems.clear(),this.items.forEach((t=>{if(this.currentFilters.has("*"))this.showItem(t),this.visibleItems.add(t);else{const e=new Set(t.dataset.categories?.split(" ")||[]);("OR"===this.options.filterMode?this.matchesAnyFilter(e):this.matchesAllFilters(e))?(this.showItem(t),this.visibleItems.add(t)):this.hideItem(t)}}))};0===this.filterGroups.size?t():(this.visibleItems.clear(),this.items.forEach((t=>{if(this.currentFilters.has("*"))this.showItem(t),this.visibleItems.add(t);else{const e=new Set(t.dataset.categories?.split(" ")||[]);this.matchesFilterGroups(e)?(this.showItem(t),this.visibleItems.add(t)):this.hideItem(t)}}))),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}addFilterGroup(t,e){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"OR";try{if(!t||!Array.isArray(e))return console.warn("Invalid group parameters"),!1;const i=s.toUpperCase();return["AND","OR"].includes(i)||(console.warn('Invalid operator. Using default "OR"'),s="OR"),this.filterGroups.set(t,{filters:new Set(e),operator:i}),this.filterGroups.size>0&&(this.updateFiltersFromGroups(),this.filter(),this.updateURL()),!0}catch(t){return console.error("Error adding filter group:",t),!1}}setGroupMode(t){const e=t.toUpperCase();["AND","OR"].includes(e)&&(this.groupMode=e,this.filterGroups.size>0&&this.filter())}removeFilterGroup(t){return!!this.filterGroups.has(t)&&(this.filterGroups.delete(t),0===this.filterGroups.size?this.resetFilters():this.updateFiltersFromGroups(),this.filter(),this.updateURL(),!0)}updateFiltersFromGroups(){if(0!==this.filterGroups.size){this.currentFilters.has("*")||this.currentFilters.clear();for(const t of this.filterGroups.values())t.filters.forEach((t=>{"*"!==t&&this.currentFilters.add(t)}))}}matchesAnyFilter(t){return[...this.currentFilters].some((e=>{const[s,i]=e.split(":");return t.has(`${s}:${i}`)}))}matchesAllFilters(t){return[...this.currentFilters].every((e=>{const[s,i]=e.split(":");return t.has(`${s}:${i}`)}))}showItem(t){this.visibleItems.add(t),t.classList.remove(this.options.hiddenClass),t.style.opacity="0",t.style.transform="scale(0.95)",t.offsetHeight,requestAnimationFrame((()=>{t.style.opacity="1",t.style.transform="scale(1)"}))}hideItem(t){t.style.opacity="0",t.style.transform="scale(0.95)",setTimeout((()=>{"0"===t.style.opacity&&(t.classList.add(this.options.hiddenClass),this.visibleItems.delete(t))}),this.options.animationDuration)}search(t){this.currentSearch=t.toLowerCase().trim(),this.items.forEach((t=>{const e=this.options.searchKeys.map((e=>t.dataset[e]||"")).join(" ").toLowerCase();""===this.currentSearch||e.includes(this.currentSearch)?this.showItem(t):this.hideItem(t)})),this.updateURL(),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}sortMultiple(t){const e=[...this.items];e.sort(((e,s)=>{for(const i of t){const t=e.dataset[i.key],r=s.dataset[i.key],o="asc"===i.direction?t.localeCompare(r):r.localeCompare(t);if(0!==o)return o}return 0})),e.forEach((t=>this.container.appendChild(t)))}addRangeFilter(t,e,s){this.items.forEach((i=>{const r=parseFloat(i.dataset[t]);r>=e&&r<=s?this.showItem(i):this.hideItem(i)})),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}matchesFilterGroups(t){const e=[...this.filterGroups.values()].map((e=>{const s=[...e.filters];return 0===s.length||("OR"===e.operator?s.some((e=>t.has(e))):s.every((e=>t.has(e))))}));return"OR"===this.groupMode?e.some((t=>t)):e.every((t=>t))}updateURL(){const t=new URLSearchParams(window.location.search);if(this.filterGroups.size>0){for(const[e,s]of this.filterGroups.entries())t.set(`group_${e}`,[...s.filters].join(",")),t.set(`groupOp_${e}`,s.operator.toLowerCase());t.set("groupMode",this.groupMode.toLowerCase())}const e={};for(const t of this.currentFilters)if("*"!==t){const[s,i]=t.split(":");e[s]||(e[s]=new Set),e[s].add(i)}Object.entries(e).forEach((e=>{let[s,i]=e;t.set(s,Array.from(i).join(","))})),this.currentSearch&&t.set("search",this.currentSearch);const s=`${window.location.pathname}${t.toString()?"?"+t.toString():""}`;window.history.pushState({},"",s)}loadFromURL(){const t=new URLSearchParams(window.location.search);this.filterGroups.clear();for(const[e,s]of t.entries())if(e.startsWith("group_")){const i=e.replace("group_",""),r=t.get(`groupOp_${i}`)?.toUpperCase()||"OR",o=s.split(",");this.addFilterGroup(i,o,r)}const e=t.get("groupMode")?.toUpperCase();e&&["AND","OR"].includes(e)&&(this.groupMode=e),this.currentFilters.clear();let s=!1;for(const[e,i]of t.entries())"search"!==e&&(s=!0,i.split(",").forEach((t=>{this.currentFilters.add(`${e}:${t}`)})));s||this.currentFilters.add("*"),this.filterButtons.forEach((t=>{const e=t.dataset.filter;this.currentFilters.has(e)||"*"===e&&this.currentFilters.has("*")?t.classList.add(this.options.activeClass):t.classList.remove(this.options.activeClass)}));const i=t.get("search")||"";this.searchInput&&(this.searchInput.value=i),this.filter(),i&&this.search(i)}updateCounter(){const t=this.items.length,e=this.visibleItems.size;return this.counter&&(this.counter.textContent=`Showing ${e} of ${t}`),{total:t,visible:e}}setAnimationOptions(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.options.animationDuration=t.duration||this.options.animationDuration,this.options.animationType=t.type||"ease-out",this.addStyles()}addEventSystem(){this.events={},this.on=(t,e)=>{this.events[t]||(this.events[t]=[]),this.events[t].push(e)},this.emit=(t,e)=>{this.events[t]&&this.events[t].forEach((t=>t(e)))}}setPagination(t){this.pagination={currentPage:1,itemsPerPage:t,totalPages:Math.ceil(this.visibleItems.size/t)},this.updatePagination()}updatePagination(){const t=(this.pagination.currentPage-1)*this.pagination.itemsPerPage,e=t+this.pagination.itemsPerPage;[...this.visibleItems].forEach(((s,i)=>{i>=t&&i<e?this.showItem(s):this.hideItem(s)}))}enableAnalytics(t){this.analyticsCallback=t,this.on("filter",(t=>{this.analyticsCallback({type:"filter",filters:[...this.currentFilters],visibleItems:this.visibleItems.size,timestamp:(new Date).toISOString()})}))}sortWithComparator(t,e){const s=[...this.items];s.sort(((s,i)=>{const r=s.dataset[t],o=i.dataset[t];return e(r,o)})),s.forEach((t=>this.container.appendChild(t)))}setResponsiveOptions(t){window.addEventListener("resize",e((()=>{const e=window.innerWidth;for(const[s,i]of Object.entries(t))if(e<=parseInt(s)){Object.assign(this.options,i),this.filter();break}}),250))}enableKeyboardNavigation(){document.addEventListener("keydown",(t=>{"Enter"===t.key&&document.activeElement.classList.contains(this.options.filterButtonSelector.slice(1))&&document.activeElement.click()}))}exportState(){return{filters:[...this.currentFilters],search:this.currentSearch,mode:this.options.filterMode,timestamp:(new Date).toISOString()}}importState(t){t.filters&&(this.currentFilters=new Set(t.filters),this.currentSearch=t.search||"",this.options.filterMode=t.mode||"OR",this.filter(),this.updateURL())}savePreset(t){const e={filters:[...this.currentFilters],search:this.currentSearch,mode:this.options.filterMode};localStorage.setItem(`afs_preset_${t}`,JSON.stringify(e))}loadPreset(t){const e=JSON.parse(localStorage.getItem(`afs_preset_${t}`));e&&(this.currentFilters=new Set(e.filters),this.currentSearch=e.search,this.options.filterMode=e.mode,this.filter(),this.updateURL())}setLogic(t){if("boolean"==typeof t)return this.options.filterMode=t?"AND":"OR",void this.filter();const e=t.toUpperCase();["OR","AND"].includes(e)&&(this.options.filterMode=e,this.filter())}setFilterMode(t){["OR","AND"].includes(t.toUpperCase())&&(this.options.filterMode=t.toUpperCase(),this.filter())}addFilter(t,e){this.currentFilters.add(`${t}:${e}`),this.filter(),this.updateURL()}removeFilter(t,e){this.currentFilters.delete(`${t}:${e}`),0===this.currentFilters.size&&this.currentFilters.add("*"),this.filter(),this.updateURL()}getActiveFiltersByType(t){return[...this.currentFilters].filter((e=>e.startsWith(`${t}:`))).map((t=>t.split(":")[1]))}}}));
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).AFS={})}(this,(function(t){"use strict";function e(t,e){let s;return function(){for(var i=arguments.length,r=new Array(i),o=0;o<i;o++)r[o]=arguments[o];clearTimeout(s),s=setTimeout((()=>{clearTimeout(s),t(...r)}),e)}}t.AFS=class{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.options={containerSelector:".filter-container",itemSelector:".filter-item",filterButtonSelector:".btn-filter",searchInputSelector:".filter-search",counterSelector:".filter-counter",activeClass:"active",hiddenClass:"hidden",animationDuration:300,filterMode:"OR",searchKeys:["title"],debounceTime:300,...t},this.container=document.querySelector(this.options.containerSelector),this.items=document.querySelectorAll(this.options.itemSelector),this.filterButtons=document.querySelectorAll(this.options.filterButtonSelector),this.searchInput=document.querySelector(this.options.searchInputSelector),this.counter=document.querySelector(this.options.counterSelector),this.currentFilters=new Set(["*"]),this.currentSearch="",this.visibleItems=new Set(this.items),this.filterGroups=new Map,this.groupMode="OR",this.init()}init(){this.addStyles(),this.bindEvents(),this.loadFromURL(),this.updateCounter()}addStyles(){const t=`\n .${this.options.hiddenClass} {\n display: none !important;\n }\n\n ${this.options.itemSelector} {\n opacity: 1;\n transform: scale(1);\n transition: opacity ${this.options.animationDuration}ms ease-out,\n transform ${this.options.animationDuration}ms ease-out;\n }\n\n ${this.options.filterButtonSelector} {\n opacity: 0.5;\n transition: opacity ${this.options.animationDuration}ms ease;\n }\n\n ${this.options.filterButtonSelector}.${this.options.activeClass} {\n opacity: 1;\n }\n `,e=document.createElement("style");e.textContent=t,document.head.appendChild(e)}bindEvents(){this.filterButtons.forEach((t=>{t.addEventListener("click",(()=>this.handleFilterClick(t)))})),this.searchInput&&this.searchInput.addEventListener("input",e((t=>{this.search(t.target.value)}),this.options.debounceTime)),window.addEventListener("popstate",(()=>this.loadFromURL()))}handleFilterClick(t){const e=t.dataset.filter;"*"===e?this.resetFilters():this.toggleFilter(e,t),this.filter(),this.updateURL()}resetFilters(){this.filterButtons.forEach((t=>t.classList.remove(this.options.activeClass))),this.currentFilters.clear(),this.currentFilters.add("*"),this.filterButtons[0].classList.add(this.options.activeClass),this.resetCounter()}resetCounter(){this.visibleItems=new Set(this.items),this.updateCounter()}toggleFilter(t,e){if(this.currentFilters.delete("*"),this.filterButtons[0].classList.remove(this.options.activeClass),e.classList.contains(this.options.activeClass)){if(e.classList.remove(this.options.activeClass),this.currentFilters.delete(t),0===this.currentFilters.size)return this.resetFilters(),void window.history.pushState({},"",window.location.pathname)}else e.classList.add(this.options.activeClass),this.currentFilters.add(t)}filter(){const t=()=>{this.visibleItems.clear(),this.items.forEach((t=>{if(this.currentFilters.has("*"))this.showItem(t),this.visibleItems.add(t);else{const e=new Set(t.dataset.categories?.split(" ")||[]);("OR"===this.options.filterMode?this.matchesAnyFilter(e):this.matchesAllFilters(e))?(this.showItem(t),this.visibleItems.add(t)):this.hideItem(t)}}))};0===this.filterGroups.size?t():(this.visibleItems.clear(),this.items.forEach((t=>{if(this.currentFilters.has("*"))this.showItem(t),this.visibleItems.add(t);else{const e=new Set(t.dataset.categories?.split(" ")||[]);this.matchesFilterGroups(e)?(this.showItem(t),this.visibleItems.add(t)):this.hideItem(t)}}))),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}addFilterGroup(t,e){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"OR";try{if(!t||!Array.isArray(e))return console.warn("Invalid group parameters"),!1;const i=s.toUpperCase();return["AND","OR"].includes(i)||(console.warn('Invalid operator. Using default "OR"'),s="OR"),this.filterGroups.set(t,{filters:new Set(e),operator:i}),this.filterGroups.size>0&&(this.updateFiltersFromGroups(),this.filter(),this.updateURL()),!0}catch(t){return console.error("Error adding filter group:",t),!1}}setGroupMode(t){const e=t.toUpperCase();["AND","OR"].includes(e)&&(this.groupMode=e,this.filterGroups.size>0&&this.filter())}removeFilterGroup(t){return!!this.filterGroups.has(t)&&(this.filterGroups.delete(t),0===this.filterGroups.size?this.resetFilters():this.updateFiltersFromGroups(),this.filter(),this.updateURL(),!0)}updateFiltersFromGroups(){if(0!==this.filterGroups.size){this.currentFilters.has("*")||this.currentFilters.clear();for(const t of this.filterGroups.values())t.filters.forEach((t=>{"*"!==t&&this.currentFilters.add(t)}))}}matchesAnyFilter(t){return[...this.currentFilters].some((e=>{const[s,i]=e.split(":");return t.has(`${s}:${i}`)}))}matchesAllFilters(t){return[...this.currentFilters].every((e=>{const[s,i]=e.split(":");return t.has(`${s}:${i}`)}))}showItem(t){this.visibleItems.add(t),t.classList.remove(this.options.hiddenClass),t.style.opacity="0",t.style.transform="scale(0.95)",t.offsetHeight,requestAnimationFrame((()=>{t.style.opacity="1",t.style.transform="scale(1)"}))}hideItem(t){t.style.opacity="0",t.style.transform="scale(0.95)",setTimeout((()=>{"0"===t.style.opacity&&(t.classList.add(this.options.hiddenClass),this.visibleItems.delete(t))}),this.options.animationDuration)}search(t){this.currentSearch=t.toLowerCase().trim(),this.items.forEach((t=>{const e=this.options.searchKeys.map((e=>t.dataset[e]||"")).join(" ").toLowerCase();""===this.currentSearch||e.includes(this.currentSearch)?this.showItem(t):this.hideItem(t)})),this.updateURL(),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}sortMultiple(t){const e=[...this.items];e.sort(((e,s)=>{for(const i of t){const t=e.dataset[i.key],r=s.dataset[i.key],o="asc"===i.direction?t.localeCompare(r):r.localeCompare(t);if(0!==o)return o}return 0})),e.forEach((t=>this.container.appendChild(t)))}addRangeFilter(t,e,s){this.items.forEach((i=>{const r=parseFloat(i.dataset[t]);r>=e&&r<=s?this.showItem(i):this.hideItem(i)})),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}matchesFilterGroups(t){const e=[...this.filterGroups.values()].map((e=>{const s=[...e.filters];return 0===s.length||("OR"===e.operator?s.some((e=>t.has(e))):s.every((e=>t.has(e))))}));return"OR"===this.groupMode?e.some((t=>t)):e.every((t=>t))}updateURL(){if(0===this.currentFilters.size||1===this.currentFilters.size&&this.currentFilters.has("*"))return void window.history.pushState({},"",window.location.pathname);const t=new URLSearchParams(window.location.search);if(this.filterGroups.size>0){for(const[e,s]of this.filterGroups.entries())t.set(`group_${e}`,[...s.filters].join(",")),t.set(`groupOp_${e}`,s.operator.toLowerCase());t.set("groupMode",this.groupMode.toLowerCase())}const e={};for(const t of this.currentFilters)if("*"!==t){const[s,i]=t.split(":");e[s]||(e[s]=new Set),e[s].add(i)}Object.entries(e).forEach((e=>{let[s,i]=e;t.set(s,Array.from(i).join(","))})),this.currentSearch&&t.set("search",this.currentSearch);const s=`${window.location.pathname}${t.toString()?"?"+t.toString():""}`;window.history.pushState({},"",s)}loadFromURL(){const t=new URLSearchParams(window.location.search);this.filterGroups.clear();for(const[e,s]of t.entries())if(e.startsWith("group_")){const i=e.replace("group_",""),r=t.get(`groupOp_${i}`)?.toUpperCase()||"OR",o=s.split(",");this.addFilterGroup(i,o,r)}const e=t.get("groupMode")?.toUpperCase();e&&["AND","OR"].includes(e)&&(this.groupMode=e),this.currentFilters.clear();let s=!1;for(const[e,i]of t.entries())"search"!==e&&(s=!0,i.split(",").forEach((t=>{this.currentFilters.add(`${e}:${t}`)})));s||this.currentFilters.add("*"),this.filterButtons.forEach((t=>{const e=t.dataset.filter;this.currentFilters.has(e)||"*"===e&&this.currentFilters.has("*")?t.classList.add(this.options.activeClass):t.classList.remove(this.options.activeClass)}));const i=t.get("search")||"";this.searchInput&&(this.searchInput.value=i),this.filter(),i&&this.search(i)}updateCounter(){const t=this.items.length,e=this.visibleItems.size;return this.counter&&(this.counter.textContent=`Showing ${e} of ${t}`),{total:t,visible:e}}setAnimationOptions(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.options.animationDuration=t.duration||this.options.animationDuration,this.options.animationType=t.type||"ease-out",this.addStyles()}addEventSystem(){this.events={},this.on=(t,e)=>{this.events[t]||(this.events[t]=[]),this.events[t].push(e)},this.emit=(t,e)=>{this.events[t]&&this.events[t].forEach((t=>t(e)))}}setPagination(t){this.pagination={currentPage:1,itemsPerPage:t,totalPages:Math.ceil(this.visibleItems.size/t)},this.updatePagination()}updatePagination(){const t=(this.pagination.currentPage-1)*this.pagination.itemsPerPage,e=t+this.pagination.itemsPerPage;[...this.visibleItems].forEach(((s,i)=>{i>=t&&i<e?this.showItem(s):this.hideItem(s)}))}enableAnalytics(t){this.analyticsCallback=t,this.on("filter",(t=>{this.analyticsCallback({type:"filter",filters:[...this.currentFilters],visibleItems:this.visibleItems.size,timestamp:(new Date).toISOString()})}))}sortWithComparator(t,e){const s=[...this.items];s.sort(((s,i)=>{const r=s.dataset[t],o=i.dataset[t];return e(r,o)})),s.forEach((t=>this.container.appendChild(t)))}setResponsiveOptions(t){window.addEventListener("resize",e((()=>{const e=window.innerWidth;for(const[s,i]of Object.entries(t))if(e<=parseInt(s)){Object.assign(this.options,i),this.filter();break}}),250))}enableKeyboardNavigation(){document.addEventListener("keydown",(t=>{"Enter"===t.key&&document.activeElement.classList.contains(this.options.filterButtonSelector.slice(1))&&document.activeElement.click()}))}exportState(){return{filters:[...this.currentFilters],search:this.currentSearch,mode:this.options.filterMode,timestamp:(new Date).toISOString()}}importState(t){t.filters&&(this.currentFilters=new Set(t.filters),this.currentSearch=t.search||"",this.options.filterMode=t.mode||"OR",this.filter(),this.updateURL())}savePreset(t){const e={filters:[...this.currentFilters],search:this.currentSearch,mode:this.options.filterMode};localStorage.setItem(`afs_preset_${t}`,JSON.stringify(e))}loadPreset(t){const e=JSON.parse(localStorage.getItem(`afs_preset_${t}`));e&&(this.currentFilters=new Set(e.filters),this.currentSearch=e.search,this.options.filterMode=e.mode,this.filter(),this.updateURL())}setLogic(t){if("boolean"==typeof t)return this.options.filterMode=t?"AND":"OR",void this.filter();const e=t.toUpperCase();["OR","AND"].includes(e)&&(this.options.filterMode=e,this.filter())}setFilterMode(t){["OR","AND"].includes(t.toUpperCase())&&(this.options.filterMode=t.toUpperCase(),this.filter())}addFilter(t,e){this.currentFilters.add(`${t}:${e}`),this.filter(),this.updateURL()}removeFilter(t,e){this.currentFilters.delete(`${t}:${e}`),0===this.currentFilters.size&&this.currentFilters.add("*"),this.filter(),this.updateURL()}getActiveFiltersByType(t){return[...this.currentFilters].filter((e=>e.startsWith(`${t}:`))).map((t=>t.split(":")[1]))}}}));
|
|
2
2
|
//# sourceMappingURL=AFS.min.js.map
|
package/dist/AFS.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AFS.min.js","sources":["../src/AFS.js"],"sourcesContent":["/**\n * @fileoverview Advanced Filter System for DOM elements\n * @version 1.0.0\n *\n * A flexible and customizable filtering system that supports:\n * - Multiple filtering modes (OR/AND)\n * - Text search with debouncing\n * - Multiple sorting criteria\n * - Range filtering\n * - URL state management\n * - Animation and transitions\n * - Results counter\n */\n\n/**\n * Utility function for debouncing\n * @param {Function} func - Function to debounce\n * @param {number} wait - Delay in milliseconds\n * @returns {Function} Debounced function\n */\nfunction debounce(func, wait) {\n let timeout;\n return function executedFunction(...args) {\n const later = () => {\n clearTimeout(timeout);\n func(...args);\n };\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n}\n\nclass AFS {\n /**\n * @typedef {Object} FilterOptions\n * @property {string} containerSelector - Main container selector\n * @property {string} itemSelector - Items to filter selector\n * @property {string} filterButtonSelector - Filter buttons selector\n * @property {string} [searchInputSelector] - Search input selector\n * @property {string} [counterSelector] - Results counter selector\n * @property {string} [activeClass='active'] - Active state class\n * @property {string} [hiddenClass='hidden'] - Hidden state class\n * @property {number} [animationDuration=300] - Animation duration in ms\n * @property {string} [filterMode='OR'] - Filter mode ('OR' or 'AND')\n * @property {string[]} [searchKeys=['title']] - Data attributes to search in\n * @property {number} [debounceTime=300] - Search debounce delay in ms\n */\n\n /**\n * @param {FilterOptions} options - Filter configuration options\n */\n constructor(options = {}) {\n this.options = {\n containerSelector: \".filter-container\",\n itemSelector: \".filter-item\",\n filterButtonSelector: \".btn-filter\",\n searchInputSelector: \".filter-search\",\n counterSelector: \".filter-counter\",\n activeClass: \"active\",\n hiddenClass: \"hidden\",\n animationDuration: 300,\n filterMode: \"OR\",\n searchKeys: [\"title\"],\n debounceTime: 300,\n ...options,\n };\n\n // Initialize elements\n this.container = document.querySelector(this.options.containerSelector);\n this.items = document.querySelectorAll(this.options.itemSelector);\n this.filterButtons = document.querySelectorAll(\n this.options.filterButtonSelector,\n );\n this.searchInput = document.querySelector(this.options.searchInputSelector);\n this.counter = document.querySelector(this.options.counterSelector);\n\n // Initialize state\n this.currentFilters = new Set([\"*\"]);\n this.currentSearch = \"\";\n this.visibleItems = new Set(this.items);\n this.filterGroups = new Map();\n this.groupMode = \"OR\"; // Default group mode\n\n this.init();\n }\n\n /**\n * Initialize the filter system\n * @private\n */\n init() {\n this.addStyles();\n this.bindEvents();\n this.loadFromURL();\n this.updateCounter();\n }\n\n /**\n * Add required styles to document\n * @private\n */\n addStyles() {\n const styles = `\n .${this.options.hiddenClass} {\n display: none !important;\n }\n\n ${this.options.itemSelector} {\n opacity: 1;\n transform: scale(1);\n transition: opacity ${this.options.animationDuration}ms ease-out,\n transform ${this.options.animationDuration}ms ease-out;\n }\n\n ${this.options.filterButtonSelector} {\n opacity: 0.5;\n transition: opacity ${this.options.animationDuration}ms ease;\n }\n\n ${this.options.filterButtonSelector}.${this.options.activeClass} {\n opacity: 1;\n }\n `;\n\n const styleSheet = document.createElement(\"style\");\n styleSheet.textContent = styles;\n document.head.appendChild(styleSheet);\n }\n\n /**\n * Bind all event listeners\n * @private\n */\n bindEvents() {\n this.filterButtons.forEach((button) => {\n button.addEventListener(\"click\", () => this.handleFilterClick(button));\n });\n\n if (this.searchInput) {\n this.searchInput.addEventListener(\n \"input\",\n debounce((e) => {\n this.search(e.target.value);\n }, this.options.debounceTime),\n );\n }\n\n window.addEventListener(\"popstate\", () => this.loadFromURL());\n }\n\n /**\n * Handle filter button clicks\n * @private\n * @param {HTMLElement} button - Clicked filter button\n */\n handleFilterClick(button) {\n const filterValue = button.dataset.filter;\n\n if (filterValue === \"*\") {\n this.resetFilters();\n } else {\n this.toggleFilter(filterValue, button);\n }\n\n this.filter();\n this.updateURL();\n }\n\n /**\n * Reset all filters to default state\n * @private\n */\n resetFilters() {\n this.filterButtons.forEach((btn) =>\n btn.classList.remove(this.options.activeClass),\n );\n this.currentFilters.clear();\n this.currentFilters.add(\"*\");\n this.filterButtons[0].classList.add(this.options.activeClass);\n this.resetCounter();\n }\n\n /**\n * Reset visible items counter\n * @private\n */\n resetCounter() {\n this.visibleItems = new Set(this.items);\n this.updateCounter();\n }\n\n /**\n * Toggle individual filter state\n * @private\n * @param {string} filterValue - Filter value to toggle\n * @param {HTMLElement} button - Filter button element\n */\n toggleFilter(filterValue, button) {\n this.currentFilters.delete(\"*\");\n this.filterButtons[0].classList.remove(this.options.activeClass);\n\n if (button.classList.contains(this.options.activeClass)) {\n button.classList.remove(this.options.activeClass);\n this.currentFilters.delete(filterValue);\n\n if (this.currentFilters.size === 0) {\n this.resetFilters();\n }\n } else {\n button.classList.add(this.options.activeClass);\n this.currentFilters.add(filterValue);\n }\n }\n\n /**\n * Apply current filters to items\n * @public\n */\n /**\n * Apply current filters to items\n * @public\n */\nfilter() {\n // Store the original filter logic\n const standardFilter = () => {\n this.visibleItems.clear();\n \n this.items.forEach((item) => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n const itemCategories = new Set(\n item.dataset.categories?.split(\" \") || [],\n );\n const matchesFilter =\n this.options.filterMode === \"OR\"\n ? this.matchesAnyFilter(itemCategories)\n : this.matchesAllFilters(itemCategories);\n\n if (matchesFilter) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n this.hideItem(item);\n }\n }\n });\n };\n\n // Check if we should use group filtering or standard filtering\n if (this.filterGroups.size === 0) {\n standardFilter();\n } else {\n this.visibleItems.clear();\n\n this.items.forEach(item => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n const itemCategories = new Set(item.dataset.categories?.split(\" \") || []);\n const matchesGroups = this.matchesFilterGroups(itemCategories);\n\n if (matchesGroups) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n this.hideItem(item);\n }\n }\n });\n }\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\n}\n\n /**\n * Add or update a filter group\n * @public\n * @param {string} groupId - Group identifier\n * @param {string[]} filters - Array of filter values\n * @param {string} [operator='OR'] - Operator within group ('AND' or 'OR')\n * @returns {boolean} Success status\n */\n addFilterGroup(groupId, filters, operator = \"OR\") {\n try {\n // Validate inputs\n if (!groupId || !Array.isArray(filters)) {\n console.warn(\"Invalid group parameters\");\n return false;\n }\n\n const validOperator = operator.toUpperCase();\n if (![\"AND\", \"OR\"].includes(validOperator)) {\n console.warn('Invalid operator. Using default \"OR\"');\n operator = \"OR\";\n }\n\n // Create or update group\n this.filterGroups.set(groupId, {\n filters: new Set(filters),\n operator: validOperator,\n });\n\n // Only update if we have active groups\n if (this.filterGroups.size > 0) {\n this.updateFiltersFromGroups();\n this.filter();\n this.updateURL();\n }\n\n return true;\n } catch (error) {\n console.error(\"Error adding filter group:\", error);\n return false;\n }\n }\n\n /**\n * Set how groups combine with each other\n * @public\n * @param {string} mode - Mode for combining groups ('AND' or 'OR')\n */\n setGroupMode(mode) {\n const validMode = mode.toUpperCase();\n if ([\"AND\", \"OR\"].includes(validMode)) {\n this.groupMode = validMode;\n if (this.filterGroups.size > 0) {\n this.filter();\n }\n }\n }\n\n /**\n * Remove a filter group\n * @public\n * @param {string} groupId - Group identifier\n * @returns {boolean} Success status\n */\n removeFilterGroup(groupId) {\n if (this.filterGroups.has(groupId)) {\n this.filterGroups.delete(groupId);\n\n // If no groups left, revert to normal filtering\n if (this.filterGroups.size === 0) {\n this.resetFilters();\n } else {\n this.updateFiltersFromGroups();\n }\n\n this.filter();\n this.updateURL();\n return true;\n }\n return false;\n }\n\n /**\n * Update filters based on groups\n * @private\n */\n updateFiltersFromGroups() {\n // Only process if we have groups\n if (this.filterGroups.size === 0) return;\n\n // Clear current filters except '*'\n if (!this.currentFilters.has(\"*\")) {\n this.currentFilters.clear();\n }\n\n // Combine all group filters\n for (const group of this.filterGroups.values()) {\n group.filters.forEach((filter) => {\n if (filter !== \"*\") {\n this.currentFilters.add(filter);\n }\n });\n }\n }\n\n /**\n * Check if item matches any active filter (OR mode)\n * @private\n * @param {Set} itemCategories - Item's categories\n * @returns {boolean} Whether item matches any filter\n */\n matchesAnyFilter(itemCategories) {\n return [...this.currentFilters].some((filter) => {\n const [type, value] = filter.split(\":\");\n return itemCategories.has(`${type}:${value}`);\n });\n }\n\n /**\n * Check if item matches all active filters (AND mode)\n * @private\n * @param {Set} itemCategories - Item's categories\n * @returns {boolean} Whether item matches all filters\n */\n matchesAllFilters(itemCategories) {\n return [...this.currentFilters].every((filter) => {\n const [type, value] = filter.split(\":\");\n return itemCategories.has(`${type}:${value}`);\n });\n }\n\n /**\n * Show an item with animation\n * @private\n * @param {HTMLElement} item - Item to show\n */\n showItem(item) {\n this.visibleItems.add(item);\n item.classList.remove(this.options.hiddenClass);\n item.style.opacity = \"0\";\n item.style.transform = \"scale(0.95)\";\n\n item.offsetHeight;\n\n requestAnimationFrame(() => {\n item.style.opacity = \"1\";\n item.style.transform = \"scale(1)\";\n });\n }\n\n /**\n * Hide an item with animation\n * @private\n * @param {HTMLElement} item - Item to hide\n */\n hideItem(item) {\n item.style.opacity = \"0\";\n item.style.transform = \"scale(0.95)\";\n\n setTimeout(() => {\n if (item.style.opacity === \"0\") {\n item.classList.add(this.options.hiddenClass);\n this.visibleItems.delete(item);\n }\n }, this.options.animationDuration);\n }\n\n /**\n * Search items by text\n * @public\n * @param {string} query - Search query\n */\n search(query) {\n this.currentSearch = query.toLowerCase().trim();\n let matches = 0;\n\n this.items.forEach((item) => {\n const searchText = this.options.searchKeys\n .map((key) => item.dataset[key] || \"\")\n .join(\" \")\n .toLowerCase();\n\n const matchesSearch =\n this.currentSearch === \"\" || searchText.includes(this.currentSearch);\n\n if (matchesSearch) {\n this.showItem(item);\n matches++;\n } else {\n this.hideItem(item);\n }\n });\n\n this.updateURL();\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\n }\n\n /**\n * Sort items by multiple criteria\n * @public\n * @param {Array<{key: string, direction: string}>} criteria - Sort criteria\n */\n sortMultiple(criteria) {\n const items = [...this.items];\n\n items.sort((a, b) => {\n for (const criterion of criteria) {\n const valueA = a.dataset[criterion.key];\n const valueB = b.dataset[criterion.key];\n\n const comparison =\n criterion.direction === \"asc\"\n ? valueA.localeCompare(valueB)\n : valueB.localeCompare(valueA);\n\n if (comparison !== 0) return comparison;\n }\n return 0;\n });\n\n items.forEach((item) => this.container.appendChild(item));\n }\n\n /**\n * Filter items by numeric range\n * @public\n * @param {string} key - Data attribute key\n * @param {number} min - Minimum value\n * @param {number} max - Maximum value\n */\n addRangeFilter(key, min, max) {\n this.items.forEach((item) => {\n const value = parseFloat(item.dataset[key]);\n const inRange = value >= min && value <= max;\n\n if (inRange) {\n this.showItem(item);\n } else {\n this.hideItem(item);\n }\n });\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\n }\n\n /**\n * Check if item matches filter groups\n * @private\n * @param {Set} itemCategories - Item's categories\n * @returns {boolean} Whether item matches the group filters\n */\n matchesFilterGroups(itemCategories) {\n const groupMatches = [...this.filterGroups.values()].map((group) => {\n const groupFilters = [...group.filters];\n if (groupFilters.length === 0) return true;\n\n return group.operator === \"OR\"\n ? groupFilters.some((filter) => itemCategories.has(filter))\n : groupFilters.every((filter) => itemCategories.has(filter));\n });\n\n return this.groupMode === \"OR\"\n ? groupMatches.some((matches) => matches)\n : groupMatches.every((matches) => matches);\n }\n\n /**\n * Update URL with current filter state\n * @private\n */\n updateURL() {\n const params = new URLSearchParams(window.location.search);\n\n // Add groups to URL if they exist\n if (this.filterGroups.size > 0) {\n for (const [groupId, group] of this.filterGroups.entries()) {\n params.set(`group_${groupId}`, [...group.filters].join(\",\"));\n params.set(`groupOp_${groupId}`, group.operator.toLowerCase());\n }\n params.set(\"groupMode\", this.groupMode.toLowerCase());\n }\n\n // Séparer les filtres par type\n const filtersByType = {};\n for (const filter of this.currentFilters) {\n if (filter !== \"*\") {\n const [type, value] = filter.split(\":\");\n if (!filtersByType[type]) {\n filtersByType[type] = new Set();\n }\n filtersByType[type].add(value);\n }\n }\n\n // Ajouter chaque type de filtre à l'URL\n Object.entries(filtersByType).forEach(([type, values]) => {\n params.set(type, Array.from(values).join(\",\"));\n });\n\n if (this.currentSearch) {\n params.set(\"search\", this.currentSearch);\n }\n\n const newURL = `${window.location.pathname}${\n params.toString() ? \"?\" + params.toString() : \"\"\n }`;\n window.history.pushState({}, \"\", newURL);\n }\n\n /**\n * Load filter state from URL\n * @private\n */\n loadFromURL() {\n const params = new URLSearchParams(window.location.search);\n\n // Load groups if they exist\n this.filterGroups.clear();\n for (const [key, value] of params.entries()) {\n if (key.startsWith(\"group_\")) {\n const groupId = key.replace(\"group_\", \"\");\n const operator =\n params.get(`groupOp_${groupId}`)?.toUpperCase() || \"OR\";\n const filters = value.split(\",\");\n this.addFilterGroup(groupId, filters, operator);\n }\n }\n\n // Set group mode if present\n const groupMode = params.get(\"groupMode\")?.toUpperCase();\n if (groupMode && [\"AND\", \"OR\"].includes(groupMode)) {\n this.groupMode = groupMode;\n }\n\n this.currentFilters.clear();\n\n // Si aucun filtre n'est présent, utiliser '*'\n let hasFilters = false;\n\n // Parcourir tous les paramètres\n for (const [type, values] of params.entries()) {\n if (type !== \"search\") {\n hasFilters = true;\n values.split(\",\").forEach((value) => {\n this.currentFilters.add(`${type}:${value}`);\n });\n }\n }\n\n if (!hasFilters) {\n this.currentFilters.add(\"*\");\n }\n\n // Update active buttons\n this.filterButtons.forEach((button) => {\n const filterValue = button.dataset.filter;\n if (\n this.currentFilters.has(filterValue) ||\n (filterValue === \"*\" && this.currentFilters.has(\"*\"))\n ) {\n button.classList.add(this.options.activeClass);\n } else {\n button.classList.remove(this.options.activeClass);\n }\n });\n\n // Load search\n const search = params.get(\"search\") || \"\";\n if (this.searchInput) {\n this.searchInput.value = search;\n }\n\n this.filter();\n if (search) {\n this.search(search);\n }\n }\n\n /**\n * Update results counter\n * @private\n * @returns {{total: number, visible: number}}\n */\n updateCounter() {\n const total = this.items.length;\n const visible = this.visibleItems.size;\n\n if (this.counter) {\n this.counter.textContent = `Showing ${visible} of ${total}`;\n }\n\n return { total, visible };\n }\n\n /**\n * Set animation options\n * @public\n * @param {Object} options - Animation options\n */\n setAnimationOptions(options = {}) {\n this.options.animationDuration =\n options.duration || this.options.animationDuration;\n this.options.animationType = options.type || \"ease-out\";\n this.addStyles(); // Refresh styles with new options\n }\n\n /**\n * Event handling system\n */\n addEventSystem() {\n this.events = {};\n\n this.on = (eventName, callback) => {\n if (!this.events[eventName]) {\n this.events[eventName] = [];\n }\n this.events[eventName].push(callback);\n };\n\n this.emit = (eventName, data) => {\n if (this.events[eventName]) {\n this.events[eventName].forEach((callback) => callback(data));\n }\n };\n }\n\n /**\n * Add pagination\n * @public\n * @param {number} itemsPerPage - Number of items per page\n */\n setPagination(itemsPerPage) {\n this.pagination = {\n currentPage: 1,\n itemsPerPage: itemsPerPage,\n totalPages: Math.ceil(this.visibleItems.size / itemsPerPage),\n };\n this.updatePagination();\n }\n\n updatePagination() {\n const start =\n (this.pagination.currentPage - 1) * this.pagination.itemsPerPage;\n const end = start + this.pagination.itemsPerPage;\n\n [...this.visibleItems].forEach((item, index) => {\n if (index >= start && index < end) {\n this.showItem(item);\n } else {\n this.hideItem(item);\n }\n });\n }\n\n /**\n * Enable analytics tracking\n * @public\n * @param {Function} callback - Analytics callback function\n */\n enableAnalytics(callback) {\n this.analyticsCallback = callback;\n this.on(\"filter\", (data) => {\n this.analyticsCallback({\n type: \"filter\",\n filters: [...this.currentFilters],\n visibleItems: this.visibleItems.size,\n timestamp: new Date().toISOString(),\n });\n });\n }\n\n /**\n * Sort with custom comparator\n * @public\n * @param {string} key - Data attribute key\n * @param {Function} comparator - Custom comparison function\n */\n sortWithComparator(key, comparator) {\n const items = [...this.items];\n items.sort((a, b) => {\n const valueA = a.dataset[key];\n const valueB = b.dataset[key];\n return comparator(valueA, valueB);\n });\n items.forEach((item) => this.container.appendChild(item));\n }\n\n /**\n * Add responsive behavior\n * @public\n * @param {Object} breakpoints - Breakpoint configurations\n */\n setResponsiveOptions(breakpoints) {\n window.addEventListener(\n \"resize\",\n debounce(() => {\n const width = window.innerWidth;\n for (const [breakpoint, options] of Object.entries(breakpoints)) {\n if (width <= parseInt(breakpoint)) {\n Object.assign(this.options, options);\n this.filter();\n break;\n }\n }\n }, 250),\n );\n }\n\n /**\n * Enable keyboard navigation\n * @public\n */\n enableKeyboardNavigation() {\n document.addEventListener(\"keydown\", (e) => {\n if (\n e.key === \"Enter\" &&\n document.activeElement.classList.contains(\n this.options.filterButtonSelector.slice(1),\n )\n ) {\n document.activeElement.click();\n }\n });\n }\n\n /**\n * Export current filter state\n * @public\n * @returns {Object} Filter state\n */\n exportState() {\n return {\n filters: [...this.currentFilters],\n search: this.currentSearch,\n mode: this.options.filterMode,\n timestamp: new Date().toISOString(),\n };\n }\n\n /**\n * Import filter state\n * @public\n * @param {Object} state - Filter state to import\n */\n importState(state) {\n if (state.filters) {\n this.currentFilters = new Set(state.filters);\n this.currentSearch = state.search || \"\";\n this.options.filterMode = state.mode || \"OR\";\n this.filter();\n this.updateURL();\n }\n }\n\n /**\n * Save current filter state as preset\n * @public\n * @param {string} presetName - Name of the preset\n */\n savePreset(presetName) {\n const preset = {\n filters: [...this.currentFilters],\n search: this.currentSearch,\n mode: this.options.filterMode,\n };\n localStorage.setItem(`afs_preset_${presetName}`, JSON.stringify(preset));\n }\n\n /**\n * Load filter preset\n * @public\n * @param {string} presetName - Name of the preset to load\n */\n loadPreset(presetName) {\n const preset = JSON.parse(localStorage.getItem(`afs_preset_${presetName}`));\n if (preset) {\n this.currentFilters = new Set(preset.filters);\n this.currentSearch = preset.search;\n this.options.filterMode = preset.mode;\n this.filter();\n this.updateURL();\n }\n }\n\n /**\n * Set filter logic mode (alias for setFilterMode)\n * @public\n * @param {string} logic - New filter logic ('AND' or 'OR')\n */\n setLogic(logic) {\n if (typeof logic === \"boolean\") {\n // Handle boolean input (true = AND, false = OR)\n this.options.filterMode = logic ? \"AND\" : \"OR\";\n this.filter();\n return;\n }\n\n const mode = logic.toUpperCase();\n if ([\"OR\", \"AND\"].includes(mode)) {\n this.options.filterMode = mode;\n this.filter();\n }\n }\n\n /**\n * Change filter mode\n * @public\n * @param {string} mode - New filter mode ('OR' or 'AND')\n */\n setFilterMode(mode) {\n if ([\"OR\", \"AND\"].includes(mode.toUpperCase())) {\n this.options.filterMode = mode.toUpperCase();\n this.filter();\n }\n }\n\n /**\n * Add filter by type and value\n * @public\n * @param {string} type - Filter type\n * @param {string} value - Filter value\n */\n addFilter(type, value) {\n this.currentFilters.add(`${type}:${value}`);\n this.filter();\n this.updateURL();\n }\n\n /**\n * Remove filter by type and value\n * @public\n * @param {string} type - Filter type\n */\n removeFilter(type, value) {\n this.currentFilters.delete(`${type}:${value}`);\n if (this.currentFilters.size === 0) {\n this.currentFilters.add(\"*\");\n }\n this.filter();\n this.updateURL();\n }\n\n /**\n * Get active filters by type\n * @public\n * @param {string} type - Filter type\n */\n getActiveFiltersByType(type) {\n return [...this.currentFilters]\n .filter((filter) => filter.startsWith(`${type}:`))\n .map((filter) => filter.split(\":\")[1]);\n }\n}\n\nexport { AFS };\n"],"names":["debounce","func","wait","timeout","_len","arguments","length","args","Array","_key","clearTimeout","setTimeout","later","constructor","options","undefined","this","containerSelector","itemSelector","filterButtonSelector","searchInputSelector","counterSelector","activeClass","hiddenClass","animationDuration","filterMode","searchKeys","debounceTime","container","document","querySelector","items","querySelectorAll","filterButtons","searchInput","counter","currentFilters","Set","currentSearch","visibleItems","filterGroups","Map","groupMode","init","addStyles","bindEvents","loadFromURL","updateCounter","styles","styleSheet","createElement","textContent","head","appendChild","forEach","button","addEventListener","handleFilterClick","e","search","target","value","window","filterValue","dataset","filter","resetFilters","toggleFilter","updateURL","btn","classList","remove","clear","add","resetCounter","delete","contains","size","standardFilter","item","has","showItem","itemCategories","categories","split","matchesAnyFilter","matchesAllFilters","hideItem","matchesFilterGroups","addFilterGroup","groupId","filters","operator","isArray","console","warn","validOperator","toUpperCase","includes","set","updateFiltersFromGroups","error","setGroupMode","mode","validMode","removeFilterGroup","group","values","some","type","every","style","opacity","transform","offsetHeight","requestAnimationFrame","query","toLowerCase","trim","searchText","map","key","join","sortMultiple","criteria","sort","a","b","criterion","valueA","valueB","comparison","direction","localeCompare","addRangeFilter","min","max","parseFloat","groupMatches","groupFilters","matches","params","URLSearchParams","location","entries","filtersByType","Object","_ref","from","newURL","pathname","toString","history","pushState","startsWith","replace","get","hasFilters","total","visible","setAnimationOptions","duration","animationType","addEventSystem","events","on","eventName","callback","push","emit","data","setPagination","itemsPerPage","pagination","currentPage","totalPages","Math","ceil","updatePagination","start","end","index","enableAnalytics","analyticsCallback","timestamp","Date","toISOString","sortWithComparator","comparator","setResponsiveOptions","breakpoints","width","innerWidth","breakpoint","parseInt","assign","enableKeyboardNavigation","activeElement","slice","click","exportState","importState","state","savePreset","presetName","preset","localStorage","setItem","JSON","stringify","loadPreset","parse","getItem","setLogic","logic","setFilterMode","addFilter","removeFilter","getActiveFiltersByType"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AFS.min.js","sources":["../src/AFS.js"],"sourcesContent":["/**\n * @fileoverview Advanced Filter System for DOM elements\n * @version 1.0.5\n *\n * A flexible and customizable filtering system that supports:\n * - Multiple filtering modes (OR/AND)\n * - Text search with debouncing\n * - Multiple sorting criteria\n * - Range filtering\n * - URL state management\n * - Animation and transitions\n * - Results counter\n */\n\n/**\n * Utility function for debouncing\n * @param {Function} func - Function to debounce\n * @param {number} wait - Delay in milliseconds\n * @returns {Function} Debounced function\n */\nfunction debounce(func, wait) {\n let timeout;\n return function executedFunction(...args) {\n const later = () => {\n clearTimeout(timeout);\n func(...args);\n };\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n}\n\nclass AFS {\n /**\n * @typedef {Object} FilterOptions\n * @property {string} containerSelector - Main container selector\n * @property {string} itemSelector - Items to filter selector\n * @property {string} filterButtonSelector - Filter buttons selector\n * @property {string} [searchInputSelector] - Search input selector\n * @property {string} [counterSelector] - Results counter selector\n * @property {string} [activeClass='active'] - Active state class\n * @property {string} [hiddenClass='hidden'] - Hidden state class\n * @property {number} [animationDuration=300] - Animation duration in ms\n * @property {string} [filterMode='OR'] - Filter mode ('OR' or 'AND')\n * @property {string[]} [searchKeys=['title']] - Data attributes to search in\n * @property {number} [debounceTime=300] - Search debounce delay in ms\n */\n\n /**\n * @param {FilterOptions} options - Filter configuration options\n */\n constructor(options = {}) {\n this.options = {\n containerSelector: \".filter-container\",\n itemSelector: \".filter-item\",\n filterButtonSelector: \".btn-filter\",\n searchInputSelector: \".filter-search\",\n counterSelector: \".filter-counter\",\n activeClass: \"active\",\n hiddenClass: \"hidden\",\n animationDuration: 300,\n filterMode: \"OR\",\n searchKeys: [\"title\"],\n debounceTime: 300,\n ...options,\n };\n\n // Initialize elements\n this.container = document.querySelector(this.options.containerSelector);\n this.items = document.querySelectorAll(this.options.itemSelector);\n this.filterButtons = document.querySelectorAll(\n this.options.filterButtonSelector,\n );\n this.searchInput = document.querySelector(this.options.searchInputSelector);\n this.counter = document.querySelector(this.options.counterSelector);\n\n // Initialize state\n this.currentFilters = new Set([\"*\"]);\n this.currentSearch = \"\";\n this.visibleItems = new Set(this.items);\n this.filterGroups = new Map();\n this.groupMode = \"OR\"; // Default group mode\n\n this.init();\n }\n\n /**\n * Initialize the filter system\n * @private\n */\n init() {\n this.addStyles();\n this.bindEvents();\n this.loadFromURL();\n this.updateCounter();\n }\n\n /**\n * Add required styles to document\n * @private\n */\n addStyles() {\n const styles = `\n .${this.options.hiddenClass} {\n display: none !important;\n }\n\n ${this.options.itemSelector} {\n opacity: 1;\n transform: scale(1);\n transition: opacity ${this.options.animationDuration}ms ease-out,\n transform ${this.options.animationDuration}ms ease-out;\n }\n\n ${this.options.filterButtonSelector} {\n opacity: 0.5;\n transition: opacity ${this.options.animationDuration}ms ease;\n }\n\n ${this.options.filterButtonSelector}.${this.options.activeClass} {\n opacity: 1;\n }\n `;\n\n const styleSheet = document.createElement(\"style\");\n styleSheet.textContent = styles;\n document.head.appendChild(styleSheet);\n }\n\n /**\n * Bind all event listeners\n * @private\n */\n bindEvents() {\n this.filterButtons.forEach((button) => {\n button.addEventListener(\"click\", () => this.handleFilterClick(button));\n });\n\n if (this.searchInput) {\n this.searchInput.addEventListener(\n \"input\",\n debounce((e) => {\n this.search(e.target.value);\n }, this.options.debounceTime),\n );\n }\n\n window.addEventListener(\"popstate\", () => this.loadFromURL());\n }\n\n /**\n * Handle filter button clicks\n * @private\n * @param {HTMLElement} button - Clicked filter button\n */\n handleFilterClick(button) {\n const filterValue = button.dataset.filter;\n\n if (filterValue === \"*\") {\n this.resetFilters();\n } else {\n this.toggleFilter(filterValue, button);\n }\n\n this.filter();\n this.updateURL();\n }\n\n /**\n * Reset all filters to default state\n * @private\n */\n resetFilters() {\n this.filterButtons.forEach((btn) =>\n btn.classList.remove(this.options.activeClass),\n );\n this.currentFilters.clear();\n this.currentFilters.add(\"*\");\n this.filterButtons[0].classList.add(this.options.activeClass);\n this.resetCounter();\n }\n\n /**\n * Reset visible items counter\n * @private\n */\n resetCounter() {\n this.visibleItems = new Set(this.items);\n this.updateCounter();\n }\n\n /**\n * Toggle individual filter state\n * @private\n * @param {string} filterValue - Filter value to toggle\n * @param {HTMLElement} button - Filter button element\n */\n toggleFilter(filterValue, button) {\n this.currentFilters.delete(\"*\");\n this.filterButtons[0].classList.remove(this.options.activeClass);\n\n if (button.classList.contains(this.options.activeClass)) {\n button.classList.remove(this.options.activeClass);\n this.currentFilters.delete(filterValue);\n\n // If no filters are selected, reset to default state and clear URL\n if (this.currentFilters.size === 0) {\n this.resetFilters();\n window.history.pushState({}, \"\", window.location.pathname);\n return;\n }\n } else {\n button.classList.add(this.options.activeClass);\n this.currentFilters.add(filterValue);\n }\n }\n\n /**\n * Apply current filters to items\n * @public\n */\n /**\n * Apply current filters to items\n * @public\n */\n filter() {\n // Store the original filter logic\n const standardFilter = () => {\n this.visibleItems.clear();\n\n this.items.forEach((item) => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n const itemCategories = new Set(\n item.dataset.categories?.split(\" \") || [],\n );\n const matchesFilter =\n this.options.filterMode === \"OR\"\n ? this.matchesAnyFilter(itemCategories)\n : this.matchesAllFilters(itemCategories);\n\n if (matchesFilter) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n this.hideItem(item);\n }\n }\n });\n };\n\n // Check if we should use group filtering or standard filtering\n if (this.filterGroups.size === 0) {\n standardFilter();\n } else {\n this.visibleItems.clear();\n\n this.items.forEach((item) => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n const itemCategories = new Set(\n item.dataset.categories?.split(\" \") || [],\n );\n const matchesGroups = this.matchesFilterGroups(itemCategories);\n\n if (matchesGroups) {\n this.showItem(item);\n this.visibleItems.add(item);\n } else {\n this.hideItem(item);\n }\n }\n });\n }\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\n }\n\n /**\n * Add or update a filter group\n * @public\n * @param {string} groupId - Group identifier\n * @param {string[]} filters - Array of filter values\n * @param {string} [operator='OR'] - Operator within group ('AND' or 'OR')\n * @returns {boolean} Success status\n */\n addFilterGroup(groupId, filters, operator = \"OR\") {\n try {\n // Validate inputs\n if (!groupId || !Array.isArray(filters)) {\n console.warn(\"Invalid group parameters\");\n return false;\n }\n\n const validOperator = operator.toUpperCase();\n if (![\"AND\", \"OR\"].includes(validOperator)) {\n console.warn('Invalid operator. Using default \"OR\"');\n operator = \"OR\";\n }\n\n // Create or update group\n this.filterGroups.set(groupId, {\n filters: new Set(filters),\n operator: validOperator,\n });\n\n // Only update if we have active groups\n if (this.filterGroups.size > 0) {\n this.updateFiltersFromGroups();\n this.filter();\n this.updateURL();\n }\n\n return true;\n } catch (error) {\n console.error(\"Error adding filter group:\", error);\n return false;\n }\n }\n\n /**\n * Set how groups combine with each other\n * @public\n * @param {string} mode - Mode for combining groups ('AND' or 'OR')\n */\n setGroupMode(mode) {\n const validMode = mode.toUpperCase();\n if ([\"AND\", \"OR\"].includes(validMode)) {\n this.groupMode = validMode;\n if (this.filterGroups.size > 0) {\n this.filter();\n }\n }\n }\n\n /**\n * Remove a filter group\n * @public\n * @param {string} groupId - Group identifier\n * @returns {boolean} Success status\n */\n removeFilterGroup(groupId) {\n if (this.filterGroups.has(groupId)) {\n this.filterGroups.delete(groupId);\n\n // If no groups left, revert to normal filtering\n if (this.filterGroups.size === 0) {\n this.resetFilters();\n } else {\n this.updateFiltersFromGroups();\n }\n\n this.filter();\n this.updateURL();\n return true;\n }\n return false;\n }\n\n /**\n * Update filters based on groups\n * @private\n */\n updateFiltersFromGroups() {\n // Only process if we have groups\n if (this.filterGroups.size === 0) return;\n\n // Clear current filters except '*'\n if (!this.currentFilters.has(\"*\")) {\n this.currentFilters.clear();\n }\n\n // Combine all group filters\n for (const group of this.filterGroups.values()) {\n group.filters.forEach((filter) => {\n if (filter !== \"*\") {\n this.currentFilters.add(filter);\n }\n });\n }\n }\n\n /**\n * Check if item matches any active filter (OR mode)\n * @private\n * @param {Set} itemCategories - Item's categories\n * @returns {boolean} Whether item matches any filter\n */\n matchesAnyFilter(itemCategories) {\n return [...this.currentFilters].some((filter) => {\n const [type, value] = filter.split(\":\");\n return itemCategories.has(`${type}:${value}`);\n });\n }\n\n /**\n * Check if item matches all active filters (AND mode)\n * @private\n * @param {Set} itemCategories - Item's categories\n * @returns {boolean} Whether item matches all filters\n */\n matchesAllFilters(itemCategories) {\n return [...this.currentFilters].every((filter) => {\n const [type, value] = filter.split(\":\");\n return itemCategories.has(`${type}:${value}`);\n });\n }\n\n /**\n * Show an item with animation\n * @private\n * @param {HTMLElement} item - Item to show\n */\n showItem(item) {\n this.visibleItems.add(item);\n item.classList.remove(this.options.hiddenClass);\n item.style.opacity = \"0\";\n item.style.transform = \"scale(0.95)\";\n\n item.offsetHeight;\n\n requestAnimationFrame(() => {\n item.style.opacity = \"1\";\n item.style.transform = \"scale(1)\";\n });\n }\n\n /**\n * Hide an item with animation\n * @private\n * @param {HTMLElement} item - Item to hide\n */\n hideItem(item) {\n item.style.opacity = \"0\";\n item.style.transform = \"scale(0.95)\";\n\n setTimeout(() => {\n if (item.style.opacity === \"0\") {\n item.classList.add(this.options.hiddenClass);\n this.visibleItems.delete(item);\n }\n }, this.options.animationDuration);\n }\n\n /**\n * Search items by text\n * @public\n * @param {string} query - Search query\n */\n search(query) {\n this.currentSearch = query.toLowerCase().trim();\n let matches = 0;\n\n this.items.forEach((item) => {\n const searchText = this.options.searchKeys\n .map((key) => item.dataset[key] || \"\")\n .join(\" \")\n .toLowerCase();\n\n const matchesSearch =\n this.currentSearch === \"\" || searchText.includes(this.currentSearch);\n\n if (matchesSearch) {\n this.showItem(item);\n matches++;\n } else {\n this.hideItem(item);\n }\n });\n\n this.updateURL();\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\n }\n\n /**\n * Sort items by multiple criteria\n * @public\n * @param {Array<{key: string, direction: string}>} criteria - Sort criteria\n */\n sortMultiple(criteria) {\n const items = [...this.items];\n\n items.sort((a, b) => {\n for (const criterion of criteria) {\n const valueA = a.dataset[criterion.key];\n const valueB = b.dataset[criterion.key];\n\n const comparison =\n criterion.direction === \"asc\"\n ? valueA.localeCompare(valueB)\n : valueB.localeCompare(valueA);\n\n if (comparison !== 0) return comparison;\n }\n return 0;\n });\n\n items.forEach((item) => this.container.appendChild(item));\n }\n\n /**\n * Filter items by numeric range\n * @public\n * @param {string} key - Data attribute key\n * @param {number} min - Minimum value\n * @param {number} max - Maximum value\n */\n addRangeFilter(key, min, max) {\n this.items.forEach((item) => {\n const value = parseFloat(item.dataset[key]);\n const inRange = value >= min && value <= max;\n\n if (inRange) {\n this.showItem(item);\n } else {\n this.hideItem(item);\n }\n });\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\n }\n\n /**\n * Check if item matches filter groups\n * @private\n * @param {Set} itemCategories - Item's categories\n * @returns {boolean} Whether item matches the group filters\n */\n matchesFilterGroups(itemCategories) {\n const groupMatches = [...this.filterGroups.values()].map((group) => {\n const groupFilters = [...group.filters];\n if (groupFilters.length === 0) return true;\n\n return group.operator === \"OR\"\n ? groupFilters.some((filter) => itemCategories.has(filter))\n : groupFilters.every((filter) => itemCategories.has(filter));\n });\n\n return this.groupMode === \"OR\"\n ? groupMatches.some((matches) => matches)\n : groupMatches.every((matches) => matches);\n }\n\n /**\n * Update URL with current filter state\n * @private\n */\n updateURL() {\n // If only \"*\" filter is active or no filters are active, clear the URL\n if (\n this.currentFilters.size === 0 ||\n (this.currentFilters.size === 1 && this.currentFilters.has(\"*\"))\n ) {\n window.history.pushState({}, \"\", window.location.pathname);\n return;\n }\n\n const params = new URLSearchParams(window.location.search);\n\n // Add groups to URL if they exist\n if (this.filterGroups.size > 0) {\n for (const [groupId, group] of this.filterGroups.entries()) {\n params.set(`group_${groupId}`, [...group.filters].join(\",\"));\n params.set(`groupOp_${groupId}`, group.operator.toLowerCase());\n }\n params.set(\"groupMode\", this.groupMode.toLowerCase());\n }\n\n // Separate filters by type\n const filtersByType = {};\n for (const filter of this.currentFilters) {\n if (filter !== \"*\") {\n const [type, value] = filter.split(\":\");\n if (!filtersByType[type]) {\n filtersByType[type] = new Set();\n }\n filtersByType[type].add(value);\n }\n }\n\n // Add each filter type to the URL\n Object.entries(filtersByType).forEach(([type, values]) => {\n params.set(type, Array.from(values).join(\",\"));\n });\n\n if (this.currentSearch) {\n params.set(\"search\", this.currentSearch);\n }\n\n const newURL = `${window.location.pathname}${\n params.toString() ? \"?\" + params.toString() : \"\"\n }`;\n window.history.pushState({}, \"\", newURL);\n }\n\n /**\n * Load filter state from URL\n * @private\n */\n loadFromURL() {\n const params = new URLSearchParams(window.location.search);\n\n // Load groups if they exist\n this.filterGroups.clear();\n for (const [key, value] of params.entries()) {\n if (key.startsWith(\"group_\")) {\n const groupId = key.replace(\"group_\", \"\");\n const operator =\n params.get(`groupOp_${groupId}`)?.toUpperCase() || \"OR\";\n const filters = value.split(\",\");\n this.addFilterGroup(groupId, filters, operator);\n }\n }\n\n // Set group mode if present\n const groupMode = params.get(\"groupMode\")?.toUpperCase();\n if (groupMode && [\"AND\", \"OR\"].includes(groupMode)) {\n this.groupMode = groupMode;\n }\n\n this.currentFilters.clear();\n\n // Si aucun filtre n'est présent, utiliser '*'\n let hasFilters = false;\n\n // Parcourir tous les paramètres\n for (const [type, values] of params.entries()) {\n if (type !== \"search\") {\n hasFilters = true;\n values.split(\",\").forEach((value) => {\n this.currentFilters.add(`${type}:${value}`);\n });\n }\n }\n\n if (!hasFilters) {\n this.currentFilters.add(\"*\");\n }\n\n // Update active buttons\n this.filterButtons.forEach((button) => {\n const filterValue = button.dataset.filter;\n if (\n this.currentFilters.has(filterValue) ||\n (filterValue === \"*\" && this.currentFilters.has(\"*\"))\n ) {\n button.classList.add(this.options.activeClass);\n } else {\n button.classList.remove(this.options.activeClass);\n }\n });\n\n // Load search\n const search = params.get(\"search\") || \"\";\n if (this.searchInput) {\n this.searchInput.value = search;\n }\n\n this.filter();\n if (search) {\n this.search(search);\n }\n }\n\n /**\n * Update results counter\n * @private\n * @returns {{total: number, visible: number}}\n */\n updateCounter() {\n const total = this.items.length;\n const visible = this.visibleItems.size;\n\n if (this.counter) {\n this.counter.textContent = `Showing ${visible} of ${total}`;\n }\n\n return { total, visible };\n }\n\n /**\n * Set animation options\n * @public\n * @param {Object} options - Animation options\n */\n setAnimationOptions(options = {}) {\n this.options.animationDuration =\n options.duration || this.options.animationDuration;\n this.options.animationType = options.type || \"ease-out\";\n this.addStyles(); // Refresh styles with new options\n }\n\n /**\n * Event handling system\n */\n addEventSystem() {\n this.events = {};\n\n this.on = (eventName, callback) => {\n if (!this.events[eventName]) {\n this.events[eventName] = [];\n }\n this.events[eventName].push(callback);\n };\n\n this.emit = (eventName, data) => {\n if (this.events[eventName]) {\n this.events[eventName].forEach((callback) => callback(data));\n }\n };\n }\n\n /**\n * Add pagination\n * @public\n * @param {number} itemsPerPage - Number of items per page\n */\n setPagination(itemsPerPage) {\n this.pagination = {\n currentPage: 1,\n itemsPerPage: itemsPerPage,\n totalPages: Math.ceil(this.visibleItems.size / itemsPerPage),\n };\n this.updatePagination();\n }\n\n updatePagination() {\n const start =\n (this.pagination.currentPage - 1) * this.pagination.itemsPerPage;\n const end = start + this.pagination.itemsPerPage;\n\n [...this.visibleItems].forEach((item, index) => {\n if (index >= start && index < end) {\n this.showItem(item);\n } else {\n this.hideItem(item);\n }\n });\n }\n\n /**\n * Enable analytics tracking\n * @public\n * @param {Function} callback - Analytics callback function\n */\n enableAnalytics(callback) {\n this.analyticsCallback = callback;\n this.on(\"filter\", (data) => {\n this.analyticsCallback({\n type: \"filter\",\n filters: [...this.currentFilters],\n visibleItems: this.visibleItems.size,\n timestamp: new Date().toISOString(),\n });\n });\n }\n\n /**\n * Sort with custom comparator\n * @public\n * @param {string} key - Data attribute key\n * @param {Function} comparator - Custom comparison function\n */\n sortWithComparator(key, comparator) {\n const items = [...this.items];\n items.sort((a, b) => {\n const valueA = a.dataset[key];\n const valueB = b.dataset[key];\n return comparator(valueA, valueB);\n });\n items.forEach((item) => this.container.appendChild(item));\n }\n\n /**\n * Add responsive behavior\n * @public\n * @param {Object} breakpoints - Breakpoint configurations\n */\n setResponsiveOptions(breakpoints) {\n window.addEventListener(\n \"resize\",\n debounce(() => {\n const width = window.innerWidth;\n for (const [breakpoint, options] of Object.entries(breakpoints)) {\n if (width <= parseInt(breakpoint)) {\n Object.assign(this.options, options);\n this.filter();\n break;\n }\n }\n }, 250),\n );\n }\n\n /**\n * Enable keyboard navigation\n * @public\n */\n enableKeyboardNavigation() {\n document.addEventListener(\"keydown\", (e) => {\n if (\n e.key === \"Enter\" &&\n document.activeElement.classList.contains(\n this.options.filterButtonSelector.slice(1),\n )\n ) {\n document.activeElement.click();\n }\n });\n }\n\n /**\n * Export current filter state\n * @public\n * @returns {Object} Filter state\n */\n exportState() {\n return {\n filters: [...this.currentFilters],\n search: this.currentSearch,\n mode: this.options.filterMode,\n timestamp: new Date().toISOString(),\n };\n }\n\n /**\n * Import filter state\n * @public\n * @param {Object} state - Filter state to import\n */\n importState(state) {\n if (state.filters) {\n this.currentFilters = new Set(state.filters);\n this.currentSearch = state.search || \"\";\n this.options.filterMode = state.mode || \"OR\";\n this.filter();\n this.updateURL();\n }\n }\n\n /**\n * Save current filter state as preset\n * @public\n * @param {string} presetName - Name of the preset\n */\n savePreset(presetName) {\n const preset = {\n filters: [...this.currentFilters],\n search: this.currentSearch,\n mode: this.options.filterMode,\n };\n localStorage.setItem(`afs_preset_${presetName}`, JSON.stringify(preset));\n }\n\n /**\n * Load filter preset\n * @public\n * @param {string} presetName - Name of the preset to load\n */\n loadPreset(presetName) {\n const preset = JSON.parse(localStorage.getItem(`afs_preset_${presetName}`));\n if (preset) {\n this.currentFilters = new Set(preset.filters);\n this.currentSearch = preset.search;\n this.options.filterMode = preset.mode;\n this.filter();\n this.updateURL();\n }\n }\n\n /**\n * Set filter logic mode (alias for setFilterMode)\n * @public\n * @param {string} logic - New filter logic ('AND' or 'OR')\n */\n setLogic(logic) {\n if (typeof logic === \"boolean\") {\n // Handle boolean input (true = AND, false = OR)\n this.options.filterMode = logic ? \"AND\" : \"OR\";\n this.filter();\n return;\n }\n\n const mode = logic.toUpperCase();\n if ([\"OR\", \"AND\"].includes(mode)) {\n this.options.filterMode = mode;\n this.filter();\n }\n }\n\n /**\n * Change filter mode\n * @public\n * @param {string} mode - New filter mode ('OR' or 'AND')\n */\n setFilterMode(mode) {\n if ([\"OR\", \"AND\"].includes(mode.toUpperCase())) {\n this.options.filterMode = mode.toUpperCase();\n this.filter();\n }\n }\n\n /**\n * Add filter by type and value\n * @public\n * @param {string} type - Filter type\n * @param {string} value - Filter value\n */\n addFilter(type, value) {\n this.currentFilters.add(`${type}:${value}`);\n this.filter();\n this.updateURL();\n }\n\n /**\n * Remove filter by type and value\n * @public\n * @param {string} type - Filter type\n */\n removeFilter(type, value) {\n this.currentFilters.delete(`${type}:${value}`);\n if (this.currentFilters.size === 0) {\n this.currentFilters.add(\"*\");\n }\n this.filter();\n this.updateURL();\n }\n\n /**\n * Get active filters by type\n * @public\n * @param {string} type - Filter type\n */\n getActiveFiltersByType(type) {\n return [...this.currentFilters]\n .filter((filter) => filter.startsWith(`${type}:`))\n .map((filter) => filter.split(\":\")[1]);\n }\n}\n\nexport { AFS };\n"],"names":["debounce","func","wait","timeout","_len","arguments","length","args","Array","_key","clearTimeout","setTimeout","later","constructor","options","undefined","this","containerSelector","itemSelector","filterButtonSelector","searchInputSelector","counterSelector","activeClass","hiddenClass","animationDuration","filterMode","searchKeys","debounceTime","container","document","querySelector","items","querySelectorAll","filterButtons","searchInput","counter","currentFilters","Set","currentSearch","visibleItems","filterGroups","Map","groupMode","init","addStyles","bindEvents","loadFromURL","updateCounter","styles","styleSheet","createElement","textContent","head","appendChild","forEach","button","addEventListener","handleFilterClick","e","search","target","value","window","filterValue","dataset","filter","resetFilters","toggleFilter","updateURL","btn","classList","remove","clear","add","resetCounter","delete","contains","size","history","pushState","location","pathname","standardFilter","item","has","showItem","itemCategories","categories","split","matchesAnyFilter","matchesAllFilters","hideItem","matchesFilterGroups","addFilterGroup","groupId","filters","operator","isArray","console","warn","validOperator","toUpperCase","includes","set","updateFiltersFromGroups","error","setGroupMode","mode","validMode","removeFilterGroup","group","values","some","type","every","style","opacity","transform","offsetHeight","requestAnimationFrame","query","toLowerCase","trim","searchText","map","key","join","sortMultiple","criteria","sort","a","b","criterion","valueA","valueB","comparison","direction","localeCompare","addRangeFilter","min","max","parseFloat","groupMatches","groupFilters","matches","params","URLSearchParams","entries","filtersByType","Object","_ref","from","newURL","toString","startsWith","replace","get","hasFilters","total","visible","setAnimationOptions","duration","animationType","addEventSystem","events","on","eventName","callback","push","emit","data","setPagination","itemsPerPage","pagination","currentPage","totalPages","Math","ceil","updatePagination","start","end","index","enableAnalytics","analyticsCallback","timestamp","Date","toISOString","sortWithComparator","comparator","setResponsiveOptions","breakpoints","width","innerWidth","breakpoint","parseInt","assign","enableKeyboardNavigation","activeElement","slice","click","exportState","importState","state","savePreset","presetName","preset","localStorage","setItem","JSON","stringify","loadPreset","parse","getItem","setLogic","logic","setFilterMode","addFilter","removeFilter","getActiveFiltersByType"],"mappings":"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"}
|