import { createFloatingLayer, type FloatingLayerController, type FloatingLayerOptions } from "./floating.js"; export interface SelectControllerOptions extends Omit { value: T; onChange: (value: T) => void; valueTarget?: HTMLElement; } export interface SelectController extends FloatingLayerController { getValue: () => T; setValue: (value: T, emit?: boolean) => void; } export function createSelectController({ value, onChange, valueTarget, surface, trigger, ...floatingOptions }: SelectControllerOptions): SelectController { let currentValue = value; const floating = createFloatingLayer({ trigger, surface, closeOnSelect: true, ...floatingOptions, }); const options = () => Array.from(surface.querySelectorAll("[data-nodedc-option][data-nodedc-value]")); const sync = () => { options().forEach((option) => { const selected = option.dataset.nodedcValue === currentValue; option.dataset.selected = selected ? "true" : "false"; option.setAttribute("aria-selected", String(selected)); if (selected && valueTarget) valueTarget.textContent = option.dataset.nodedcLabel ?? option.textContent ?? currentValue; }); }; const setValue = (nextValue: T, emit = true) => { currentValue = nextValue; sync(); if (emit) onChange(nextValue); }; const handleOptionClick = (event: MouseEvent) => { if (!(event.target instanceof HTMLElement)) return; const option = event.target.closest("[data-nodedc-option][data-nodedc-value]"); if (!option || option.hasAttribute("disabled")) return; setValue(option.dataset.nodedcValue as T); }; surface.addEventListener("click", handleOptionClick); sync(); return { ...floating, getValue: () => currentValue, setValue, destroy: () => { surface.removeEventListener("click", handleOptionClick); floating.destroy(); }, }; }