({
isDisabled = false,
disableSelection = false,
state,
options,
isMultiSelect = false,
onUpFromFirstItem,
onDownFromLastItem,
onInputModeToggle,
inputValues,
imagesSelected = false,
onEnterImageSelection,
}: UseSelectProps<T>)
| 84 | } |
| 85 | |
| 86 | export const useSelectInput = <T>({ |
| 87 | isDisabled = false, |
| 88 | disableSelection = false, |
| 89 | state, |
| 90 | options, |
| 91 | isMultiSelect = false, |
| 92 | onUpFromFirstItem, |
| 93 | onDownFromLastItem, |
| 94 | onInputModeToggle, |
| 95 | inputValues, |
| 96 | imagesSelected = false, |
| 97 | onEnterImageSelection, |
| 98 | }: UseSelectProps<T>) => { |
| 99 | // Automatically register as an overlay when onCancel is provided. |
| 100 | // This ensures CancelRequestHandler won't intercept Escape when the select is active. |
| 101 | useRegisterOverlay('select', !!state.onCancel) |
| 102 | |
| 103 | // Determine if the focused option is an input type |
| 104 | const isInInput = useMemo(() => { |
| 105 | const focusedOption = options.find(opt => opt.value === state.focusedValue) |
| 106 | return focusedOption?.type === 'input' |
| 107 | }, [options, state.focusedValue]) |
| 108 | |
| 109 | // Core navigation via keybindings (up/down/enter/escape) |
| 110 | // When in input mode, exclude navigation/accept keybindings so that |
| 111 | // j/k/enter pass through to the TextInput instead of being intercepted. |
| 112 | const keybindingHandlers = useMemo(() => { |
| 113 | const handlers: Record<string, () => void> = {} |
| 114 | |
| 115 | if (!isInInput) { |
| 116 | handlers['select:next'] = () => { |
| 117 | if (onDownFromLastItem) { |
| 118 | const lastOption = options[options.length - 1] |
| 119 | if (lastOption && state.focusedValue === lastOption.value) { |
| 120 | onDownFromLastItem() |
| 121 | return |
| 122 | } |
| 123 | } |
| 124 | state.focusNextOption() |
| 125 | } |
| 126 | handlers['select:previous'] = () => { |
| 127 | if (onUpFromFirstItem && state.visibleFromIndex === 0) { |
| 128 | const firstOption = options[0] |
| 129 | if (firstOption && state.focusedValue === firstOption.value) { |
| 130 | onUpFromFirstItem() |
| 131 | return |
| 132 | } |
| 133 | } |
| 134 | state.focusPreviousOption() |
| 135 | } |
| 136 | handlers['select:accept'] = () => { |
| 137 | if (disableSelection === true) return |
| 138 | if (state.focusedValue === undefined) return |
| 139 | |
| 140 | const focusedOption = options.find( |
| 141 | opt => opt.value === state.focusedValue, |
| 142 | ) |
| 143 | if (focusedOption?.disabled === true) return |
no test coverage detected