1import { createDropdown, getUniqueDropdownMenuId } from './dropdown.js'; 2 3const createOptionsDropdownController = ({ 4 dropdown, 5 trigger, 6 menuIdFallback, 7 menuDataAttribute, 8 menuZIndexClass = 'z-60', 9 getOptions, 10 getSelectedValue, 11 onSelect, 12} = {}) => { 13 if (!(dropdown instanceof HTMLElement) || !(trigger instanceof HTMLButtonElement)) { 14 return null; 15 } 16 17 const menuId = getUniqueDropdownMenuId(trigger.getAttribute('aria-controls') || menuIdFallback); 18 const spacingValue = dropdown.getAttribute('data-dropdown-spacing'); 19 const parsedSpacing = spacingValue ? Number(spacingValue) : NaN; 20 const spacing = Number.isFinite(parsedSpacing) ? parsedSpacing : undefined; 21 const menuClassNameBase = dropdown.getAttribute('data-dropdown-content-class') || ''; 22 const itemClassName = (dropdown.getAttribute('data-dropdown-item-class') || '').trim(); 23 24 trigger.setAttribute('aria-controls', menuId); 25 26 return createDropdown({ 27 dropdown, 28 trigger, 29 menuId, 30 spacing, 31 menuClassName: `fixed ${menuZIndexClass} ${menuClassNameBase}`.trim(), 32 menuAttributes: { 33 role: 'listbox', 34 'data-select-menu': '', 35 [menuDataAttribute]: '', 36 }, 37 renderMenu: (menuEl, setOpen) => { 38 menuEl.replaceChildren(); 39 40 const selectedValue = String(getSelectedValue?.() || ''); 41 (typeof getOptions === 'function' ? getOptions() : []).forEach((option) => { 42 const item = document.createElement('button'); 43 item.type = 'button'; 44 item.tabIndex = -1; 45 item.className = option?.disabled 46 ? `${itemClassName} cursor-not-allowed text-gray hover:text-gray focus:bg-transparent focus-visible:bg-transparent` 47 : itemClassName; 48 item.textContent = String(option?.label || ''); 49 item.setAttribute('role', 'option'); 50 item.dataset.dropdownItem = ''; 51 item.dataset.selectOption = ''; 52 item.dataset.label = String(option?.label || ''); 53 54 if (String(option?.value || '') === selectedValue) { 55 item.setAttribute('aria-selected', 'true'); 56 } 57 58 if (option?.disabled) { 59 item.disabled = true; 60 item.setAttribute('aria-disabled', 'true'); 61 } else { 62 item.addEventListener('click', () => { 63 setOpen(false, { restoreFocus: true }); 64 onSelect?.(option?.value); 65 }); 66 } 67 68 menuEl.appendChild(item); 69 }); 70 71 const rect = trigger.getBoundingClientRect(); 72 menuEl.style.minWidth = `${rect.width}px`; 73 }, 74 }); 75}; 76 77export { createOptionsDropdownController };
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.