export class CustomDropdown { constructor({ container, options, defaultValue, onChange, prefixIcon }) { this.container = container; this.options = options; // Array of { value, text, i18nKey } this.onChange = onChange; this.currentValue = defaultValue || options[0]?.value; this.prefixIcon = prefixIcon; // optional class name like 'fas fa-sort' this.isOpen = false; this.elements = {}; this._build(); this._bindEvents(); } _build() { this.container.innerHTML = ''; this.container.classList.add('custom-dropdown'); // Trigger Button const trigger = document.createElement('button'); trigger.className = 'custom-dropdown-trigger'; trigger.setAttribute('aria-haspopup', 'listbox'); trigger.setAttribute('aria-expanded', 'false'); trigger.type = 'button'; const triggerLeft = document.createElement('div'); triggerLeft.className = 'trigger-left-content'; triggerLeft.style.display = 'flex'; triggerLeft.style.alignItems = 'center'; triggerLeft.style.gap = '10px'; if (this.prefixIcon) { const icon = document.createElement('i'); icon.className = this.prefixIcon; triggerLeft.appendChild(icon); } const labelSpan = document.createElement('span'); labelSpan.className = 'trigger-label'; // Find default text const defaultOpt = this.options.find(opt => opt.value === this.currentValue) || this.options[0]; labelSpan.textContent = defaultOpt ? defaultOpt.text : ''; if (defaultOpt?.i18nKey) { labelSpan.setAttribute('data-tp-i18n', defaultOpt.i18nKey); } triggerLeft.appendChild(labelSpan); trigger.appendChild(triggerLeft); const arrowIcon = document.createElement('i'); arrowIcon.className = 'fas fa-chevron-down trigger-arrow'; trigger.appendChild(arrowIcon); this.container.appendChild(trigger); // Menu wrapper const menu = document.createElement('div'); menu.className = 'custom-dropdown-menu'; menu.setAttribute('role', 'listbox'); // Populate options this.options.forEach(opt => { const item = document.createElement('div'); item.className = 'custom-dropdown-item' + (opt.value === this.currentValue ? ' active' : ''); item.setAttribute('role', 'option'); item.setAttribute('data-value', opt.value); const itemText = document.createElement('span'); itemText.textContent = opt.text; if (opt.i18nKey) { itemText.setAttribute('data-tp-i18n', opt.i18nKey); } item.appendChild(itemText); menu.appendChild(item); }); this.container.appendChild(menu); this.elements = { trigger, labelSpan, menu }; } _bindEvents() { // Toggle dropdown on trigger click this.elements.trigger.addEventListener('click', (e) => { e.stopPropagation(); this.toggle(); }); // Handle item selection this.elements.menu.addEventListener('click', (e) => { const item = e.target.closest('.custom-dropdown-item'); if (!item) return; const value = item.getAttribute('data-value'); this.select(value); this.close(); }); // Close when clicking outside document.addEventListener('click', () => { this.close(); }); // Close on escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape') this.close(); }); } toggle() { if (this.isOpen) { this.close(); } else { this.open(); } } open() { // Close other custom dropdowns document.querySelectorAll('.custom-dropdown.active').forEach(dropdown => { if (dropdown !== this.container) { dropdown.classList.remove('active'); dropdown.querySelector('.custom-dropdown-trigger')?.setAttribute('aria-expanded', 'false'); } }); this.isOpen = true; this.container.classList.add('active'); this.elements.trigger.setAttribute('aria-expanded', 'true'); } close() { this.isOpen = false; this.container.classList.remove('active'); this.elements.trigger.setAttribute('aria-expanded', 'false'); } select(value) { if (value === this.currentValue) return; this.currentValue = value; // Update active class in menu items this.elements.menu.querySelectorAll('.custom-dropdown-item').forEach(item => { const isCurrent = item.getAttribute('data-value') === value; item.classList.toggle('active', isCurrent); }); // Update trigger label const selectedOpt = this.options.find(opt => opt.value === value); if (selectedOpt) { this.elements.labelSpan.textContent = selectedOpt.text; if (selectedOpt.i18nKey) { this.elements.labelSpan.setAttribute('data-tp-i18n', selectedOpt.i18nKey); } else { this.elements.labelSpan.removeAttribute('data-tp-i18n'); } } // Trigger callback if (typeof this.onChange === 'function') { this.onChange(value); } } setValue(value) { this.select(value); } getValue() { return this.currentValue; } }