1import { FilterPlugin } from '../FilterPlugin.js'; 2 3/** 4 * Desktop option list plugin 5 * Handles click-to-select filter options (string, list, etc.) 6 */ 7export class DesktopOptionListPlugin extends FilterPlugin { 8 static getPluginType() { 9 return 'desktop-option-list'; 10 } 11 12 static getDomSelector() { 13 return '[data-set-filter]'; 14 } 15 16 init() { 17 this.triggers = this.element.querySelectorAll('[data-set-filter]'); 18 this.resetTriggers = this.element.querySelectorAll('[data-reset-filter]'); 19 this.titleElement = this.element.querySelector('.filter-group-title'); 20 this.optionsContainer = this.element.querySelector('.generic-filter-options'); 21 22 // Store original title text for reset from data attribute 23 if (this.titleElement) { 24 this.originalTitle = this.titleElement.dataset.resetLabel || 25 this.titleElement.childNodes[0]?.textContent?.trim() || 26 this.titleElement.textContent.trim(); 27 } 28 29 this._bindEvents(); 30 this._updateUIFromState(); 31 } 32 33 _bindEvents() { 34 // Toggle dropdown on title click 35 if (this.titleElement) { 36 this.titleElement.addEventListener('mouseover', () => { 37 this._openDropdown(); 38 }); 39 } 40 41 // Option click 42 this.triggers.forEach(trigger => { 43 trigger.addEventListener('click', (e) => { 44 e.preventDefault(); 45 e.stopPropagation(); 46 const value = trigger.dataset.setFilter.toString(); 47 this._handleOptionClick(value); 48 }); 49 }); 50 51 // Reset click 52 this.resetTriggers.forEach(trigger => { 53 trigger.addEventListener('click', (e) => { 54 e.preventDefault(); 55 e.stopPropagation(); 56 this.resetFilter(this.config.applyMode === 'immediate'); 57 this._updateUIFromState(); 58 this._closeDropdown(); 59 }); 60 }); 61 62 // Close dropdown when clicking outside 63 this._documentClickHandler = (e) => { 64 if (!this.element.contains(e.target)) { 65 this._closeDropdown(); 66 } 67 }; 68 document.addEventListener('click', this._documentClickHandler); 69 } 70 71 _handleOptionClick(value) { 72 const currentValue = this.getCurrentValue(); 73 74 // Toggle behavior - if clicking same option, clear it 75 const newValue = currentValue === value ? '' : value; 76 77 this.updateFilter(newValue, this.config.applyMode === 'immediate'); 78 this._updateUIFromState(); 79 80 // Close the dropdown after selection 81 this._closeDropdown(); 82 } 83 84 _updateUIFromState() { 85 const currentValue = this.getCurrentValue(); 86 87 // Remove all active states 88 this.triggers.forEach(el => el.classList.remove('active')); 89 this.resetTriggers.forEach(el => el.classList.remove('active')); 90 91 if (currentValue === '') { 92 // No selection - show original title 93 this.resetTriggers.forEach(el => el.classList.add('active')); 94 this._updateTitleText(this.originalTitle); 95 } else { 96 // Find and activate the selected option 97 let selectedText = null; 98 this.triggers.forEach(el => { 99 if (el.dataset.setFilter === currentValue) { 100 el.classList.add('active'); 101 // Get the display text from the option 102 selectedText = el.textContent.trim(); 103 } 104 }); 105 106 // Update title to show selected value 107 if (selectedText) { 108 this._updateTitleText(selectedText); 109 } 110 } 111 } 112 113 /** 114 * Update the filter group title text 115 * @private 116 * @param {string} text - New text to display 117 */ 118 _updateTitleText(text) { 119 if (!this.titleElement) return; 120 121 // Preserve the chevron/icon if it exists 122 const chevron = this.titleElement.querySelector('img'); 123 124 if (chevron) { 125 // Update text node while keeping chevron 126 this.titleElement.childNodes[0].textContent = text + ' '; 127 } else { 128 // No chevron, just update text 129 this.titleElement.textContent = text; 130 } 131 } 132 133 /** 134 * Toggle the dropdown open/closed 135 * @private 136 */ 137 _openDropdown() { 138 this.element.classList.add('open'); 139 } 140 141 /** 142 * Close the dropdown 143 * @private 144 */ 145 _closeDropdown() { 146 // Remove 'open' class from parent filter group 147 this.element.classList.remove('open'); 148 } 149 150 updateUI(value) { 151 this._updateUIFromState(); 152 } 153 154 destroy() { 155 // Remove document click listener 156 if (this._documentClickHandler) { 157 document.removeEventListener('click', this._documentClickHandler); 158 } 159 } 160}
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.