parent
f18b452fc1
commit
d7dd4c4c32
|
After Width: | Height: | Size: 250 B |
@ -0,0 +1,88 @@ |
|||||||
|
import type { ChangeEvent, FocusEvent } from 'react' |
||||||
|
import type { Countries } from 'requests' |
||||||
|
|
||||||
|
import { |
||||||
|
useEffect, |
||||||
|
useState, |
||||||
|
useCallback, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import toLower from 'lodash/toLower' |
||||||
|
import slice from 'lodash/slice' |
||||||
|
import find from 'lodash/find' |
||||||
|
|
||||||
|
import { getCountries } from 'requests' |
||||||
|
|
||||||
|
import { matchSort } from './helpers' |
||||||
|
|
||||||
|
// временно, будем считывать из стора(контекста) лексики
|
||||||
|
const useCurrentLang = () => 'eng' |
||||||
|
|
||||||
|
const useCountriesList = () => { |
||||||
|
const [countries, setCountries] = useState<Countries>([]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getCountries().then(setCountries) |
||||||
|
}, []) |
||||||
|
|
||||||
|
return countries |
||||||
|
} |
||||||
|
|
||||||
|
const useQuery = () => { |
||||||
|
const [query, setQuery] = useState('') |
||||||
|
|
||||||
|
const onQueryChange = useCallback(({ target }: ChangeEvent<HTMLInputElement>) => { |
||||||
|
setQuery(target.value) |
||||||
|
}, []) |
||||||
|
|
||||||
|
return { |
||||||
|
onQueryChange, |
||||||
|
query, |
||||||
|
setQuery, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const isOptionClicked = (target: HTMLElement) => target?.getAttribute('role') === 'option' |
||||||
|
|
||||||
|
export const useCountrySelector = () => { |
||||||
|
const lang = useCurrentLang() |
||||||
|
const countries = useCountriesList() |
||||||
|
const { |
||||||
|
onQueryChange, |
||||||
|
query, |
||||||
|
setQuery, |
||||||
|
} = useQuery() |
||||||
|
|
||||||
|
const keyToSortBy = `name_${lang}` as 'name_eng' |
||||||
|
|
||||||
|
const results = matchSort( |
||||||
|
countries, |
||||||
|
keyToSortBy, |
||||||
|
query, |
||||||
|
) |
||||||
|
|
||||||
|
const onBlur = (event: FocusEvent<HTMLInputElement>) => { |
||||||
|
const target = event.relatedTarget as HTMLElement | null |
||||||
|
// клик по элементу списка тоже вызывает onBlur
|
||||||
|
// если кликали элемент списка то событие обрабатывается onCountrySelect
|
||||||
|
if (target && isOptionClicked(target)) return |
||||||
|
|
||||||
|
const found = find( |
||||||
|
countries, |
||||||
|
(country) => toLower(country[keyToSortBy]) === toLower(query), |
||||||
|
) |
||||||
|
setQuery(found ? found[keyToSortBy] : '') |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
countries: slice( |
||||||
|
results, |
||||||
|
0, |
||||||
|
20, |
||||||
|
), |
||||||
|
onBlur, |
||||||
|
onCountrySelect: setQuery, |
||||||
|
onQueryChange, |
||||||
|
query, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
import React from 'react' |
||||||
|
|
||||||
|
import isEmpty from 'lodash/isEmpty' |
||||||
|
import map from 'lodash/map' |
||||||
|
import { |
||||||
|
ComboboxPopover, |
||||||
|
ComboboxList, |
||||||
|
ComboboxOption, |
||||||
|
} from '@reach/combobox' |
||||||
|
import '@reach/combobox/styles.css' |
||||||
|
|
||||||
|
import { Label } from 'features/Common/Input/styled' |
||||||
|
|
||||||
|
import { useCountrySelector } from './hooks' |
||||||
|
import { |
||||||
|
ComboboxStyled, |
||||||
|
ComboboxInputStyled, |
||||||
|
Arrow, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
labelWidth?: number, |
||||||
|
} |
||||||
|
|
||||||
|
export const CountrySelector = ({ labelWidth }: Props) => { |
||||||
|
const { |
||||||
|
countries, |
||||||
|
onBlur, |
||||||
|
onCountrySelect, |
||||||
|
onQueryChange, |
||||||
|
query, |
||||||
|
} = useCountrySelector() |
||||||
|
|
||||||
|
return ( |
||||||
|
<ComboboxStyled onSelect={onCountrySelect}> |
||||||
|
<Label |
||||||
|
labelWidth={labelWidth} |
||||||
|
htmlFor='country' |
||||||
|
> |
||||||
|
Страна |
||||||
|
<Arrow /> |
||||||
|
</Label> |
||||||
|
<ComboboxInputStyled |
||||||
|
id='country' |
||||||
|
value={query} |
||||||
|
onChange={onQueryChange} |
||||||
|
onBlur={onBlur} |
||||||
|
/> |
||||||
|
{!isEmpty(countries) && ( |
||||||
|
<ComboboxPopover> |
||||||
|
<ComboboxList> |
||||||
|
{map(countries, ({ id, name_eng }) => ( |
||||||
|
<ComboboxOption |
||||||
|
key={id} |
||||||
|
value={name_eng} |
||||||
|
/> |
||||||
|
))} |
||||||
|
</ComboboxList> |
||||||
|
</ComboboxPopover> |
||||||
|
)} |
||||||
|
</ComboboxStyled> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { Combobox, ComboboxInput } from '@reach/combobox' |
||||||
|
|
||||||
|
import { wrapperStyles, inputStyles } from 'features/Common/Input/styled' |
||||||
|
|
||||||
|
export const ComboboxStyled = styled(Combobox)` |
||||||
|
${wrapperStyles} |
||||||
|
position: relative; |
||||||
|
` |
||||||
|
|
||||||
|
export const ComboboxInputStyled = styled(ComboboxInput)` |
||||||
|
${inputStyles} |
||||||
|
padding-right: 24px; |
||||||
|
` |
||||||
|
|
||||||
|
export const Arrow = styled.div` |
||||||
|
position: absolute; |
||||||
|
right: 20px; |
||||||
|
top: 50%; |
||||||
|
transform: translateY(-50%); |
||||||
|
width: 12px; |
||||||
|
height: 12px; |
||||||
|
background-image: url(/images/arrowDown.svg); |
||||||
|
background-position: center; |
||||||
|
background-repeat: no-repeat; |
||||||
|
` |
||||||
Loading…
Reference in new issue