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