Fix SearchableSelect — only open on focus, close properly on blur/select
Dropdown was opening automatically on render and not closing when clicking elsewhere. Now opens only on focus/click, closes on blur, selection, Enter, Escape, and Tab. Selected value persists in the input after selection.
This commit is contained in:
@@ -41,7 +41,7 @@ const OPTION_HIGHLIGHT = {
|
||||
export default function SearchableSelect({ value, options, onChange, onClose, placeholder, autoFocus }) {
|
||||
const [filter, setFilter] = useState(value || '');
|
||||
const [highlightIdx, setHighlightIdx] = useState(-1);
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const inputRef = useRef(null);
|
||||
const listRef = useRef(null);
|
||||
|
||||
@@ -49,6 +49,7 @@ export default function SearchableSelect({ value, options, onChange, onClose, pl
|
||||
if (autoFocus && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
inputRef.current.select();
|
||||
setIsOpen(true);
|
||||
}
|
||||
}, [autoFocus]);
|
||||
|
||||
@@ -79,25 +80,31 @@ export default function SearchableSelect({ value, options, onChange, onClose, pl
|
||||
e.preventDefault();
|
||||
if (highlightIdx >= 0 && filteredOptions[highlightIdx]) {
|
||||
onChange(filteredOptions[highlightIdx]);
|
||||
setFilter(filteredOptions[highlightIdx]);
|
||||
} else if (filter.trim()) {
|
||||
onChange(filter.trim());
|
||||
}
|
||||
setIsOpen(false);
|
||||
if (onClose) onClose();
|
||||
} else if (e.key === 'Escape') {
|
||||
setIsOpen(false);
|
||||
if (onClose) onClose();
|
||||
} else if (e.key === 'Tab') {
|
||||
// Accept current value on tab
|
||||
if (highlightIdx >= 0 && filteredOptions[highlightIdx]) {
|
||||
onChange(filteredOptions[highlightIdx]);
|
||||
setFilter(filteredOptions[highlightIdx]);
|
||||
} else if (filter.trim()) {
|
||||
onChange(filter.trim());
|
||||
}
|
||||
setIsOpen(false);
|
||||
if (onClose) onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = (opt) => {
|
||||
onChange(opt);
|
||||
setFilter(opt);
|
||||
setIsOpen(false);
|
||||
if (onClose) onClose();
|
||||
};
|
||||
|
||||
@@ -106,16 +113,18 @@ export default function SearchableSelect({ value, options, onChange, onClose, pl
|
||||
<input
|
||||
ref={inputRef}
|
||||
style={{
|
||||
background: '#0F172A', border: '1px solid #7C3AED', borderRadius: isOpen ? '0.375rem 0.375rem 0 0' : '0.375rem',
|
||||
background: '#0F172A', border: isOpen ? '1px solid #7C3AED' : '1px solid #334155', borderRadius: isOpen ? '0.375rem 0.375rem 0 0' : '0.375rem',
|
||||
color: '#E2E8F0', padding: '0.3rem 0.5rem', fontSize: '0.7rem', width: '100%',
|
||||
fontFamily: "'JetBrains Mono', monospace", outline: 'none', boxSizing: 'border-box',
|
||||
}}
|
||||
value={filter}
|
||||
onChange={e => { setFilter(e.target.value); setHighlightIdx(-1); setIsOpen(true); }}
|
||||
onFocus={() => setIsOpen(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={() => {
|
||||
// Delay close so click on option can fire
|
||||
setTimeout(() => {
|
||||
setIsOpen(false);
|
||||
if (filter.trim() && filter.trim() !== value) {
|
||||
onChange(filter.trim());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user