advanced-filter-system 1.0.0

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 ADDED
@@ -0,0 +1,502 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.AFS = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ /**
8
+ * @fileoverview Advanced Filter System for DOM elements
9
+ * @version 1.0.0
10
+ *
11
+ * A flexible and customizable filtering system that supports:
12
+ * - Multiple filtering modes (OR/AND)
13
+ * - Text search with debouncing
14
+ * - Multiple sorting criteria
15
+ * - Range filtering
16
+ * - URL state management
17
+ * - Animation and transitions
18
+ * - Results counter
19
+ */
20
+
21
+ /**
22
+ * Utility function for debouncing
23
+ * @param {Function} func - Function to debounce
24
+ * @param {number} wait - Delay in milliseconds
25
+ * @returns {Function} Debounced function
26
+ */
27
+ function debounce(func, wait) {
28
+ let timeout;
29
+ return function executedFunction() {
30
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
31
+ args[_key] = arguments[_key];
32
+ }
33
+ const later = () => {
34
+ clearTimeout(timeout);
35
+ func(...args);
36
+ };
37
+ clearTimeout(timeout);
38
+ timeout = setTimeout(later, wait);
39
+ };
40
+ }
41
+ class AFS {
42
+ /**
43
+ * @typedef {Object} FilterOptions
44
+ * @property {string} containerSelector - Main container selector
45
+ * @property {string} itemSelector - Items to filter selector
46
+ * @property {string} filterButtonSelector - Filter buttons selector
47
+ * @property {string} [searchInputSelector] - Search input selector
48
+ * @property {string} [counterSelector] - Results counter selector
49
+ * @property {string} [activeClass='active'] - Active state class
50
+ * @property {string} [hiddenClass='hidden'] - Hidden state class
51
+ * @property {number} [animationDuration=300] - Animation duration in ms
52
+ * @property {string} [filterMode='OR'] - Filter mode ('OR' or 'AND')
53
+ * @property {string[]} [searchKeys=['title']] - Data attributes to search in
54
+ * @property {number} [debounceTime=300] - Search debounce delay in ms
55
+ */
56
+
57
+ /**
58
+ * @param {FilterOptions} options - Filter configuration options
59
+ */
60
+ constructor() {
61
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
62
+ this.options = {
63
+ containerSelector: ".filter-container",
64
+ itemSelector: ".filter-item",
65
+ filterButtonSelector: ".btn-filter",
66
+ searchInputSelector: ".filter-search",
67
+ counterSelector: ".filter-counter",
68
+ activeClass: "active",
69
+ hiddenClass: "hidden",
70
+ animationDuration: 300,
71
+ filterMode: "OR",
72
+ searchKeys: ["title"],
73
+ debounceTime: 300,
74
+ ...options
75
+ };
76
+
77
+ // Initialize elements
78
+ this.container = document.querySelector(this.options.containerSelector);
79
+ this.items = document.querySelectorAll(this.options.itemSelector);
80
+ this.filterButtons = document.querySelectorAll(this.options.filterButtonSelector);
81
+ this.searchInput = document.querySelector(this.options.searchInputSelector);
82
+ this.counter = document.querySelector(this.options.counterSelector);
83
+
84
+ // Initialize state
85
+ this.currentFilters = new Set(["*"]);
86
+ this.currentSearch = "";
87
+ this.visibleItems = new Set(this.items);
88
+ this.init();
89
+ }
90
+
91
+ /**
92
+ * Initialize the filter system
93
+ * @private
94
+ */
95
+ init() {
96
+ this.addStyles();
97
+ this.bindEvents();
98
+ this.loadFromURL();
99
+ this.updateCounter();
100
+ }
101
+
102
+ /**
103
+ * Add required styles to document
104
+ * @private
105
+ */
106
+ addStyles() {
107
+ const styles = `
108
+ .${this.options.hiddenClass} {
109
+ display: none !important;
110
+ }
111
+
112
+ ${this.options.itemSelector} {
113
+ opacity: 1;
114
+ transform: scale(1);
115
+ transition: opacity ${this.options.animationDuration}ms ease-out,
116
+ transform ${this.options.animationDuration}ms ease-out;
117
+ }
118
+
119
+ ${this.options.filterButtonSelector} {
120
+ opacity: 0.5;
121
+ transition: opacity ${this.options.animationDuration}ms ease;
122
+ }
123
+
124
+ ${this.options.filterButtonSelector}.${this.options.activeClass} {
125
+ opacity: 1;
126
+ }
127
+ `;
128
+ const styleSheet = document.createElement("style");
129
+ styleSheet.textContent = styles;
130
+ document.head.appendChild(styleSheet);
131
+ }
132
+
133
+ /**
134
+ * Bind all event listeners
135
+ * @private
136
+ */
137
+ bindEvents() {
138
+ this.filterButtons.forEach(button => {
139
+ button.addEventListener("click", () => this.handleFilterClick(button));
140
+ });
141
+ if (this.searchInput) {
142
+ this.searchInput.addEventListener("input", debounce(e => {
143
+ this.search(e.target.value);
144
+ }, this.options.debounceTime));
145
+ }
146
+ window.addEventListener("popstate", () => this.loadFromURL());
147
+ }
148
+
149
+ /**
150
+ * Handle filter button clicks
151
+ * @private
152
+ * @param {HTMLElement} button - Clicked filter button
153
+ */
154
+ handleFilterClick(button) {
155
+ const filterValue = button.dataset.filter;
156
+ if (filterValue === "*") {
157
+ this.resetFilters();
158
+ } else {
159
+ this.toggleFilter(filterValue, button);
160
+ }
161
+ this.filter();
162
+ this.updateURL();
163
+ }
164
+
165
+ /**
166
+ * Reset all filters to default state
167
+ * @private
168
+ */
169
+ resetFilters() {
170
+ this.filterButtons.forEach(btn => btn.classList.remove(this.options.activeClass));
171
+ this.currentFilters.clear();
172
+ this.currentFilters.add("*");
173
+ this.filterButtons[0].classList.add(this.options.activeClass);
174
+ this.resetCounter();
175
+ }
176
+
177
+ /**
178
+ * Reset visible items counter
179
+ * @private
180
+ */
181
+ resetCounter() {
182
+ this.visibleItems = new Set(this.items);
183
+ this.updateCounter();
184
+ }
185
+
186
+ /**
187
+ * Toggle individual filter state
188
+ * @private
189
+ * @param {string} filterValue - Filter value to toggle
190
+ * @param {HTMLElement} button - Filter button element
191
+ */
192
+ toggleFilter(filterValue, button) {
193
+ this.currentFilters.delete("*");
194
+ this.filterButtons[0].classList.remove(this.options.activeClass);
195
+ if (button.classList.contains(this.options.activeClass)) {
196
+ button.classList.remove(this.options.activeClass);
197
+ this.currentFilters.delete(filterValue);
198
+ if (this.currentFilters.size === 0) {
199
+ this.resetFilters();
200
+ }
201
+ } else {
202
+ button.classList.add(this.options.activeClass);
203
+ this.currentFilters.add(filterValue);
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Apply current filters to items
209
+ * @public
210
+ */
211
+ filter() {
212
+ this.visibleItems.clear(); // Start with an empty set
213
+
214
+ this.items.forEach(item => {
215
+ if (this.currentFilters.has("*")) {
216
+ this.showItem(item);
217
+ this.visibleItems.add(item); // Add visible item to the set
218
+ } else {
219
+ const itemCategories = new Set(item.dataset.categories?.split(" ") || []);
220
+ const matchesFilter = this.options.filterMode === "OR" ? this.matchesAnyFilter(itemCategories) : this.matchesAllFilters(itemCategories);
221
+ if (matchesFilter) {
222
+ this.showItem(item);
223
+ this.visibleItems.add(item); // Add visible item to the set
224
+ } else {
225
+ this.hideItem(item);
226
+ }
227
+ }
228
+ });
229
+ setTimeout(() => {
230
+ this.updateCounter();
231
+ }, this.options.animationDuration);
232
+ }
233
+
234
+ /**
235
+ * Check if item matches any active filter (OR mode)
236
+ * @private
237
+ * @param {Set} itemCategories - Item's categories
238
+ * @returns {boolean} Whether item matches any filter
239
+ */
240
+ matchesAnyFilter(itemCategories) {
241
+ return [...this.currentFilters].some(filter => {
242
+ const [type, value] = filter.split(":");
243
+ return itemCategories.has(`${type}:${value}`);
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Check if item matches all active filters (AND mode)
249
+ * @private
250
+ * @param {Set} itemCategories - Item's categories
251
+ * @returns {boolean} Whether item matches all filters
252
+ */
253
+ matchesAllFilters(itemCategories) {
254
+ return [...this.currentFilters].every(filter => {
255
+ const [type, value] = filter.split(":");
256
+ return itemCategories.has(`${type}:${value}`);
257
+ });
258
+ }
259
+
260
+ /**
261
+ * Show an item with animation
262
+ * @private
263
+ * @param {HTMLElement} item - Item to show
264
+ */
265
+ showItem(item) {
266
+ this.visibleItems.add(item);
267
+ item.classList.remove(this.options.hiddenClass);
268
+ item.style.opacity = "0";
269
+ item.style.transform = "scale(0.95)";
270
+ item.offsetHeight;
271
+ requestAnimationFrame(() => {
272
+ item.style.opacity = "1";
273
+ item.style.transform = "scale(1)";
274
+ });
275
+ }
276
+
277
+ /**
278
+ * Hide an item with animation
279
+ * @private
280
+ * @param {HTMLElement} item - Item to hide
281
+ */
282
+ hideItem(item) {
283
+ item.style.opacity = "0";
284
+ item.style.transform = "scale(0.95)";
285
+ setTimeout(() => {
286
+ if (item.style.opacity === "0") {
287
+ item.classList.add(this.options.hiddenClass);
288
+ this.visibleItems.delete(item);
289
+ }
290
+ }, this.options.animationDuration);
291
+ }
292
+
293
+ /**
294
+ * Search items by text
295
+ * @public
296
+ * @param {string} query - Search query
297
+ */
298
+ search(query) {
299
+ this.currentSearch = query.toLowerCase().trim();
300
+ this.items.forEach(item => {
301
+ const searchText = this.options.searchKeys.map(key => item.dataset[key] || "").join(" ").toLowerCase();
302
+ const matchesSearch = this.currentSearch === "" || searchText.includes(this.currentSearch);
303
+ if (matchesSearch) {
304
+ this.showItem(item);
305
+ } else {
306
+ this.hideItem(item);
307
+ }
308
+ });
309
+ this.updateURL();
310
+ setTimeout(() => {
311
+ this.updateCounter();
312
+ }, this.options.animationDuration);
313
+ }
314
+
315
+ /**
316
+ * Sort items by multiple criteria
317
+ * @public
318
+ * @param {Array<{key: string, direction: string}>} criteria - Sort criteria
319
+ */
320
+ sortMultiple(criteria) {
321
+ const items = [...this.items];
322
+ items.sort((a, b) => {
323
+ for (const criterion of criteria) {
324
+ const valueA = a.dataset[criterion.key];
325
+ const valueB = b.dataset[criterion.key];
326
+ const comparison = criterion.direction === "asc" ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA);
327
+ if (comparison !== 0) return comparison;
328
+ }
329
+ return 0;
330
+ });
331
+ items.forEach(item => this.container.appendChild(item));
332
+ }
333
+
334
+ /**
335
+ * Filter items by numeric range
336
+ * @public
337
+ * @param {string} key - Data attribute key
338
+ * @param {number} min - Minimum value
339
+ * @param {number} max - Maximum value
340
+ */
341
+ addRangeFilter(key, min, max) {
342
+ this.items.forEach(item => {
343
+ const value = parseFloat(item.dataset[key]);
344
+ const inRange = value >= min && value <= max;
345
+ if (inRange) {
346
+ this.showItem(item);
347
+ } else {
348
+ this.hideItem(item);
349
+ }
350
+ });
351
+ setTimeout(() => {
352
+ this.updateCounter();
353
+ }, this.options.animationDuration);
354
+ }
355
+
356
+ /**
357
+ * Update URL with current filter state
358
+ * @private
359
+ */
360
+ updateURL() {
361
+ const params = new URLSearchParams();
362
+
363
+ // Séparer les filtres par type
364
+ const filtersByType = {};
365
+ for (const filter of this.currentFilters) {
366
+ if (filter !== "*") {
367
+ const [type, value] = filter.split(":");
368
+ if (!filtersByType[type]) {
369
+ filtersByType[type] = new Set();
370
+ }
371
+ filtersByType[type].add(value);
372
+ }
373
+ }
374
+
375
+ // Ajouter chaque type de filtre à l'URL
376
+ Object.entries(filtersByType).forEach(_ref => {
377
+ let [type, values] = _ref;
378
+ params.set(type, Array.from(values).join(","));
379
+ });
380
+ if (this.currentSearch) {
381
+ params.set("search", this.currentSearch);
382
+ }
383
+ const newURL = `${window.location.pathname}${params.toString() ? "?" + params.toString() : ""}`;
384
+ window.history.pushState({}, "", newURL);
385
+ }
386
+
387
+ /**
388
+ * Load filter state from URL
389
+ * @private
390
+ */
391
+ loadFromURL() {
392
+ const params = new URLSearchParams(window.location.search);
393
+ this.currentFilters.clear();
394
+
395
+ // Si aucun filtre n'est présent, utiliser '*'
396
+ let hasFilters = false;
397
+
398
+ // Parcourir tous les paramètres
399
+ for (const [type, values] of params.entries()) {
400
+ if (type !== "search") {
401
+ hasFilters = true;
402
+ values.split(",").forEach(value => {
403
+ this.currentFilters.add(`${type}:${value}`);
404
+ });
405
+ }
406
+ }
407
+ if (!hasFilters) {
408
+ this.currentFilters.add("*");
409
+ }
410
+
411
+ // Update active buttons
412
+ this.filterButtons.forEach(button => {
413
+ const filterValue = button.dataset.filter;
414
+ if (this.currentFilters.has(filterValue) || filterValue === "*" && this.currentFilters.has("*")) {
415
+ button.classList.add(this.options.activeClass);
416
+ } else {
417
+ button.classList.remove(this.options.activeClass);
418
+ }
419
+ });
420
+
421
+ // Load search
422
+ const search = params.get("search") || "";
423
+ if (this.searchInput) {
424
+ this.searchInput.value = search;
425
+ }
426
+ this.filter();
427
+ if (search) {
428
+ this.search(search);
429
+ }
430
+ }
431
+
432
+ /**
433
+ * Update results counter
434
+ * @private
435
+ * @returns {{total: number, visible: number}}
436
+ */
437
+ updateCounter() {
438
+ const total = this.items.length;
439
+ const visible = this.visibleItems.size;
440
+ if (this.counter) {
441
+ this.counter.textContent = `Showing ${visible} of ${total}`;
442
+ }
443
+ return {
444
+ total,
445
+ visible
446
+ };
447
+ }
448
+
449
+ /**
450
+ * Change filter mode
451
+ * @public
452
+ * @param {string} mode - New filter mode ('OR' or 'AND')
453
+ */
454
+ setFilterMode(mode) {
455
+ if (["OR", "AND"].includes(mode.toUpperCase())) {
456
+ this.options.filterMode = mode.toUpperCase();
457
+ this.filter();
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Add filter by type and value
463
+ * @public
464
+ * @param {string} type - Filter type
465
+ * @param {string} value - Filter value
466
+ */
467
+ addFilter(type, value) {
468
+ this.currentFilters.add(`${type}:${value}`);
469
+ this.filter();
470
+ this.updateURL();
471
+ }
472
+
473
+ /**
474
+ * Remove filter by type and value
475
+ * @public
476
+ * @param {string} type - Filter type
477
+ */
478
+ removeFilter(type, value) {
479
+ this.currentFilters.delete(`${type}:${value}`);
480
+ if (this.currentFilters.size === 0) {
481
+ this.currentFilters.add("*");
482
+ }
483
+ this.filter();
484
+ this.updateURL();
485
+ }
486
+
487
+ /**
488
+ * Get active filters by type
489
+ * @public
490
+ * @param {string} type - Filter type
491
+ */
492
+ getActiveFiltersByType(type) {
493
+ return [...this.currentFilters].filter(filter => filter.startsWith(`${type}:`)).map(filter => filter.split(":")[1]);
494
+ }
495
+ }
496
+
497
+ exports.AFS = AFS;
498
+
499
+ Object.defineProperty(exports, '__esModule', { value: true });
500
+
501
+ }));
502
+ //# sourceMappingURL=AFS.js.map
@@ -0,0 +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":"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"}
@@ -0,0 +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})}));
2
+ //# sourceMappingURL=AFS.min.js.map
@@ -0,0 +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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"}