PageSourceSearch

https://www.peakretreats.co.uk/Content/Scripts/Nxt/filtermanager/filters/plugins/DesktopOptionListPlugin.js

js peakretreats.co.uk collected 2026-10-02 11:05:24 UTC 5,238 bytes, 160 lines download raw bytes

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.