(`[data-select-index="${String(activeIndex)}"]`);
el?.scrollIntoView({ block: 'nearest' });
}, [activeIndex, open]);
const moveActive = (dir: 1 | -1) => {
if (enabledIndexes.length === 0) return;
const pos = enabledIndexes.indexOf(activeIndex);
const nextPos =
pos < 0
? dir === 1
? 0
: enabledIndexes.length - 1
: (pos + dir + enabledIndexes.length) % enabledIndexes.length;
setActiveIndex(enabledIndexes[nextPos]!);
};
const commitIndex = (index: number) => {
const opt = options[index];
if (!opt || opt.disabled) return;
onChange(opt.value);
close();
triggerRef.current?.focus();
};
const onTriggerKeyDown = (e: React.KeyboardEvent) => {
if (disabled) return;
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (!open) {
openMenu();
return;
}
if (e.key === 'ArrowDown') moveActive(1);
else if (e.key === 'ArrowUp') moveActive(-1);
else if (e.key === 'Enter' || e.key === ' ') commitIndex(activeIndex);
} else if (e.key === 'Escape' && open) {
e.preventDefault();
close();
}
};
const onMenuKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'ArrowDown') {
e.preventDefault();
moveActive(1);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
moveActive(-1);
} else if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
commitIndex(activeIndex);
} else if (e.key === 'Escape') {
e.preventDefault();
close();
triggerRef.current?.focus();
} else if (e.key === 'Tab') {
close();
}
};
const triggerClass = [
styles.trigger,
open ? styles.triggerOpen : '',
className ?? '',
]
.filter(Boolean)
.join(' ');
const menu =
open && menuPos && typeof document !== 'undefined'
? createPortal(
{options.map((opt, index) => {
const selectedOpt = opt.value === value;
const active = index === activeIndex;
return (
);
})}
,
document.body,
)
: null;
return (
{menu}
);
}