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