import { ComponentChildren } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';
export interface SelectOption<T extends string> {
value: T;
label: ComponentChildren;
}
export function Select<T extends string>({
value,
options,
onChange,
class: className,
disabled,
}: {
value: T;
options: SelectOption<T>[];
onChange: (v: T) => void;
class?: string;
disabled?: boolean;
}) {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const selected = options.find((o) => o.value === value);
useEffect(() => {
if (!open) return;
const handler = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) {
setOpen(false);
}
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [open]);
return (
<div ref={ref} class={'custom-select' + (className ? ' ' + className : '')}>
<button
type="button"
class={'custom-select-trigger' + (open ? ' open' : '')}
disabled={disabled}
onClick={() => !disabled && setOpen(!open)}
>
<span class="custom-select-value">{selected?.label ?? value}</span>
<svg class="custom-select-arrow" width="12" height="12" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
{open && (
<div class="custom-select-dropdown">
{options.map((o) => (
<div
key={o.value}
class={'custom-select-option' + (o.value === value ? ' selected' : '')}
onClick={() => {
onChange(o.value);
setOpen(false);
}}
>
{o.label}
{o.value === value && (
<svg class="custom-select-check" width="12" height="12" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12" />
</svg>
)}
</div>
))}
</div>
)}
</div>
);
}