advanced-filter-system 1.0.1 → 1.0.3

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/dist/AFS.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"AFS.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(\n this.options.searchInputSelector,\n );\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\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\", () =>\n this.handleFilterClick(button),\n );\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 filter() {\n this.visibleItems.clear(); // Start with an empty set\n\n this.items.forEach((item) => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item); // Add visible item to the set\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); // Add visible item to the set\n } else {\n this.hideItem(item);\n }\n }\n });\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\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 === \"\" ||\n 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 * Update URL with current filter state\n * @private\n */\n updateURL() {\n const params = new URLSearchParams();\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 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 * 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","executedFunction","_len","arguments","length","args","Array","_key","later","clearTimeout","setTimeout","AFS","constructor","options","undefined","containerSelector","itemSelector","filterButtonSelector","searchInputSelector","counterSelector","activeClass","hiddenClass","animationDuration","filterMode","searchKeys","debounceTime","container","document","querySelector","items","querySelectorAll","filterButtons","searchInput","counter","currentFilters","Set","currentSearch","visibleItems","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","item","has","showItem","itemCategories","categories","split","matchesFilter","matchesAnyFilter","matchesAllFilters","hideItem","some","type","every","style","opacity","transform","offsetHeight","requestAnimationFrame","query","toLowerCase","trim","searchText","map","key","join","matchesSearch","includes","sortMultiple","criteria","sort","a","b","criterion","valueA","valueB","comparison","direction","localeCompare","addRangeFilter","min","max","parseFloat","inRange","params","URLSearchParams","filtersByType","Object","entries","_ref","values","set","from","newURL","location","pathname","toString","history","pushState","hasFilters","get","total","visible","setFilterMode","mode","toUpperCase","addFilter","removeFilter","getActiveFiltersByType","startsWith"],"mappings":";;;;;;IAAA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;IAEA;IACA;IACA;IACA;IACA;IACA;IACA,SAASA,QAAQA,CAACC,IAAI,EAAEC,IAAI,EAAE;IAC1B,EAAA,IAAIC,OAAO,CAAA;MACX,OAAO,SAASC,gBAAgBA,GAAU;IAAA,IAAA,KAAA,IAAAC,IAAA,GAAAC,SAAA,CAAAC,MAAA,EAANC,IAAI,GAAAC,IAAAA,KAAA,CAAAJ,IAAA,GAAAK,IAAA,GAAA,CAAA,EAAAA,IAAA,GAAAL,IAAA,EAAAK,IAAA,EAAA,EAAA;IAAJF,MAAAA,IAAI,CAAAE,IAAA,CAAAJ,GAAAA,SAAA,CAAAI,IAAA,CAAA,CAAA;IAAA,KAAA;QACpC,MAAMC,KAAK,GAAGA,MAAM;UAChBC,YAAY,CAACT,OAAO,CAAC,CAAA;UACrBF,IAAI,CAAC,GAAGO,IAAI,CAAC,CAAA;SAChB,CAAA;QACDI,YAAY,CAACT,OAAO,CAAC,CAAA;IACrBA,IAAAA,OAAO,GAAGU,UAAU,CAACF,KAAK,EAAET,IAAI,CAAC,CAAA;OACpC,CAAA;IACL,CAAA;IAEA,MAAMY,GAAG,CAAC;IACN;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;IAEI;IACJ;IACA;IACIC,EAAAA,WAAWA,GAAe;IAAA,IAAA,IAAdC,OAAO,GAAAV,SAAA,CAAAC,MAAA,GAAA,CAAA,IAAAD,SAAA,CAAA,CAAA,CAAA,KAAAW,SAAA,GAAAX,SAAA,CAAA,CAAA,CAAA,GAAG,EAAE,CAAA;QACpB,IAAI,CAACU,OAAO,GAAG;IACXE,MAAAA,iBAAiB,EAAE,mBAAmB;IACtCC,MAAAA,YAAY,EAAE,cAAc;IAC5BC,MAAAA,oBAAoB,EAAE,aAAa;IACnCC,MAAAA,mBAAmB,EAAE,gBAAgB;IACrCC,MAAAA,eAAe,EAAE,iBAAiB;IAClCC,MAAAA,WAAW,EAAE,QAAQ;IACrBC,MAAAA,WAAW,EAAE,QAAQ;IACrBC,MAAAA,iBAAiB,EAAE,GAAG;IACtBC,MAAAA,UAAU,EAAE,IAAI;UAChBC,UAAU,EAAE,CAAC,OAAO,CAAC;IACrBC,MAAAA,YAAY,EAAE,GAAG;UACjB,GAAGZ,OAAAA;SACN,CAAA;;IAED;IACA,IAAA,IAAI,CAACa,SAAS,GAAGC,QAAQ,CAACC,aAAa,CAAC,IAAI,CAACf,OAAO,CAACE,iBAAiB,CAAC,CAAA;IACvE,IAAA,IAAI,CAACc,KAAK,GAAGF,QAAQ,CAACG,gBAAgB,CAAC,IAAI,CAACjB,OAAO,CAACG,YAAY,CAAC,CAAA;IACjE,IAAA,IAAI,CAACe,aAAa,GAAGJ,QAAQ,CAACG,gBAAgB,CAC1C,IAAI,CAACjB,OAAO,CAACI,oBACjB,CAAC,CAAA;IACD,IAAA,IAAI,CAACe,WAAW,GAAGL,QAAQ,CAACC,aAAa,CACrC,IAAI,CAACf,OAAO,CAACK,mBACjB,CAAC,CAAA;IACD,IAAA,IAAI,CAACe,OAAO,GAAGN,QAAQ,CAACC,aAAa,CAAC,IAAI,CAACf,OAAO,CAACM,eAAe,CAAC,CAAA;;IAEnE;QACA,IAAI,CAACe,cAAc,GAAG,IAAIC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACpC,IAAI,CAACC,aAAa,GAAG,EAAE,CAAA;QACvB,IAAI,CAACC,YAAY,GAAG,IAAIF,GAAG,CAAC,IAAI,CAACN,KAAK,CAAC,CAAA;QAEvC,IAAI,CAACS,IAAI,EAAE,CAAA;IACf,GAAA;;IAEA;IACJ;IACA;IACA;IACIA,EAAAA,IAAIA,GAAG;QACH,IAAI,CAACC,SAAS,EAAE,CAAA;QAChB,IAAI,CAACC,UAAU,EAAE,CAAA;QACjB,IAAI,CAACC,WAAW,EAAE,CAAA;QAClB,IAAI,CAACC,aAAa,EAAE,CAAA;IACxB,GAAA;;IAEA;IACJ;IACA;IACA;IACIH,EAAAA,SAASA,GAAG;IACR,IAAA,MAAMI,MAAM,GAAG,CAAA;AACvB,aAAA,EAAe,IAAI,CAAC9B,OAAO,CAACQ,WAAW,CAAA;AACvC;AACA;AACA;AACA,YAAA,EAAc,IAAI,CAACR,OAAO,CAACG,YAAY,CAAA;AACvC;AACA;AACA,oCAAA,EAAsC,IAAI,CAACH,OAAO,CAACS,iBAAiB,CAAA;AACpE,sCAAA,EAAwC,IAAI,CAACT,OAAO,CAACS,iBAAiB,CAAA;AACtE;AACA;AACA,YAAA,EAAc,IAAI,CAACT,OAAO,CAACI,oBAAoB,CAAA;AAC/C;AACA,oCAAA,EAAsC,IAAI,CAACJ,OAAO,CAACS,iBAAiB,CAAA;AACpE;AACA;AACA,YAAc,EAAA,IAAI,CAACT,OAAO,CAACI,oBAAoB,IAAI,IAAI,CAACJ,OAAO,CAACO,WAAW,CAAA;AAC3E;AACA;AACA,QAAS,CAAA,CAAA;IAED,IAAA,MAAMwB,UAAU,GAAGjB,QAAQ,CAACkB,aAAa,CAAC,OAAO,CAAC,CAAA;QAClDD,UAAU,CAACE,WAAW,GAAGH,MAAM,CAAA;IAC/BhB,IAAAA,QAAQ,CAACoB,IAAI,CAACC,WAAW,CAACJ,UAAU,CAAC,CAAA;IACzC,GAAA;;IAEA;IACJ;IACA;IACA;IACIJ,EAAAA,UAAUA,GAAG;IACT,IAAA,IAAI,CAACT,aAAa,CAACkB,OAAO,CAAEC,MAAM,IAAK;IACnCA,MAAAA,MAAM,CAACC,gBAAgB,CAAC,OAAO,EAAE,MAC7B,IAAI,CAACC,iBAAiB,CAACF,MAAM,CACjC,CAAC,CAAA;IACL,KAAC,CAAC,CAAA;QAEF,IAAI,IAAI,CAAClB,WAAW,EAAE;UAClB,IAAI,CAACA,WAAW,CAACmB,gBAAgB,CAC7B,OAAO,EACPtD,QAAQ,CAAEwD,CAAC,IAAK;YACZ,IAAI,CAACC,MAAM,CAACD,CAAC,CAACE,MAAM,CAACC,KAAK,CAAC,CAAA;IAC/B,OAAC,EAAE,IAAI,CAAC3C,OAAO,CAACY,YAAY,CAChC,CAAC,CAAA;IACL,KAAA;QAEAgC,MAAM,CAACN,gBAAgB,CAAC,UAAU,EAAE,MAAM,IAAI,CAACV,WAAW,EAAE,CAAC,CAAA;IACjE,GAAA;;IAEA;IACJ;IACA;IACA;IACA;MACIW,iBAAiBA,CAACF,MAAM,EAAE;IACtB,IAAA,MAAMQ,WAAW,GAAGR,MAAM,CAACS,OAAO,CAACC,MAAM,CAAA;QAEzC,IAAIF,WAAW,KAAK,GAAG,EAAE;UACrB,IAAI,CAACG,YAAY,EAAE,CAAA;IACvB,KAAC,MAAM;IACH,MAAA,IAAI,CAACC,YAAY,CAACJ,WAAW,EAAER,MAAM,CAAC,CAAA;IAC1C,KAAA;QAEA,IAAI,CAACU,MAAM,EAAE,CAAA;QACb,IAAI,CAACG,SAAS,EAAE,CAAA;IACpB,GAAA;;IAEA;IACJ;IACA;IACA;IACIF,EAAAA,YAAYA,GAAG;IACX,IAAA,IAAI,CAAC9B,aAAa,CAACkB,OAAO,CAAEe,GAAG,IAC3BA,GAAG,CAACC,SAAS,CAACC,MAAM,CAAC,IAAI,CAACrD,OAAO,CAACO,WAAW,CACjD,CAAC,CAAA;IACD,IAAA,IAAI,CAACc,cAAc,CAACiC,KAAK,EAAE,CAAA;IAC3B,IAAA,IAAI,CAACjC,cAAc,CAACkC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC5B,IAAA,IAAI,CAACrC,aAAa,CAAC,CAAC,CAAC,CAACkC,SAAS,CAACG,GAAG,CAAC,IAAI,CAACvD,OAAO,CAACO,WAAW,CAAC,CAAA;QAC7D,IAAI,CAACiD,YAAY,EAAE,CAAA;IACvB,GAAA;;IAEA;IACJ;IACA;IACA;IACIA,EAAAA,YAAYA,GAAG;QACX,IAAI,CAAChC,YAAY,GAAG,IAAIF,GAAG,CAAC,IAAI,CAACN,KAAK,CAAC,CAAA;QACvC,IAAI,CAACa,aAAa,EAAE,CAAA;IACxB,GAAA;;IAEA;IACJ;IACA;IACA;IACA;IACA;IACIoB,EAAAA,YAAYA,CAACJ,WAAW,EAAER,MAAM,EAAE;IAC9B,IAAA,IAAI,CAAChB,cAAc,CAACoC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC/B,IAAA,IAAI,CAACvC,aAAa,CAAC,CAAC,CAAC,CAACkC,SAAS,CAACC,MAAM,CAAC,IAAI,CAACrD,OAAO,CAACO,WAAW,CAAC,CAAA;IAEhE,IAAA,IAAI8B,MAAM,CAACe,SAAS,CAACM,QAAQ,CAAC,IAAI,CAAC1D,OAAO,CAACO,WAAW,CAAC,EAAE;UACrD8B,MAAM,CAACe,SAAS,CAACC,MAAM,CAAC,IAAI,CAACrD,OAAO,CAACO,WAAW,CAAC,CAAA;IACjD,MAAA,IAAI,CAACc,cAAc,CAACoC,MAAM,CAACZ,WAAW,CAAC,CAAA;IAEvC,MAAA,IAAI,IAAI,CAACxB,cAAc,CAACsC,IAAI,KAAK,CAAC,EAAE;YAChC,IAAI,CAACX,YAAY,EAAE,CAAA;IACvB,OAAA;IACJ,KAAC,MAAM;UACHX,MAAM,CAACe,SAAS,CAACG,GAAG,CAAC,IAAI,CAACvD,OAAO,CAACO,WAAW,CAAC,CAAA;IAC9C,MAAA,IAAI,CAACc,cAAc,CAACkC,GAAG,CAACV,WAAW,CAAC,CAAA;IACxC,KAAA;IACJ,GAAA;;IAEA;IACJ;IACA;IACA;IACIE,EAAAA,MAAMA,GAAG;IACL,IAAA,IAAI,CAACvB,YAAY,CAAC8B,KAAK,EAAE,CAAC;;IAE1B,IAAA,IAAI,CAACtC,KAAK,CAACoB,OAAO,CAAEwB,IAAI,IAAK;UACzB,IAAI,IAAI,CAACvC,cAAc,CAACwC,GAAG,CAAC,GAAG,CAAC,EAAE;IAC9B,QAAA,IAAI,CAACC,QAAQ,CAACF,IAAI,CAAC,CAAA;YACnB,IAAI,CAACpC,YAAY,CAAC+B,GAAG,CAACK,IAAI,CAAC,CAAC;IAChC,OAAC,MAAM;IACH,QAAA,MAAMG,cAAc,GAAG,IAAIzC,GAAG,CAC1BsC,IAAI,CAACd,OAAO,CAACkB,UAAU,EAAEC,KAAK,CAAC,GAAG,CAAC,IAAI,EAC3C,CAAC,CAAA;YACD,MAAMC,aAAa,GACf,IAAI,CAAClE,OAAO,CAACU,UAAU,KAAK,IAAI,GAC1B,IAAI,CAACyD,gBAAgB,CAACJ,cAAc,CAAC,GACrC,IAAI,CAACK,iBAAiB,CAACL,cAAc,CAAC,CAAA;IAEhD,QAAA,IAAIG,aAAa,EAAE;IACf,UAAA,IAAI,CAACJ,QAAQ,CAACF,IAAI,CAAC,CAAA;cACnB,IAAI,CAACpC,YAAY,CAAC+B,GAAG,CAACK,IAAI,CAAC,CAAC;IAChC,SAAC,MAAM;IACH,UAAA,IAAI,CAACS,QAAQ,CAACT,IAAI,CAAC,CAAA;IACvB,SAAA;IACJ,OAAA;IACJ,KAAC,CAAC,CAAA;IAEF/D,IAAAA,UAAU,CAAC,MAAM;UACb,IAAI,CAACgC,aAAa,EAAE,CAAA;IACxB,KAAC,EAAE,IAAI,CAAC7B,OAAO,CAACS,iBAAiB,CAAC,CAAA;IACtC,GAAA;;IAEA;IACJ;IACA;IACA;IACA;IACA;MACI0D,gBAAgBA,CAACJ,cAAc,EAAE;QAC7B,OAAO,CAAC,GAAG,IAAI,CAAC1C,cAAc,CAAC,CAACiD,IAAI,CAAEvB,MAAM,IAAK;UAC7C,MAAM,CAACwB,IAAI,EAAE5B,KAAK,CAAC,GAAGI,MAAM,CAACkB,KAAK,CAAC,GAAG,CAAC,CAAA;UACvC,OAAOF,cAAc,CAACF,GAAG,CAAC,GAAGU,IAAI,CAAA,CAAA,EAAI5B,KAAK,CAAA,CAAE,CAAC,CAAA;IACjD,KAAC,CAAC,CAAA;IACN,GAAA;;IAEA;IACJ;IACA;IACA;IACA;IACA;MACIyB,iBAAiBA,CAACL,cAAc,EAAE;QAC9B,OAAO,CAAC,GAAG,IAAI,CAAC1C,cAAc,CAAC,CAACmD,KAAK,CAAEzB,MAAM,IAAK;UAC9C,MAAM,CAACwB,IAAI,EAAE5B,KAAK,CAAC,GAAGI,MAAM,CAACkB,KAAK,CAAC,GAAG,CAAC,CAAA;UACvC,OAAOF,cAAc,CAACF,GAAG,CAAC,GAAGU,IAAI,CAAA,CAAA,EAAI5B,KAAK,CAAA,CAAE,CAAC,CAAA;IACjD,KAAC,CAAC,CAAA;IACN,GAAA;;IAEA;IACJ;IACA;IACA;IACA;MACImB,QAAQA,CAACF,IAAI,EAAE;IACX,IAAA,IAAI,CAACpC,YAAY,CAAC+B,GAAG,CAACK,IAAI,CAAC,CAAA;QAC3BA,IAAI,CAACR,SAAS,CAACC,MAAM,CAAC,IAAI,CAACrD,OAAO,CAACQ,WAAW,CAAC,CAAA;IAC/CoD,IAAAA,IAAI,CAACa,KAAK,CAACC,OAAO,GAAG,GAAG,CAAA;IACxBd,IAAAA,IAAI,CAACa,KAAK,CAACE,SAAS,GAAG,aAAa,CAAA;IAEpCf,IAAAA,IAAI,CAACgB,YAAY,CAAA;IAEjBC,IAAAA,qBAAqB,CAAC,MAAM;IACxBjB,MAAAA,IAAI,CAACa,KAAK,CAACC,OAAO,GAAG,GAAG,CAAA;IACxBd,MAAAA,IAAI,CAACa,KAAK,CAACE,SAAS,GAAG,UAAU,CAAA;IACrC,KAAC,CAAC,CAAA;IACN,GAAA;;IAEA;IACJ;IACA;IACA;IACA;MACIN,QAAQA,CAACT,IAAI,EAAE;IACXA,IAAAA,IAAI,CAACa,KAAK,CAACC,OAAO,GAAG,GAAG,CAAA;IACxBd,IAAAA,IAAI,CAACa,KAAK,CAACE,SAAS,GAAG,aAAa,CAAA;IAEpC9E,IAAAA,UAAU,CAAC,MAAM;IACb,MAAA,IAAI+D,IAAI,CAACa,KAAK,CAACC,OAAO,KAAK,GAAG,EAAE;YAC5Bd,IAAI,CAACR,SAAS,CAACG,GAAG,CAAC,IAAI,CAACvD,OAAO,CAACQ,WAAW,CAAC,CAAA;IAC5C,QAAA,IAAI,CAACgB,YAAY,CAACiC,MAAM,CAACG,IAAI,CAAC,CAAA;IAClC,OAAA;IACJ,KAAC,EAAE,IAAI,CAAC5D,OAAO,CAACS,iBAAiB,CAAC,CAAA;IACtC,GAAA;;IAEA;IACJ;IACA;IACA;IACA;MACIgC,MAAMA,CAACqC,KAAK,EAAE;QACV,IAAI,CAACvD,aAAa,GAAGuD,KAAK,CAACC,WAAW,EAAE,CAACC,IAAI,EAAE,CAAA;IAG/C,IAAA,IAAI,CAAChE,KAAK,CAACoB,OAAO,CAAEwB,IAAI,IAAK;IACzB,MAAA,MAAMqB,UAAU,GAAG,IAAI,CAACjF,OAAO,CAACW,UAAU,CACrCuE,GAAG,CAAEC,GAAG,IAAKvB,IAAI,CAACd,OAAO,CAACqC,GAAG,CAAC,IAAI,EAAE,CAAC,CACrCC,IAAI,CAAC,GAAG,CAAC,CACTL,WAAW,EAAE,CAAA;IAElB,MAAA,MAAMM,aAAa,GACf,IAAI,CAAC9D,aAAa,KAAK,EAAE,IACzB0D,UAAU,CAACK,QAAQ,CAAC,IAAI,CAAC/D,aAAa,CAAC,CAAA;IAE3C,MAAA,IAAI8D,aAAa,EAAE;IACf,QAAA,IAAI,CAACvB,QAAQ,CAACF,IAAI,CAAC,CAAA;IAEvB,OAAC,MAAM;IACH,QAAA,IAAI,CAACS,QAAQ,CAACT,IAAI,CAAC,CAAA;IACvB,OAAA;IACJ,KAAC,CAAC,CAAA;QAEF,IAAI,CAACV,SAAS,EAAE,CAAA;IAEhBrD,IAAAA,UAAU,CAAC,MAAM;UACb,IAAI,CAACgC,aAAa,EAAE,CAAA;IACxB,KAAC,EAAE,IAAI,CAAC7B,OAAO,CAACS,iBAAiB,CAAC,CAAA;IACtC,GAAA;;IAEA;IACJ;IACA;IACA;IACA;MACI8E,YAAYA,CAACC,QAAQ,EAAE;IACnB,IAAA,MAAMxE,KAAK,GAAG,CAAC,GAAG,IAAI,CAACA,KAAK,CAAC,CAAA;IAE7BA,IAAAA,KAAK,CAACyE,IAAI,CAAC,CAACC,CAAC,EAAEC,CAAC,KAAK;IACjB,MAAA,KAAK,MAAMC,SAAS,IAAIJ,QAAQ,EAAE;YAC9B,MAAMK,MAAM,GAAGH,CAAC,CAAC5C,OAAO,CAAC8C,SAAS,CAACT,GAAG,CAAC,CAAA;YACvC,MAAMW,MAAM,GAAGH,CAAC,CAAC7C,OAAO,CAAC8C,SAAS,CAACT,GAAG,CAAC,CAAA;YAEvC,MAAMY,UAAU,GACZH,SAAS,CAACI,SAAS,KAAK,KAAK,GACvBH,MAAM,CAACI,aAAa,CAACH,MAAM,CAAC,GAC5BA,MAAM,CAACG,aAAa,CAACJ,MAAM,CAAC,CAAA;IAEtC,QAAA,IAAIE,UAAU,KAAK,CAAC,EAAE,OAAOA,UAAU,CAAA;IAC3C,OAAA;IACA,MAAA,OAAO,CAAC,CAAA;IACZ,KAAC,CAAC,CAAA;IAEF/E,IAAAA,KAAK,CAACoB,OAAO,CAAEwB,IAAI,IAAK,IAAI,CAAC/C,SAAS,CAACsB,WAAW,CAACyB,IAAI,CAAC,CAAC,CAAA;IAC7D,GAAA;;IAEA;IACJ;IACA;IACA;IACA;IACA;IACA;IACIsC,EAAAA,cAAcA,CAACf,GAAG,EAAEgB,GAAG,EAAEC,GAAG,EAAE;IAC1B,IAAA,IAAI,CAACpF,KAAK,CAACoB,OAAO,CAAEwB,IAAI,IAAK;UACzB,MAAMjB,KAAK,GAAG0D,UAAU,CAACzC,IAAI,CAACd,OAAO,CAACqC,GAAG,CAAC,CAAC,CAAA;UAC3C,MAAMmB,OAAO,GAAG3D,KAAK,IAAIwD,GAAG,IAAIxD,KAAK,IAAIyD,GAAG,CAAA;IAE5C,MAAA,IAAIE,OAAO,EAAE;IACT,QAAA,IAAI,CAACxC,QAAQ,CAACF,IAAI,CAAC,CAAA;IACvB,OAAC,MAAM;IACH,QAAA,IAAI,CAACS,QAAQ,CAACT,IAAI,CAAC,CAAA;IACvB,OAAA;IACJ,KAAC,CAAC,CAAA;IAEF/D,IAAAA,UAAU,CAAC,MAAM;UACb,IAAI,CAACgC,aAAa,EAAE,CAAA;IACxB,KAAC,EAAE,IAAI,CAAC7B,OAAO,CAACS,iBAAiB,CAAC,CAAA;IACtC,GAAA;;IAEA;IACJ;IACA;IACA;IACIyC,EAAAA,SAASA,GAAG;IACR,IAAA,MAAMqD,MAAM,GAAG,IAAIC,eAAe,EAAE,CAAA;;IAEpC;QACA,MAAMC,aAAa,GAAG,EAAE,CAAA;IACxB,IAAA,KAAK,MAAM1D,MAAM,IAAI,IAAI,CAAC1B,cAAc,EAAE;UACtC,IAAI0B,MAAM,KAAK,GAAG,EAAE;YAChB,MAAM,CAACwB,IAAI,EAAE5B,KAAK,CAAC,GAAGI,MAAM,CAACkB,KAAK,CAAC,GAAG,CAAC,CAAA;IACvC,QAAA,IAAI,CAACwC,aAAa,CAAClC,IAAI,CAAC,EAAE;IACtBkC,UAAAA,aAAa,CAAClC,IAAI,CAAC,GAAG,IAAIjD,GAAG,EAAE,CAAA;IACnC,SAAA;IACAmF,QAAAA,aAAa,CAAClC,IAAI,CAAC,CAAChB,GAAG,CAACZ,KAAK,CAAC,CAAA;IAClC,OAAA;IACJ,KAAA;;IAEA;QACA+D,MAAM,CAACC,OAAO,CAACF,aAAa,CAAC,CAACrE,OAAO,CAACwE,IAAA,IAAoB;IAAA,MAAA,IAAnB,CAACrC,IAAI,EAAEsC,MAAM,CAAC,GAAAD,IAAA,CAAA;IACjDL,MAAAA,MAAM,CAACO,GAAG,CAACvC,IAAI,EAAE9E,KAAK,CAACsH,IAAI,CAACF,MAAM,CAAC,CAACzB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IAClD,KAAC,CAAC,CAAA;QAEF,IAAI,IAAI,CAAC7D,aAAa,EAAE;UACpBgF,MAAM,CAACO,GAAG,CAAC,QAAQ,EAAE,IAAI,CAACvF,aAAa,CAAC,CAAA;IAC5C,KAAA;QAEA,MAAMyF,MAAM,GAAG,CAAA,EAAGpE,MAAM,CAACqE,QAAQ,CAACC,QAAQ,CACtCX,EAAAA,MAAM,CAACY,QAAQ,EAAE,GAAG,GAAG,GAAGZ,MAAM,CAACY,QAAQ,EAAE,GAAG,EAAE,CAClD,CAAA,CAAA;QACFvE,MAAM,CAACwE,OAAO,CAACC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAEL,MAAM,CAAC,CAAA;IAC5C,GAAA;;IAEA;IACJ;IACA;IACA;IACIpF,EAAAA,WAAWA,GAAG;QACV,MAAM2E,MAAM,GAAG,IAAIC,eAAe,CAAC5D,MAAM,CAACqE,QAAQ,CAACxE,MAAM,CAAC,CAAA;IAC1D,IAAA,IAAI,CAACpB,cAAc,CAACiC,KAAK,EAAE,CAAA;;IAE3B;QACA,IAAIgE,UAAU,GAAG,KAAK,CAAA;;IAEtB;IACA,IAAA,KAAK,MAAM,CAAC/C,IAAI,EAAEsC,MAAM,CAAC,IAAIN,MAAM,CAACI,OAAO,EAAE,EAAE;UAC3C,IAAIpC,IAAI,KAAK,QAAQ,EAAE;IACnB+C,QAAAA,UAAU,GAAG,IAAI,CAAA;YACjBT,MAAM,CAAC5C,KAAK,CAAC,GAAG,CAAC,CAAC7B,OAAO,CAAEO,KAAK,IAAK;cACjC,IAAI,CAACtB,cAAc,CAACkC,GAAG,CAAC,GAAGgB,IAAI,CAAA,CAAA,EAAI5B,KAAK,CAAA,CAAE,CAAC,CAAA;IAC/C,SAAC,CAAC,CAAA;IACN,OAAA;IACJ,KAAA;QAEA,IAAI,CAAC2E,UAAU,EAAE;IACb,MAAA,IAAI,CAACjG,cAAc,CAACkC,GAAG,CAAC,GAAG,CAAC,CAAA;IAChC,KAAA;;IAEA;IACA,IAAA,IAAI,CAACrC,aAAa,CAACkB,OAAO,CAAEC,MAAM,IAAK;IACnC,MAAA,MAAMQ,WAAW,GAAGR,MAAM,CAACS,OAAO,CAACC,MAAM,CAAA;UACzC,IACI,IAAI,CAAC1B,cAAc,CAACwC,GAAG,CAAChB,WAAW,CAAC,IACnCA,WAAW,KAAK,GAAG,IAAI,IAAI,CAACxB,cAAc,CAACwC,GAAG,CAAC,GAAG,CAAE,EACvD;YACExB,MAAM,CAACe,SAAS,CAACG,GAAG,CAAC,IAAI,CAACvD,OAAO,CAACO,WAAW,CAAC,CAAA;IAClD,OAAC,MAAM;YACH8B,MAAM,CAACe,SAAS,CAACC,MAAM,CAAC,IAAI,CAACrD,OAAO,CAACO,WAAW,CAAC,CAAA;IACrD,OAAA;IACJ,KAAC,CAAC,CAAA;;IAEF;QACA,MAAMkC,MAAM,GAAG8D,MAAM,CAACgB,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;QACzC,IAAI,IAAI,CAACpG,WAAW,EAAE;IAClB,MAAA,IAAI,CAACA,WAAW,CAACwB,KAAK,GAAGF,MAAM,CAAA;IACnC,KAAA;QAEA,IAAI,CAACM,MAAM,EAAE,CAAA;IACb,IAAA,IAAIN,MAAM,EAAE;IACR,MAAA,IAAI,CAACA,MAAM,CAACA,MAAM,CAAC,CAAA;IACvB,KAAA;IACJ,GAAA;;IAEA;IACJ;IACA;IACA;IACA;IACIZ,EAAAA,aAAaA,GAAG;IACZ,IAAA,MAAM2F,KAAK,GAAG,IAAI,CAACxG,KAAK,CAACzB,MAAM,CAAA;IAC/B,IAAA,MAAMkI,OAAO,GAAG,IAAI,CAACjG,YAAY,CAACmC,IAAI,CAAA;QAEtC,IAAI,IAAI,CAACvC,OAAO,EAAE;UACd,IAAI,CAACA,OAAO,CAACa,WAAW,GAAG,CAAWwF,QAAAA,EAAAA,OAAO,CAAOD,IAAAA,EAAAA,KAAK,CAAE,CAAA,CAAA;IAC/D,KAAA;QAEA,OAAO;UAAEA,KAAK;IAAEC,MAAAA,OAAAA;SAAS,CAAA;IAC7B,GAAA;;IAEA;IACJ;IACA;IACA;IACA;MACIC,aAAaA,CAACC,IAAI,EAAE;IAChB,IAAA,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAACrC,QAAQ,CAACqC,IAAI,CAACC,WAAW,EAAE,CAAC,EAAE;UAC5C,IAAI,CAAC5H,OAAO,CAACU,UAAU,GAAGiH,IAAI,CAACC,WAAW,EAAE,CAAA;UAC5C,IAAI,CAAC7E,MAAM,EAAE,CAAA;IACjB,KAAA;IACJ,GAAA;;IAEA;IACJ;IACA;IACA;IACA;IACA;IACI8E,EAAAA,SAASA,CAACtD,IAAI,EAAE5B,KAAK,EAAE;QACnB,IAAI,CAACtB,cAAc,CAACkC,GAAG,CAAC,GAAGgB,IAAI,CAAA,CAAA,EAAI5B,KAAK,CAAA,CAAE,CAAC,CAAA;QAC3C,IAAI,CAACI,MAAM,EAAE,CAAA;QACb,IAAI,CAACG,SAAS,EAAE,CAAA;IACpB,GAAA;;IAEA;IACJ;IACA;IACA;IACA;IACI4E,EAAAA,YAAYA,CAACvD,IAAI,EAAE5B,KAAK,EAAE;QACtB,IAAI,CAACtB,cAAc,CAACoC,MAAM,CAAC,GAAGc,IAAI,CAAA,CAAA,EAAI5B,KAAK,CAAA,CAAE,CAAC,CAAA;IAC9C,IAAA,IAAI,IAAI,CAACtB,cAAc,CAACsC,IAAI,KAAK,CAAC,EAAE;IAChC,MAAA,IAAI,CAACtC,cAAc,CAACkC,GAAG,CAAC,GAAG,CAAC,CAAA;IAChC,KAAA;QACA,IAAI,CAACR,MAAM,EAAE,CAAA;QACb,IAAI,CAACG,SAAS,EAAE,CAAA;IACpB,GAAA;;IAEA;IACJ;IACA;IACA;IACA;MACI6E,sBAAsBA,CAACxD,IAAI,EAAE;IACzB,IAAA,OAAO,CAAC,GAAG,IAAI,CAAClD,cAAc,CAAC,CAC1B0B,MAAM,CAAEA,MAAM,IAAKA,MAAM,CAACiF,UAAU,CAAC,CAAGzD,EAAAA,IAAI,CAAG,CAAA,CAAA,CAAC,CAAC,CACjDW,GAAG,CAAEnC,MAAM,IAAKA,MAAM,CAACkB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9C,GAAA;IACJ;;;;;;;;;;"}
1
+ {"version":3,"file":"AFS.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(\n this.options.searchInputSelector,\n );\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\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\", () =>\n this.handleFilterClick(button),\n );\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 filter() {\n this.visibleItems.clear(); // Start with an empty set\n\n this.items.forEach((item) => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item); // Add visible item to the set\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); // Add visible item to the set\n } else {\n this.hideItem(item);\n }\n }\n });\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\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 === \"\" ||\n 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 * Update URL with current filter state\n * @private\n */\n updateURL() {\n const params = new URLSearchParams();\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 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 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 /**\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","executedFunction","_len","arguments","length","args","Array","_key","later","clearTimeout","setTimeout","AFS","constructor","options","undefined","containerSelector","itemSelector","filterButtonSelector","searchInputSelector","counterSelector","activeClass","hiddenClass","animationDuration","filterMode","searchKeys","debounceTime","container","document","querySelector","items","querySelectorAll","filterButtons","searchInput","counter","currentFilters","Set","currentSearch","visibleItems","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","item","has","showItem","itemCategories","categories","split","matchesFilter","matchesAnyFilter","matchesAllFilters","hideItem","some","type","every","style","opacity","transform","offsetHeight","requestAnimationFrame","query","toLowerCase","trim","searchText","map","key","join","matchesSearch","includes","sortMultiple","criteria","sort","a","b","criterion","valueA","valueB","comparison","direction","localeCompare","addRangeFilter","min","max","parseFloat","inRange","params","URLSearchParams","filtersByType","Object","entries","_ref","values","set","from","newURL","location","pathname","toString","history","pushState","hasFilters","get","total","visible","setLogic","logic","mode","toUpperCase","setFilterMode","addFilter","removeFilter","getActiveFiltersByType","startsWith"],"mappings":"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"}
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";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.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",function(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=>{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(){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)}})),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}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)}updateURL(){const t=new URLSearchParams,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.currentFilters.clear();let e=!1;for(const[s,i]of t.entries())"search"!==s&&(e=!0,i.split(",").forEach((t=>{this.currentFilters.add(`${s}:${t}`)})));e||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 s=t.get("search")||"";this.searchInput&&(this.searchInput.value=s),this.filter(),s&&this.search(s)}updateCounter(){const t=this.items.length,e=this.visibleItems.size;return this.counter&&(this.counter.textContent=`Showing ${e} of ${t}`),{total:t,visible:e}}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]))}},Object.defineProperty(t,"__esModule",{value:!0})}));
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";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.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",function(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=>{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(){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)}})),setTimeout((()=>{this.updateCounter()}),this.options.animationDuration)}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)}updateURL(){const t=new URLSearchParams,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.currentFilters.clear();let e=!1;for(const[s,i]of t.entries())"search"!==s&&(e=!0,i.split(",").forEach((t=>{this.currentFilters.add(`${s}:${t}`)})));e||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 s=t.get("search")||"";this.searchInput&&(this.searchInput.value=s),this.filter(),s&&this.search(s)}updateCounter(){const t=this.items.length,e=this.visibleItems.size;return this.counter&&(this.counter.textContent=`Showing ${e} of ${t}`),{total:t,visible:e}}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
@@ -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(\n this.options.searchInputSelector,\n );\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\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\", () =>\n this.handleFilterClick(button),\n );\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 filter() {\n this.visibleItems.clear(); // Start with an empty set\n\n this.items.forEach((item) => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item); // Add visible item to the set\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); // Add visible item to the set\n } else {\n this.hideItem(item);\n }\n }\n });\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\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 === \"\" ||\n 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 * Update URL with current filter state\n * @private\n */\n updateURL() {\n const params = new URLSearchParams();\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 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 * 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":["constructor","options","arguments","length","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","init","addStyles","bindEvents","loadFromURL","updateCounter","styles","styleSheet","createElement","textContent","head","appendChild","forEach","button","addEventListener","handleFilterClick","func","wait","timeout","_len","args","Array","_key","clearTimeout","setTimeout","later","debounce","e","search","target","value","window","filterValue","dataset","filter","resetFilters","toggleFilter","updateURL","btn","classList","remove","clear","add","resetCounter","delete","contains","size","item","has","showItem","itemCategories","categories","split","matchesAnyFilter","matchesAllFilters","hideItem","some","type","every","style","opacity","transform","offsetHeight","requestAnimationFrame","query","toLowerCase","trim","searchText","map","key","join","includes","sortMultiple","criteria","sort","a","b","criterion","valueA","valueB","comparison","direction","localeCompare","addRangeFilter","min","max","parseFloat","params","URLSearchParams","filtersByType","Object","entries","_ref","values","set","from","newURL","location","pathname","toString","history","pushState","hasFilters","get","total","visible","setFilterMode","mode","toUpperCase","addFilter","removeFilter","getActiveFiltersByType","startsWith"],"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.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(\n this.options.searchInputSelector,\n );\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\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\", () =>\n this.handleFilterClick(button),\n );\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 filter() {\n this.visibleItems.clear(); // Start with an empty set\n\n this.items.forEach((item) => {\n if (this.currentFilters.has(\"*\")) {\n this.showItem(item);\n this.visibleItems.add(item); // Add visible item to the set\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); // Add visible item to the set\n } else {\n this.hideItem(item);\n }\n }\n });\n\n setTimeout(() => {\n this.updateCounter();\n }, this.options.animationDuration);\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 === \"\" ||\n 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 * Update URL with current filter state\n * @private\n */\n updateURL() {\n const params = new URLSearchParams();\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 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 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 /**\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":["constructor","options","arguments","length","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","init","addStyles","bindEvents","loadFromURL","updateCounter","styles","styleSheet","createElement","textContent","head","appendChild","forEach","button","addEventListener","handleFilterClick","func","wait","timeout","_len","args","Array","_key","clearTimeout","setTimeout","later","debounce","e","search","target","value","window","filterValue","dataset","filter","resetFilters","toggleFilter","updateURL","btn","classList","remove","clear","add","resetCounter","delete","contains","size","item","has","showItem","itemCategories","categories","split","matchesAnyFilter","matchesAllFilters","hideItem","some","type","every","style","opacity","transform","offsetHeight","requestAnimationFrame","query","toLowerCase","trim","searchText","map","key","join","includes","sortMultiple","criteria","sort","a","b","criterion","valueA","valueB","comparison","direction","localeCompare","addRangeFilter","min","max","parseFloat","params","URLSearchParams","filtersByType","Object","entries","_ref","values","set","from","newURL","location","pathname","toString","history","pushState","hasFilters","get","total","visible","setLogic","logic","mode","toUpperCase","setFilterMode","addFilter","removeFilter","getActiveFiltersByType","startsWith"],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,24 @@
1
1
  {
2
2
  "name": "advanced-filter-system",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Advanced filtering system for DOM elements with multiple features",
5
5
  "main": "dist/AFS.js",
6
6
  "module": "dist/AFS.esm.js",
7
7
  "browser": "dist/AFS.min.js",
8
+ "private": false,
9
+ "type": "module",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "sideEffects": false,
14
+ "homepage": "https://misits.github.io/advanced-filter-system",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/misits/advanced-filter-system.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/misits/advanced-filter-system/issues"
21
+ },
8
22
  "scripts": {
9
23
  "build": "rollup -c",
10
24
  "test": "jest",
@@ -20,7 +34,12 @@
20
34
  "javascript",
21
35
  "dom",
22
36
  "sorting",
23
- "searching"
37
+ "searching",
38
+ "frontend",
39
+ "browser",
40
+ "manipulation",
41
+ "element-filter",
42
+ "web"
24
43
  ],
25
44
  "author": "misits",
26
45
  "license": "MIT",
@@ -28,13 +47,13 @@
28
47
  "@babel/core": "^7.25.9",
29
48
  "@babel/preset-env": "^7.25.9",
30
49
  "@rollup/plugin-babel": "^6.0.4",
31
- "@rollup/plugin-commonjs": "^25.0.8",
50
+ "@rollup/plugin-commonjs": "^28.0.1",
32
51
  "@rollup/plugin-node-resolve": "^15.3.0",
33
52
  "@rollup/plugin-terser": "^0.4.4",
34
53
  "babel-jest": "^29.7.0",
35
54
  "jest": "^29.7.0",
36
55
  "jest-environment-jsdom": "^29.7.0",
37
- "rollup": "^2.79.2"
56
+ "rollup": "^4.24.0"
38
57
  },
39
58
  "directories": {
40
59
  "example": "examples",