Wire autocomplete into GifPickerHeader
Adds `autocomplete` prop (GifAutocompleteState) to GifPickerHeader, restructures layout so suggestions render below the search bar, and wires up keyboard navigation + ARIA attributes for the combobox pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,34 +9,33 @@ import * as TextField from '#/components/forms/TextField'
|
|||||||
import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow'
|
import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow'
|
||||||
import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass'
|
import {MagnifyingGlass_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass'
|
||||||
import {IS_WEB} from '#/env'
|
import {IS_WEB} from '#/env'
|
||||||
|
import {
|
||||||
|
GIF_AUTOCOMPLETE_LISTBOX_ID,
|
||||||
|
GifAutocompleteSuggestions,
|
||||||
|
suggestionItemId,
|
||||||
|
} from '#/features/gifPicker/components/GifAutocompleteSuggestions'
|
||||||
|
import {type GifAutocompleteState} from '#/features/gifPicker/hooks/useGifAutocomplete'
|
||||||
|
|
||||||
export function GifPickerHeader({
|
export function GifPickerHeader({
|
||||||
inputRef,
|
inputRef,
|
||||||
onChangeText,
|
onChangeText,
|
||||||
onClose,
|
onClose,
|
||||||
onEscape,
|
onEscape,
|
||||||
|
autocomplete,
|
||||||
}: {
|
}: {
|
||||||
inputRef: Ref<TextInput>
|
inputRef: Ref<TextInput>
|
||||||
onChangeText: (text: string) => void
|
onChangeText: (text: string) => void
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onEscape: () => void
|
onEscape: () => void
|
||||||
|
autocomplete: GifAutocompleteState
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={[native(a.pt_4xl), a.relative, a.mb_lg, a.pb_sm, t.atoms.bg]}>
|
||||||
style={[
|
<View style={[a.flex_row, a.align_center, !gtMobile && web(a.gap_md)]}>
|
||||||
native(a.pt_4xl),
|
|
||||||
a.relative,
|
|
||||||
a.mb_lg,
|
|
||||||
a.flex_row,
|
|
||||||
a.align_center,
|
|
||||||
!gtMobile && web(a.gap_md),
|
|
||||||
a.pb_sm,
|
|
||||||
t.atoms.bg,
|
|
||||||
]}>
|
|
||||||
{!gtMobile && IS_WEB && (
|
{!gtMobile && IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
@@ -61,13 +60,36 @@ export function GifPickerHeader({
|
|||||||
maxLength={50}
|
maxLength={50}
|
||||||
onKeyPress={({nativeEvent}) => {
|
onKeyPress={({nativeEvent}) => {
|
||||||
if (nativeEvent.key === 'Escape') {
|
if (nativeEvent.key === 'Escape') {
|
||||||
|
if (!autocomplete.handleKeyDown('Escape')) {
|
||||||
onEscape()
|
onEscape()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
autocomplete.handleKeyDown(nativeEvent.key)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
// @ts-ignore web-only ARIA props
|
||||||
|
role={autocomplete.isVisible ? 'combobox' : undefined}
|
||||||
|
aria-controls={
|
||||||
|
autocomplete.isVisible ? GIF_AUTOCOMPLETE_LISTBOX_ID : undefined
|
||||||
|
}
|
||||||
|
aria-expanded={autocomplete.isVisible}
|
||||||
|
aria-autocomplete={autocomplete.isVisible ? 'list' : undefined}
|
||||||
|
aria-activedescendant={
|
||||||
|
autocomplete.isVisible && autocomplete.activeIndex >= 0
|
||||||
|
? suggestionItemId(autocomplete.activeIndex)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* future: tabs (Trending / Recents / Categories) render here */}
|
{autocomplete.isVisible && (
|
||||||
|
<GifAutocompleteSuggestions
|
||||||
|
suggestions={autocomplete.suggestions}
|
||||||
|
activeIndex={autocomplete.activeIndex}
|
||||||
|
onSelect={autocomplete.selectSuggestion}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user