You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
spa_instat_tv/src/features/Combobox/hooks/index.tsx

80 lines
2.0 KiB

import type { ChangeEvent, FocusEvent } from 'react'
import { useState, useCallback } from 'react'
import isUndefined from 'lodash/isUndefined'
import toLower from 'lodash/toLower'
import find from 'lodash/find'
import trim from 'lodash/trim'
import type { Props, Option } from '../types'
import { matchSort } from '../helpers'
import { useKeyboardScroll } from './useKeyboardScroll'
const isOptionClicked = (target: HTMLElement) => (
target?.getAttribute('role') === 'option'
)
const useQuery = <T extends Option>({ onChange, value }: Props<T>) => {
const [query, setQuery] = useState('')
const onQueryChange = useCallback(({ target }: ChangeEvent<HTMLInputElement>) => {
setQuery(target.value)
}, [])
return {
onQueryChange: !isUndefined(onChange) ? onChange : onQueryChange,
query: !isUndefined(value) ? value : query,
setQuery,
}
}
export const useCombobox = <T extends Option>(props: Props<T>) => {
const {
filterOptions = true,
onSelect,
options,
} = props
const {
onQueryChange,
query,
setQuery,
} = useQuery(props)
const results = matchSort(
options,
'name',
query,
)
const findOptionByName = (optionName: string) => (
find(
options,
({ name }) => toLower(name) === toLower(trim(optionName)),
) || null
)
const onOptionSelect = (option: string) => {
const selectedOption = findOptionByName(option)
setQuery(selectedOption?.name || '')
onSelect?.(selectedOption)
}
const onInputBlur = (event: FocusEvent<HTMLInputElement>) => {
const target = event.relatedTarget as HTMLElement | null
// клик по элементу списка тоже вызывает onBlur
// если кликали элемент списка то событие обрабатывает onOptionSelect
if (target && isOptionClicked(target)) return
onOptionSelect(query)
}
return {
...useKeyboardScroll(),
onInputBlur,
onOptionSelect,
onQueryChange,
options: filterOptions ? results : options,
query,
}
}