1class ReorderItems { 2 constructor(sel) { 3 this.component = document.querySelector(sel); 4 if (!this.component) return; 5 this.bttns = this.component.querySelectorAll('.title-wrapper button'); 6 this.itemsSelector = '.item' 7 8 9 this._init(); 10 } 11 _init() { 12 if (!this.bttns) return; 13 this.bttns.forEach(bttn => { 14 bttn.addEventListener('click', () => { 15 16 this.bttns.forEach(bttn => bttn.dataset.order = "neutral") 17 bttn.dataset.order = this._getNextOrder(bttn.dataset.order); 18 this.reorderItems(bttn.dataset.name, bttn.dataset.order) 19 }) 20 }) 21 } 22 reorderItems = (category, order) => { 23 if (order == 'neutral') return console.warn(`${category} order is neutral, no reorder were made`); 24 tinysort(this.itemsSelector, { data: category, order: order, natural: true }); 25 }; 26 _getNextOrder = (order) => { 27 switch (order) { 28 case 'neutral': 29 return 'asc'; 30 case 'asc': 31 return 'desc'; 32 case 'desc': 33 return 'asc'; 34 default: 35 return 'asc' 36 } 37 } 38} 39 40class CategorizeItems { 41 42 constructor() { 43 // CATEGORIES PROPERTIES 44 this.component = document.querySelector('.categorize-items'); 45 if (!this.component) return; 46 this.filter = this.component.querySelector('.categorizer, .filter'); 47 this.items = this.component.querySelectorAll('.item'); 48 this.defaultCategory = this.filter.querySelector('[data-default]')?.dataset.category 49 ? this.filter.querySelector('[data-default]')?.dataset.category 50 : this.filter.querySelector('[data-category]')?.dataset.category 51 this.activeCategory = false; 52 // CATEGORIES PROPERTIES 53 54 this.transitionTime = 500; 55 56 57 this.events = { 58 preFilter: (item) => true, 59 changed: () => { }, 60 ready: () => { }, 61 } 62 this.state = { 63 empty: () => this.setState('empty'), 64 ready: () => this.setState('ready'), 65 } 66 } 67 68 69 async init() { 70 if (this.filter) this._initFilter(); 71 await this.setState('loading'); 72 const status = this.setCategory(); 73 this.state[status](); 74 this.events.ready(); 75 } 76 77 78 async setState(state) { 79 this.component.dataset.state = state; 80 await this._sleep(this.transitionTime) 81 } 82 83 84 on(event, cb) { 85 this.events[event] = cb; 86 } 87 88 setCategoryFromUrl(value) { 89 const url = new URL(window.location); // Crear un objeto URL basado en el URL actual 90 url.searchParams.set('category', value); // Añadir o actualizar el parámetro 91 window.history.replaceState(null, '', url); // Actualizar la URL sin recargar la página 92 } 93 94 getCategoryFromUrl = () => new URLSearchParams(window.location.search).get('category') 95 96 updateState = async () => { 97 await this.setState('loading'); 98 const status = this.setCategory(this.component.dataset.category); 99 await this._sleep(100) 100 this.state[status](); 101 } 102 103 setCategory = (cat) => { 104 this.activeCategory = cat ?? this.getCategoryFromUrl() ?? this.defaultCategory; 105 106 debugger 107 108 this.setCategoryFromUrl(this.activeCategory); 109 110 this.component.dataset.category = this.activeCategory; 111 112 const state = this.updateItemsState(); 113 114 if (this.filter) this._updateFilterState(); 115 116 this.events.changed(); 117 return state; 118 } 119 120 121 // CATEGORIES METHODS 122 _initFilter() { 123 124 this.filter.addEventListener('click', async (e) => { 125 126 const isMobile = this.WW < 960; 127 128 const isSelector = 129 (isMobile && this.filter.classList.contains('selector-mobile')) 130 || (!isMobile && this.filter.classList.contains('selector-desktop')) 131 || this.filter.classList.contains('selector'); 132 133 if (isSelector) this.filter.classList.toggle('open'); 134 135 if (!e.target.matches('.category')) return; 136 137 await this.setState('loading'); 138 const status = this.setCategory(e.target.dataset.category); 139 await this._sleep(100) 140 this.state[status]();
141 142 143 144 }) 145 } 146 147 148 updateItemsState() { 149 let state = 'empty'; 150 151 this.items.forEach(item => { 152 const passedOthersFilters = this.events.preFilter(item); 153 const isActive = item.dataset.category === this.activeCategory || this.activeCategory === 'all'; 154 item.classList.toggle('active', passedOthersFilters && isActive); 155 156 if (!passedOthersFilters || !isActive) return; 157 state = 'ready'; 158 159 }); 160 return state; 161 } 162 163 164 _updateFilterState() { 165 const filters = this.filter.querySelectorAll('li'); 166 filters.forEach(item => { 167 const isActive = item.dataset.category === this.activeCategory; 168 item.classList.toggle('active', isActive); 169 170 if (!isActive) return; 171 const catName = item.querySelector('span'); 172 const mobileTitle = this.filter.querySelector('header span'); 173 if (mobileTitle) mobileTitle.innerHTML = catName.innerHTML; 174 175 }); 176 } 177 178 get WW() { return window.innerWidth } 179 _sleep = async (ms) => new Promise(resolve => setTimeout(resolve, ms)); 180 181} 182 183class RangeItems { 184 constructor(sel) { 185 // CATEGORIES PROPERTIES 186 this.component = document.querySelector(sel); 187 if (!this.component) return; 188 this.filter = this.component.querySelector('.ranger'); 189 this.items = this.component.querySelectorAll('.item'); 190 this.defaultCategory = this.filter.querySelector('[data-default]')?.dataset.category 191 ? this.filter.querySelector('[data-default]')?.dataset.category 192 : this.filter.querySelector('[data-min][data-max]')?.dataset.category 193 this.activeCategory = false; 194 this.events = { 195 changed: () => { }, 196 } 197 198 this._init(); 199 } 200 201 async _init() { 202 this.setDefaultMinMax(); 203 if (this.filter) this._initFilter(); 204 } 205 206 on(event, cb) { 207 this.events[event] = cb; 208 } 209 isInRange = (number) => { 210 const x = Number(number); 211 const min = Number(this.component.dataset.min) 212 const max = Number(this.component.dataset.max) 213 if (!max) return console.warn("Missing max range:", max); 214 return x >= min && x <= max; 215 } 216 _initFilter() { 217 218 this.filter.addEventListener('click', async (e) => { 219 220 const isMobile = this.WW < 960; 221 222 const isSelector = 223 (isMobile && this.filter.classList.contains('selector-mobile')) 224 || (!isMobile && this.filter.classList.contains('selector-desktop')) 225 || this.filter.classList.contains('selector'); 226 227 if (isSelector) this.filter.classList.toggle('open'); 228 229 if (!e.target.matches('.category')) return; 230 231 this.filter.querySelectorAll('li').forEach(filter => { 232 filter.classList.toggle('active', false) 233 }) 234 235 e.target.classList.toggle('active', true); 236 237 this.component.dataset.min = e.target.dataset.min; 238 this.component.dataset.max = e.target.dataset.max; 239 this.events.changed(); 240 }) 241 } 242 243 updateFilterState = () => { 244 const filters = this.filter.querySelectorAll('li'); 245 console.log(filters) 246 filters.forEach(item => { 247 const isActive = item.classList.contains('active'); 248 item.classList.toggle('active', isActive); 249 250 if (!isActive) return; 251 const catName = item.querySelector('span'); 252 const mobileTitle = this.filter.querySelector('header span'); 253 if (mobileTitle) mobileTitle.innerHTML = catName.innerHTML; 254 255 }); 256 } 257 setDefaultMinMax = () => { 258 const params = new URLSearchParams(window.location.search); 259 this.component.dataset.min = Number(params.get('min')) ?? 0; 260 this.component.dataset.max = Number(params.get('max')) == 0 ? Infinity : Number(params.get('max')); 261 } 262} 263 264class Availabilities { 265 266 constructor(sel) { 267 // view 268 this.page = document.querySelector(sel); 269 if (!this.page) return; 270 this.swiperSel = '.featured-swiper'; 271 this.categorizeItems = new CategorizeItems();
272 this.reorderItems = new ReorderItems('.unit-list'); 273 this.rangeItems = new RangeItems('.unit-list'); 274 if (!this.page) return; 275 276 if (this.page) { 277 this.init(); 278 } 279 } 280 281 init() { 282 if (this.page.querySelector(this.swiperSel)) this._initSwiper(); 283 if (this.page.querySelector('.unit-list')) { 284 this.categorizeItems.on('preFilter', (item) => { 285 return this.rangeItems.isInRange(item.dataset.size); 286 }) 287 this.rangeItems.on('changed', async () => { 288 this.categorizeItems.updateState(); 289 this.rangeItems.updateFilterState(); 290 }) 291 this.categorizeItems.init(); 292 }; 293 294 } 295 _initSwiper = () => { 296 const swiper = new Swiper(this.swiperSel, { 297 slidesPerView: 1.2, 298 navigation: { 299 nextEl: '.swiper-button-next', 300 prevEl: '.swiper-button-prev', 301 }, 302 breakpoints: { 303 960: { 304 slidesPerView: 4, 305 }, 306 } 307 }); 308 } 309 _initFilters = () => { 310 311 } 312 313} 314document.addEventListener('DOMContentLoaded', () => { 315 new Availabilities('.availabilities'); 316})
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.