Compare commits
2 Commits
develop
...
IN-146-fil
| Author | SHA1 | Date |
|---|---|---|
|
|
f1efd86f0f | 3 years ago |
|
|
9f7ea0fb6c | 3 years ago |
|
After Width: | Height: | Size: 696 B |
|
After Width: | Height: | Size: 717 B |
@ -0,0 +1,288 @@ |
||||
import { |
||||
useCallback, |
||||
useEffect, |
||||
useMemo, |
||||
useState, |
||||
MouseEvent, |
||||
ChangeEvent, |
||||
} from 'react' |
||||
|
||||
import every from 'lodash/every' |
||||
import some from 'lodash/some' |
||||
import isNil from 'lodash/isNil' |
||||
import includes from 'lodash/includes' |
||||
import filter from 'lodash/filter' |
||||
import isEmpty from 'lodash/isEmpty' |
||||
import reduce from 'lodash/reduce' |
||||
import size from 'lodash/size' |
||||
|
||||
import { format } from 'date-fns' |
||||
|
||||
import { |
||||
getAge, |
||||
getArena, |
||||
getDivision, |
||||
getGender, |
||||
getMainTeam, |
||||
getRound, |
||||
getSport, |
||||
} from 'features/HomePage/components/Dropdown/helpers' |
||||
|
||||
import { |
||||
getHomeMatches, |
||||
Match, |
||||
TQueryParams, |
||||
} from 'requests' |
||||
import type { TournamentType } from 'requests/getMatches/types' |
||||
|
||||
const MATCHES_LIMIT = 1000 |
||||
const OFFSET = 0 |
||||
|
||||
const getTimezoneOffset = (date: Date) => { |
||||
const offset = date.getTimezoneOffset() |
||||
if (offset === 0) return offset |
||||
return -(offset) |
||||
} |
||||
const getDate = (date: Date) => format(date, 'yyyy-MM-dd') |
||||
|
||||
export type TActiveFilters = { |
||||
[key: string]: Array<number>, |
||||
} |
||||
|
||||
export type TDefaultType = { |
||||
id: number, |
||||
name_eng: string, |
||||
name_rus: string, |
||||
} |
||||
|
||||
type TUseMatchFilters = { |
||||
selectedDate: Date, |
||||
selectedTournament: TournamentType | null, |
||||
} |
||||
|
||||
const DEFAULT_FILTERS = {} |
||||
|
||||
export const useMatchFilters = ({ |
||||
selectedDate, |
||||
selectedTournament, |
||||
}: TUseMatchFilters) => { |
||||
const [matchesList, setMatchesList] = useState<Array<Match>>([]) |
||||
const [tournamentMatches, setTournamentMatches] = useState<Array<Match>>([]) |
||||
const [filtersListName, setFiltersListName] = useState<string>('') |
||||
const [activeFilters, setActiveFilters] = useState<TActiveFilters>(DEFAULT_FILTERS) |
||||
const [inputValue, setInputValue] = useState<string>('') |
||||
const [openPopup, setOpenPopup] = useState(false) |
||||
|
||||
const toggleDropdownList = (title: string) => () => { |
||||
if (title === filtersListName) { |
||||
setFiltersListName('') |
||||
setInputValue('') |
||||
} else { |
||||
setFiltersListName(title) |
||||
} |
||||
} |
||||
|
||||
const closeDropdownList = () => { |
||||
setFiltersListName('') |
||||
setInputValue('') |
||||
} |
||||
|
||||
const clearAllFilters = () => { |
||||
setInputValue('') |
||||
setActiveFilters(DEFAULT_FILTERS) |
||||
} |
||||
|
||||
const fetchMatches = useCallback( |
||||
(limit: number, offset: number) => getHomeMatches({ |
||||
date: getDate(selectedDate), |
||||
limit, |
||||
offset, |
||||
timezoneOffset: getTimezoneOffset(selectedDate), |
||||
}), |
||||
[selectedDate], |
||||
) |
||||
|
||||
useEffect(() => { |
||||
clearAllFilters() |
||||
setMatchesList([]) |
||||
setTournamentMatches([]) |
||||
|
||||
fetchMatches(MATCHES_LIMIT, OFFSET).then(({ broadcast }) => { |
||||
setMatchesList(broadcast) |
||||
}) |
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedDate]) |
||||
|
||||
useEffect(() => { |
||||
if (selectedTournament) { |
||||
setTournamentMatches( |
||||
filter(matchesList, |
||||
(match) => match.tournament.id === selectedTournament.id), |
||||
) |
||||
} |
||||
}, [ |
||||
matchesList, |
||||
selectedTournament, |
||||
]) |
||||
|
||||
const matchesForFilters = useMemo(() => ( |
||||
selectedTournament ? tournamentMatches : matchesList), |
||||
[ |
||||
matchesList, |
||||
selectedTournament, |
||||
tournamentMatches, |
||||
]) |
||||
|
||||
const getDropdownList = useCallback((filterName: string, queryString: string) => { |
||||
switch (filterName) { |
||||
case 'gender': |
||||
return getGender(matchesForFilters) |
||||
case 'youth_age': |
||||
return getAge(matchesForFilters) |
||||
case 'division': |
||||
return getDivision(matchesForFilters) |
||||
case 'main_team': |
||||
return getMainTeam(matchesForFilters) |
||||
case 'arena': |
||||
return getArena(matchesForFilters, queryString) |
||||
case 'round': |
||||
return getRound(matchesForFilters) |
||||
case 'sport': |
||||
return getSport(matchesForFilters) |
||||
default: |
||||
return [] |
||||
} |
||||
}, [matchesForFilters]) |
||||
|
||||
const currentFilters = useMemo(() => { |
||||
const arr = [] |
||||
|
||||
const hasArena = some(matchesForFilters, (item) => !isNil(item.arena?.id)) |
||||
const hasRound = some(matchesForFilters, (item) => !isNil(item.round?.id)) |
||||
const hasSport = some(matchesForFilters, (item) => !isNil(item.sport)) |
||||
const hasMainTeam = some(matchesForFilters, |
||||
(item) => !isNil(item.team1?.main_team) || !isNil(item.team2?.main_team)) |
||||
const hasGender = some(matchesForFilters, |
||||
(item) => !isNil(item.team1?.gender) || !isNil(item.team2?.gender)) |
||||
const hasDivision = some(matchesForFilters, |
||||
(item) => !isNil(item.team1?.division?.id) || !isNil(item.team2?.division?.id)) |
||||
const hasYouthAge = some(matchesForFilters, |
||||
(item) => !isNil(item.team1?.youth_age) || !isNil(item.team2?.youth_age)) |
||||
|
||||
if (hasSport) arr.push('sport') |
||||
if (hasArena) arr.push('arena') |
||||
if (hasRound) arr.push('round') |
||||
if (hasMainTeam) arr.push('main_team') |
||||
if (hasYouthAge) arr.push('youth_age') |
||||
if (hasGender) arr.push('gender') |
||||
if (hasDivision) arr.push('division') |
||||
|
||||
return filter(arr, (filterName) => ( |
||||
getDropdownList(filterName, '').length > 1 |
||||
)) |
||||
}, [getDropdownList, matchesForFilters]) |
||||
|
||||
const handleSetFilters = (filterName: string, value: number) => { |
||||
const activeFilter = activeFilters[filterName as keyof typeof activeFilters] |
||||
const isFilterPresent = !isNil(activeFilter) |
||||
|
||||
if (isFilterPresent) { |
||||
const isValuePresent = includes(activeFilter, value) |
||||
const currentValue = isValuePresent |
||||
? filter(activeFilter, (item) => item !== value) |
||||
: [...activeFilter, value] |
||||
|
||||
return setActiveFilters({ |
||||
...activeFilters, |
||||
[filterName]: currentValue, |
||||
}) |
||||
} |
||||
|
||||
return setActiveFilters({ |
||||
...activeFilters, |
||||
[filterName]: [value], |
||||
}) |
||||
} |
||||
|
||||
const clearFilters = (filterName: string) => (e: MouseEvent | ChangeEvent<HTMLInputElement>) => { |
||||
e.stopPropagation() |
||||
e.preventDefault() |
||||
|
||||
if (filterName === 'arena') { |
||||
setInputValue('') |
||||
} |
||||
|
||||
setActiveFilters({ |
||||
...activeFilters, |
||||
[filterName]: [], |
||||
}) |
||||
} |
||||
|
||||
const isEmptyFilters = every(activeFilters, (filterItem) => isEmpty(filterItem)) |
||||
|
||||
const queryParams: TQueryParams = useMemo(() => { |
||||
const params = { |
||||
arena: activeFilters.arena, |
||||
division: activeFilters.division, |
||||
gender: activeFilters.gender, |
||||
main_team: activeFilters.main_team, |
||||
round: activeFilters.round, |
||||
sport: activeFilters.sport, |
||||
youth_age: activeFilters.youth_age, |
||||
} |
||||
|
||||
return params |
||||
}, [activeFilters]) |
||||
|
||||
const changeInput = (e: ChangeEvent<HTMLInputElement>) => { |
||||
const { value } = e.target |
||||
setInputValue(value) |
||||
} |
||||
|
||||
const clickClearAll = () => { |
||||
setOpenPopup(true) |
||||
} |
||||
|
||||
const confirmClear = (e: MouseEvent) => { |
||||
setOpenPopup(false) |
||||
clearAllFilters() |
||||
} |
||||
|
||||
const clickCancel = () => { |
||||
setOpenPopup(false) |
||||
} |
||||
|
||||
const filtersSize = reduce(activeFilters, |
||||
( |
||||
result, |
||||
value, |
||||
) => result + size(value), 0) |
||||
|
||||
const isFiltersShown = useMemo(() => ( |
||||
size(matchesList) < 12 || selectedTournament), |
||||
[matchesList, selectedTournament]) |
||||
|
||||
return { |
||||
activeFilters, |
||||
changeInput, |
||||
clearAllFilters, |
||||
clearFilters, |
||||
clickCancel, |
||||
clickClearAll, |
||||
closeDropdownList, |
||||
confirmClear, |
||||
currentFilters, |
||||
filtersListName, |
||||
filtersSize, |
||||
getDropdownList, |
||||
handleSetFilters, |
||||
inputValue, |
||||
isEmptyFilters, |
||||
isFiltersShown, |
||||
matchesList, |
||||
openPopup, |
||||
queryParams, |
||||
setTournamentMatches, |
||||
toggleDropdownList, |
||||
} |
||||
} |
||||
@ -1,6 +0,0 @@ |
||||
import { atom } from 'recoil' |
||||
|
||||
export const isSportFilterShownAtom = atom({ |
||||
default: true, |
||||
key: 'isSportFilterShownAtom', |
||||
}) |
||||
@ -0,0 +1,41 @@ |
||||
import { useHeaderFiltersStore } from 'features/HeaderFilters' |
||||
import { T9n } from 'features/T9n' |
||||
|
||||
import { |
||||
ButtonsContainer, |
||||
CancelButton, |
||||
ConfirmButton, |
||||
Modal, |
||||
PopupContainer, |
||||
PopupText, |
||||
PopupTitle, |
||||
} from './styled' |
||||
|
||||
export const ClearFiltersPopup = () => { |
||||
const { |
||||
clickCancel, |
||||
confirmClear, |
||||
openPopup, |
||||
} = useHeaderFiltersStore() |
||||
|
||||
return ( |
||||
<Modal isOpen={openPopup} withCloseButton={false}> |
||||
<PopupContainer> |
||||
<PopupTitle> |
||||
<T9n t='clear_filters' /> |
||||
</PopupTitle> |
||||
<PopupText> |
||||
<T9n t='clear_popup_message' /> |
||||
</PopupText> |
||||
<ButtonsContainer> |
||||
<ConfirmButton onClick={confirmClear}> |
||||
<T9n t='clear' /> |
||||
</ConfirmButton> |
||||
<CancelButton onClick={clickCancel}> |
||||
<T9n t='still_cancel' /> |
||||
</CancelButton> |
||||
</ButtonsContainer> |
||||
</PopupContainer> |
||||
</Modal> |
||||
) |
||||
} |
||||
@ -0,0 +1,120 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { ModalWindow } from 'features/Modal/styled' |
||||
import { OutsideClickWrapper } from 'features/OutsideClick' |
||||
import { |
||||
Modal as BaseModal, |
||||
} from 'features/AuthServiceApp/components/RegisterPopup/styled' |
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
export const Modal = styled(BaseModal)` |
||||
${OutsideClickWrapper} { |
||||
${isMobileDevice |
||||
? css`height: 100%;` |
||||
: ''} |
||||
} |
||||
|
||||
${ModalWindow} { |
||||
${isMobileDevice |
||||
? css` |
||||
min-height: 201px; |
||||
max-width: 351px; |
||||
padding: 26px 15px 33px 15px; |
||||
top: 19vh; |
||||
` |
||||
: css` |
||||
min-height: 220px; |
||||
max-width: 611px; |
||||
padding: 37px 0 39px 0; |
||||
`}
|
||||
} |
||||
` |
||||
|
||||
export const PopupContainer = styled.div` |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
justify-content: center; |
||||
font-style: normal; |
||||
color: #FFFFFF; |
||||
|
||||
${isMobileDevice |
||||
? '' |
||||
: css`min-width: 611px;`} |
||||
` |
||||
|
||||
export const PopupTitle = styled.div` |
||||
font-weight: 700; |
||||
font-size: 24px; |
||||
line-height: 24px; |
||||
margin-bottom: 45px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 20px; |
||||
line-height: 24px; |
||||
margin-bottom: 25px;` |
||||
: css` |
||||
font-size: 24px; |
||||
line-height: 24px; |
||||
margin-bottom: 45px;`}
|
||||
` |
||||
|
||||
export const PopupText = styled.div` |
||||
font-weight: 400; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 16px; |
||||
line-height: 22px; |
||||
margin-bottom: 33px;` |
||||
: css` |
||||
font-size: 20px; |
||||
line-height: 28px; |
||||
margin-bottom: 55px;`}
|
||||
` |
||||
|
||||
export const ButtonsContainer = styled.div` |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
|
||||
${isMobileDevice |
||||
? css`gap: 15px;` |
||||
: css`gap: 20px;`} |
||||
` |
||||
|
||||
const Button = styled.button` |
||||
display: flex; |
||||
flex-direction: row; |
||||
justify-content: center; |
||||
align-items: center; |
||||
border-radius: 5px; |
||||
color: #FFFF; |
||||
font-weight: 600; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 16px; |
||||
line-height: 16px; |
||||
width: 149px; |
||||
height: 38px;` |
||||
: css` |
||||
font-size: 20px; |
||||
line-height: 50px; |
||||
cursor: pointer; |
||||
width: 134px; |
||||
height: 50px;`}
|
||||
` |
||||
|
||||
export const ConfirmButton = styled(Button)` |
||||
background: #294FC4; |
||||
border: 1px solid #294FC4; |
||||
filter: drop-shadow(0px 1px 1px rgba(0, 0, 0, 0.3)); |
||||
` |
||||
|
||||
export const CancelButton = styled(Button)` |
||||
border: 1px solid #FFFFFF; |
||||
background: none; |
||||
filter: drop-shadow(0px 1px 1px rgba(0, 0, 0, 0.3)); |
||||
` |
||||
@ -0,0 +1,74 @@ |
||||
import map from 'lodash/map' |
||||
import some from 'lodash/some' |
||||
import filter from 'lodash/filter' |
||||
import uniq from 'lodash/uniq' |
||||
import uniqBy from 'lodash/uniqBy' |
||||
import flatten from 'lodash/flatten' |
||||
import compact from 'lodash/compact' |
||||
import includes from 'lodash/includes' |
||||
import toLower from 'lodash/toLower' |
||||
|
||||
import { Match } from 'requests' |
||||
|
||||
const checkString = (text: string, searchString: string) => includes( |
||||
toLower(text), toLower(searchString), |
||||
) |
||||
|
||||
export const getAge = (filterList: Array<Match>) => compact( |
||||
uniq( |
||||
flatten( |
||||
map( |
||||
filterList, (item) => [item.team1.youth_age, item.team2.youth_age], |
||||
), |
||||
), |
||||
), |
||||
) |
||||
|
||||
export const getDivision = (filterList: Array<Match>) => filter( |
||||
flatten( |
||||
map( |
||||
filterList, (item) => [item.team1.division, item.team2.division], |
||||
), |
||||
), (item) => item?.id, |
||||
) |
||||
|
||||
export const getMainTeam = (filterList: Array<Match>) => filter( |
||||
flatten( |
||||
map( |
||||
filterList, (item) => [item.team1.main_team, item.team2.main_team], |
||||
), |
||||
), (item) => item?.id, |
||||
) |
||||
|
||||
export const getArena = (filterList: Array<Match>, inputValue: string) => filter( |
||||
map( |
||||
filterList, (item) => item.arena, |
||||
), (item) => item?.id |
||||
&& (checkString(item.name_eng, inputValue) |
||||
|| checkString(item.name_rus, inputValue)), |
||||
) |
||||
|
||||
export const getSport = (filterList: Array<Match>) => uniqBy( |
||||
filter( |
||||
map( |
||||
filterList, (item) => item.sport_info, |
||||
), (item) => item?.id, |
||||
), 'id', |
||||
) |
||||
|
||||
export const getRound = (filterList: Array<Match>) => uniqBy( |
||||
filter( |
||||
map( |
||||
filterList, (item) => item.round, |
||||
), (item) => item?.id, |
||||
), 'id', |
||||
) |
||||
|
||||
export const getGender = (filterList: Array<Match>) => { |
||||
const list = [] |
||||
|
||||
if (some(filterList, (item) => item.team1.gender === 1 || item.team2.gender === 1)) list.push({ id: 1, lexic: 'gender_male_long' }) |
||||
if (some(filterList, (item) => item.team1.gender === 2 || item.team2.gender === 2)) list.push({ id: 2, lexic: 'gender_female_long' }) |
||||
|
||||
return list |
||||
} |
||||
@ -0,0 +1,127 @@ |
||||
import { ChangeEvent, MouseEvent } from 'react' |
||||
|
||||
import map from 'lodash/map' |
||||
import isEmpty from 'lodash/isEmpty' |
||||
import includes from 'lodash/includes' |
||||
import isNumber from 'lodash/isNumber' |
||||
|
||||
import { T9n } from 'features/T9n' |
||||
import { Name } from 'features/Name' |
||||
import { useLexicsStore } from 'features/LexicsStore' |
||||
import { useHeaderFiltersStore } from 'features/HeaderFilters' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { |
||||
TDropDownProps, |
||||
TFilterItem, |
||||
TItem, |
||||
} from './types' |
||||
import { |
||||
DropDownContainer, |
||||
FiltersList, |
||||
FilterItem, |
||||
Checkbox, |
||||
CommonButtonsBlock, |
||||
ClearButtonContainer, |
||||
SearchWithAllContainer, |
||||
SearchContainer, |
||||
SearchInput, |
||||
BackButton, |
||||
} from './styled' |
||||
import { Title } from '../MatchesFilters/styled' |
||||
|
||||
const Label = ({ item }: TFilterItem) => ( |
||||
isNumber(item) ? <>U{item}</> : <Name nameObj={item} /> |
||||
) |
||||
|
||||
export const DropDown = ({ |
||||
activeFilters, |
||||
changeInput, |
||||
clearFilters, |
||||
closeDropdownList, |
||||
filterTitle, |
||||
inputValue, |
||||
setFilters, |
||||
}: TDropDownProps) => { |
||||
const { translate } = useLexicsStore() |
||||
const { getDropdownList } = useHeaderFiltersStore() |
||||
|
||||
const dropdownList = getDropdownList(filterTitle, inputValue) as Array<TItem | number> |
||||
|
||||
const currentActiveFilter = activeFilters[filterTitle as keyof typeof activeFilters] |
||||
|
||||
return ( |
||||
<DropDownContainer> |
||||
<FiltersList> |
||||
<CommonButtonsBlock isArena={filterTitle === 'arena'}> |
||||
<SearchWithAllContainer> |
||||
{isMobileDevice && closeDropdownList |
||||
&& ( |
||||
<BackButton onClick={closeDropdownList} className='back_button'> |
||||
{isMobileDevice && (<Title t={filterTitle} />)} |
||||
</BackButton> |
||||
)} |
||||
{filterTitle === 'arena' && ( |
||||
<SearchContainer> |
||||
<SearchInput |
||||
onChange={changeInput} |
||||
onClick={(e) => e.stopPropagation()} |
||||
placeholder={translate('search')} |
||||
value={inputValue} |
||||
/> |
||||
</SearchContainer> |
||||
)} |
||||
<Checkbox |
||||
onChange={clearFilters(filterTitle)} |
||||
checked={isEmpty(currentActiveFilter)} |
||||
labelLexic='all' |
||||
/> |
||||
</SearchWithAllContainer> |
||||
{!isEmpty(currentActiveFilter) && ( |
||||
<ClearButtonContainer |
||||
onClick={clearFilters(filterTitle)} |
||||
> |
||||
<T9n |
||||
className='clear_button' |
||||
t='clear' |
||||
/> |
||||
</ClearButtonContainer> |
||||
)} |
||||
</CommonButtonsBlock> |
||||
|
||||
{map(dropdownList, (filterItem) => { |
||||
const filterValue = isNumber(filterItem) ? filterItem : filterItem.id |
||||
const handleClick = (e: ChangeEvent | MouseEvent) => { |
||||
e.preventDefault() |
||||
e.stopPropagation() |
||||
setFilters(filterTitle, filterValue) |
||||
} |
||||
|
||||
return ( |
||||
<FilterItem |
||||
key={filterValue} |
||||
onClick={handleClick} |
||||
> |
||||
{includes(['gender', 'sport'], filterTitle) |
||||
? ( |
||||
<Checkbox |
||||
checked={includes(currentActiveFilter, filterValue)} |
||||
onChange={handleClick} |
||||
labelLexic={!isNumber(filterItem) ? filterItem.lexic : ''} |
||||
/> |
||||
) |
||||
: ( |
||||
<Checkbox |
||||
checked={includes(currentActiveFilter, filterValue)} |
||||
onChange={handleClick} |
||||
label={(<Label item={filterItem} />)} |
||||
/> |
||||
)} |
||||
</FilterItem> |
||||
) |
||||
})} |
||||
</FiltersList> |
||||
</DropDownContainer> |
||||
) |
||||
} |
||||
@ -0,0 +1,234 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { customScrollbar } from 'features/Common' |
||||
import { Checkbox as BaseCheckbox } from 'features/Common/Checkbox' |
||||
import { Label } from 'features/Common/Checkbox/styled' |
||||
import { CheckboxSvg } from 'features/Common/Checkbox/Icon' |
||||
import { isMobileDevice } from 'config/userAgent' |
||||
import { NameStyled } from 'features/Name' |
||||
|
||||
export const DropDownContainer = styled.div` |
||||
${isMobileDevice |
||||
? css` border-top: 1px solid #505050;` |
||||
: css` |
||||
position: absolute; |
||||
top: 57px; |
||||
right: -24px; |
||||
background: #333333; |
||||
border-radius: 3.5px; |
||||
z-index: 10;`}
|
||||
` |
||||
|
||||
export const FiltersList = styled.ul` |
||||
overflow-y: auto; |
||||
display: flex; |
||||
flex-direction: column; |
||||
max-height: 500px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
@media (orientation: landscape){ |
||||
max-height: 300px; |
||||
} |
||||
` |
||||
: ''} |
||||
|
||||
${customScrollbar} |
||||
` |
||||
|
||||
export const FilterItem = styled.li` |
||||
${isMobileDevice |
||||
? css` |
||||
max-width: 280px; |
||||
white-space: nowrap; |
||||
padding: 10px 0 10px 13px; |
||||
` |
||||
: css` |
||||
min-width: 286px; |
||||
white-space: nowrap; |
||||
padding: 15px 26px;`}
|
||||
|
||||
:hover { |
||||
background: rgba(255, 255, 255, 0.2); |
||||
} |
||||
` |
||||
|
||||
export const Checkbox = styled(BaseCheckbox)` |
||||
display: block; |
||||
text-transform: uppercase; |
||||
|
||||
${Label} { |
||||
text-transform: uppercase; |
||||
font-weight: 700; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 10px; |
||||
line-height: 11px;` |
||||
: css` |
||||
font-size: 18px; |
||||
line-height: 16px;`}
|
||||
|
||||
${NameStyled} { |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
white-space: nowrap; |
||||
} |
||||
|
||||
${({ checked }) => (checked |
||||
? '' |
||||
: css`color: rgba(255, 255, 255, 0.6);`)} |
||||
} |
||||
|
||||
${CheckboxSvg} { |
||||
margin-right: 8px; |
||||
flex: 0 0 auto; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
width: 14px; |
||||
height: 14px; |
||||
` |
||||
: css` |
||||
width: 20px; |
||||
height: 20px;`}
|
||||
} |
||||
` |
||||
|
||||
type TCommonButtonsBlock = { |
||||
isArena?: boolean, |
||||
} |
||||
|
||||
export const CommonButtonsBlock = styled.div<TCommonButtonsBlock>` |
||||
display: flex; |
||||
justify-content: space-between; |
||||
|
||||
${({ isArena }) => (isArena |
||||
? css` |
||||
align-items: flex-start; |
||||
padding: ${isMobileDevice ? '8px 25px 10px 13px' : '24px 26px 15px 26px'}; |
||||
` |
||||
: css` |
||||
align-items: center; |
||||
padding:${isMobileDevice ? '8px 25px 10px 13px' : '15px 26px'};`)}
|
||||
` |
||||
|
||||
export const ClearButtonContainer = styled.div` |
||||
${isMobileDevice |
||||
? css` |
||||
position: absolute; |
||||
top: 14px; |
||||
right: 36px;` |
||||
: ''} |
||||
|
||||
.clear_button { |
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 10px; |
||||
line-height: 12px;` |
||||
: css` |
||||
font-size: 18px; |
||||
line-height: 22px;`}
|
||||
|
||||
font-style: normal; |
||||
font-weight: 400; |
||||
letter-spacing: 0.05em; |
||||
text-transform: uppercase; |
||||
color: rgba(255, 255, 255, 0.5); |
||||
cursor: pointer; |
||||
} |
||||
` |
||||
|
||||
export const SearchContainer = styled.div` |
||||
padding-right: ${isMobileDevice ? '0px' : '20px'}; |
||||
position: relative; |
||||
|
||||
:before { |
||||
content: ''; |
||||
background: url(/images/search.svg) no-repeat; |
||||
position: absolute; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
left: 8px; |
||||
top: 7px; |
||||
width: 11px; |
||||
height: 11px; |
||||
` |
||||
: css` |
||||
left: 13px; |
||||
top: 10px; |
||||
width: 16px; |
||||
height: 16px;`}
|
||||
|
||||
z-index: 2; |
||||
} |
||||
` |
||||
export const SearchInput = styled.input` |
||||
border: none; |
||||
background: transparent; |
||||
outline: none; |
||||
width: 100%; |
||||
background: #292929; |
||||
border-radius: 10px; |
||||
margin-bottom: 15px; |
||||
color: #FFFF; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
height: 24px; |
||||
min-width: 242px; |
||||
padding-left: 23px;` |
||||
: css` |
||||
min-width: 513px; |
||||
padding-left: 36px; |
||||
height: 36px;`}
|
||||
|
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 10px; |
||||
line-height: 15px;` |
||||
: css` |
||||
font-size: 18px; |
||||
line-height: 22px;`}
|
||||
|
||||
::placeholder { |
||||
text-transform: uppercase; |
||||
letter-spacing: 0.05em; |
||||
} |
||||
` |
||||
|
||||
export const SearchWithAllContainer = styled.div` |
||||
flex: 1 1 auto; |
||||
` |
||||
|
||||
export const BackButton = styled.div` |
||||
font-style: normal; |
||||
font-weight: 700; |
||||
font-size: 10px; |
||||
line-height: 12px; |
||||
align-items: center; |
||||
letter-spacing: 0.05em; |
||||
text-transform: uppercase; |
||||
color: #FFFFFF; |
||||
cursor: pointer; |
||||
position: absolute; |
||||
top: 12px; |
||||
left: 13px; |
||||
padding-left: 24px; |
||||
|
||||
:before { |
||||
content: ''; |
||||
display: block; |
||||
background: url(/images/arrowUpWhite.svg) center no-repeat; |
||||
background-size: 12px 12xp; |
||||
transform: rotate(-90deg); |
||||
right: 0; |
||||
width: 12px; |
||||
height: 12px; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
} |
||||
` |
||||
@ -0,0 +1,23 @@ |
||||
import { MouseEvent, ChangeEvent } from 'react' |
||||
import { TActiveFilters } from 'features/HeaderFilters/store/hooks/useMatchFilters' |
||||
|
||||
export type TDropDownProps = { |
||||
activeFilters: TActiveFilters, |
||||
changeInput: (e: ChangeEvent<HTMLInputElement>) => void, |
||||
clearFilters: (filterName: string) => (e: MouseEvent | ChangeEvent<HTMLInputElement>) => void, |
||||
closeDropdownList?: () => void, |
||||
filterTitle: string, |
||||
inputValue: string, |
||||
setFilters: (filterName: string, value: number) => void, |
||||
} |
||||
|
||||
export type TItem = { |
||||
id: number, |
||||
lexic?: string, |
||||
name_eng: string, |
||||
name_rus: string, |
||||
} |
||||
|
||||
export type TFilterItem = { |
||||
item: TItem | number, |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
import join from 'lodash/join' |
||||
import take from 'lodash/take' |
||||
import size from 'lodash/size' |
||||
import { TDefaultType } from 'features/HeaderFilters/store/hooks/useMatchFilters' |
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
const TEXT_LENGTH = isMobileDevice ? 6 : 7 |
||||
|
||||
export const truncateString = (text: string) => ( |
||||
size(text) <= TEXT_LENGTH ? text : `${join(take(text, TEXT_LENGTH), '')}...`) |
||||
|
||||
export const checkSize = (filtersList: Array<TDefaultType> | Array<Number> | undefined) => ( |
||||
(filtersList && size(filtersList) > 2) |
||||
) |
||||
@ -0,0 +1,130 @@ |
||||
import map from 'lodash/map' |
||||
import isNil from 'lodash/isNil' |
||||
import size from 'lodash/size' |
||||
import find from 'lodash/find' |
||||
import includes from 'lodash/includes' |
||||
|
||||
import { useName } from 'features/Name' |
||||
import { T9n } from 'features/T9n' |
||||
import { useHeaderFiltersStore } from 'features/HeaderFilters' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { |
||||
FilterContainer, |
||||
FiltersCount, |
||||
MatchFiltersContainer, |
||||
ActiveFilters, |
||||
ActiveFilter, |
||||
CloseButton, |
||||
} from './styled' |
||||
import { DropDown } from '../Dropdown' |
||||
import { TItem } from '../Dropdown/types' |
||||
import { checkSize, truncateString } from './helpers' |
||||
import { Title } from '../MatchesFilters/styled' |
||||
|
||||
type TProps = { |
||||
dropdownList: Array<TItem | number>, |
||||
filterTitle: string, |
||||
item: number, |
||||
} |
||||
|
||||
const ActiveFilterText = ({ |
||||
dropdownList, |
||||
filterTitle, |
||||
item, |
||||
}: TProps) => { |
||||
const currentItem = find(dropdownList, { id: item }) as TItem |
||||
const name = useName(currentItem) |
||||
|
||||
if (includes(['gender', 'sport'], filterTitle)) return <T9n t={currentItem.lexic!} /> |
||||
|
||||
return ( |
||||
<> |
||||
{truncateString(name)} |
||||
</> |
||||
) |
||||
} |
||||
type TFiltersProps = { |
||||
isMobile?: boolean, |
||||
} |
||||
|
||||
export const MatchesFilters = ({ isMobile }: TFiltersProps) => { |
||||
const { |
||||
activeFilters, |
||||
changeInput, |
||||
clearFilters, |
||||
currentFilters, |
||||
filtersListName, |
||||
filtersSize, |
||||
getDropdownList, |
||||
handleSetFilters, |
||||
inputValue, |
||||
matchesList, |
||||
toggleDropdownList, |
||||
} = useHeaderFiltersStore() |
||||
|
||||
return ( |
||||
<MatchFiltersContainer> |
||||
{map(currentFilters, (filterTitle) => { |
||||
const currentActiveFilters = activeFilters[filterTitle as keyof typeof activeFilters] |
||||
|
||||
const isShrinkFilters = filtersSize >= 7 || size(currentActiveFilters) >= 7 |
||||
|| isMobile |
||||
const shrinkedActiveFilters = isShrinkFilters && currentActiveFilters |
||||
&& size(currentActiveFilters) > 2 |
||||
? [currentActiveFilters[0], currentActiveFilters[1]] |
||||
: currentActiveFilters |
||||
|
||||
const dropdownList = getDropdownList(filterTitle, '') as Array<TItem | number> |
||||
|
||||
return ( |
||||
<FilterContainer |
||||
active={filtersListName === filterTitle} |
||||
key={filterTitle} |
||||
onClick={toggleDropdownList(filterTitle)} |
||||
> |
||||
<Title t={filterTitle} /> |
||||
<ActiveFilters> |
||||
{map(shrinkedActiveFilters, (item) => ( |
||||
<ActiveFilter key={item}> |
||||
{filterTitle === 'youth_age' |
||||
? `U${item}` |
||||
: ( |
||||
<ActiveFilterText |
||||
dropdownList={dropdownList} |
||||
filterTitle={filterTitle} |
||||
item={item} |
||||
/> |
||||
)} |
||||
<CloseButton onClick={(e) => { |
||||
e.stopPropagation() |
||||
handleSetFilters(filterTitle, item) |
||||
}} |
||||
/> |
||||
</ActiveFilter> |
||||
))} |
||||
|
||||
{checkSize(currentActiveFilters) && isShrinkFilters && ( |
||||
<FiltersCount> |
||||
+{size(currentActiveFilters) - 2} |
||||
</FiltersCount> |
||||
)} |
||||
</ActiveFilters> |
||||
{filtersListName === filterTitle && !isNil(matchesList) && !isMobileDevice && ( |
||||
<DropDown |
||||
activeFilters={activeFilters} |
||||
changeInput={changeInput} |
||||
clearFilters={clearFilters} |
||||
filterTitle={filterTitle} |
||||
inputValue={inputValue} |
||||
setFilters={handleSetFilters} |
||||
/> |
||||
)} |
||||
</FilterContainer> |
||||
) |
||||
})} |
||||
|
||||
</MatchFiltersContainer> |
||||
) |
||||
} |
||||
@ -0,0 +1,171 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
import { T9n } from 'features/T9n' |
||||
|
||||
export const MatchFiltersContainer = styled.div` |
||||
display: flex; |
||||
flex-wrap: no-wrap; |
||||
justify-content: flex-end; |
||||
width: 100%; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
flex-direction: column; |
||||
padding-right: 16px; |
||||
border-top: 1px solid #505050; |
||||
padding-top: 19px; |
||||
gap: 22px; |
||||
` : css` |
||||
gap: 30px; |
||||
flex-direction: row; |
||||
padding-right: 10px;`}
|
||||
` |
||||
|
||||
type TFilterContainer = { |
||||
active?: boolean, |
||||
} |
||||
|
||||
export const FilterContainer = styled.div<TFilterContainer>` |
||||
font-style: normal; |
||||
font-weight: 700; |
||||
letter-spacing: 0.05em; |
||||
text-transform: uppercase; |
||||
position: relative; |
||||
display: flex; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
color:#8b8b8b; |
||||
font-size: 10px; |
||||
line-height: 14px; |
||||
padding-left: 19px; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
` |
||||
: css` |
||||
font-size: 18px; |
||||
line-height: 34px; |
||||
height: 34px; |
||||
color: #FFFFFF; |
||||
padding-left: 32px; |
||||
flex-direction: column;`}
|
||||
|
||||
${({ active }) => active && !isMobileDevice && css` |
||||
:after { |
||||
content: ''; |
||||
display: block; |
||||
position: fixed; |
||||
background: #000000; |
||||
opacity: 0.7; |
||||
top: 0px; |
||||
left: 0px; |
||||
width: 100%; |
||||
height: 100%; |
||||
z-index: 9; |
||||
cursor: auto; |
||||
}`}
|
||||
|
||||
:before { |
||||
display: block; |
||||
position: absolute; |
||||
top: 0; |
||||
content: ''; |
||||
height: 100%; |
||||
cursor: pointer; |
||||
|
||||
${({ active }) => (active |
||||
? css` |
||||
z-index: 12; |
||||
` |
||||
: css` |
||||
transform: rotate(180deg); |
||||
`)}
|
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
background: url(/images/arrowUpWhite.svg) center no-repeat; |
||||
transform: rotate(90deg); |
||||
right: 0; |
||||
width: 10px; |
||||
` |
||||
: css` |
||||
background: url(/images/arrowUpWhite.svg) center no-repeat; |
||||
background-size: 20px 12px; |
||||
left: 0; |
||||
width: 20px; |
||||
`}
|
||||
} |
||||
span { |
||||
${({ active }) => (active && css` |
||||
z-index: 12; |
||||
position: relative; |
||||
`)}
|
||||
} |
||||
` |
||||
|
||||
export const ActiveFilters = styled.div` |
||||
display: flex; |
||||
flex-direction: row; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
gap: 5px;` |
||||
: css` |
||||
gap: 10px;`}
|
||||
` |
||||
|
||||
export const CloseButton = styled.div` |
||||
background: url(/images/closeWhite.svg) no-repeat; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
width: 7px; |
||||
height: 7px; |
||||
background-size: 7px;` |
||||
: css` |
||||
height: 9px; |
||||
width: 9px; |
||||
background-size: 10px;`}
|
||||
` |
||||
|
||||
export const ActiveFilter = styled.span` |
||||
font-style: normal; |
||||
font-weight: 600; |
||||
display: flex; |
||||
align-items: center; |
||||
letter-spacing: 0.05em; |
||||
text-transform: uppercase; |
||||
color: #FFFFFF; |
||||
border: 1px solid #FFFFFF; |
||||
border-radius: 16px; |
||||
gap: 5px; |
||||
cursor: pointer; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
padding: 0 5px; |
||||
font-size: 10px; |
||||
line-height: 12px;` |
||||
: css` |
||||
padding: 0 10px; |
||||
font-size: 14px; |
||||
height: 17px; |
||||
|
||||
:hover { |
||||
background: #ffff; |
||||
color: #000000; |
||||
|
||||
${CloseButton} { |
||||
background: url(/images/closeBlack.svg) no-repeat; |
||||
} |
||||
}`}
|
||||
` |
||||
|
||||
export const FiltersCount = styled(ActiveFilter)`` |
||||
|
||||
export const Title = styled(T9n)` |
||||
cursor: pointer; |
||||
|
||||
${isMobileDevice ? css`margin-right: 6px;` : ''} |
||||
` |
||||
@ -0,0 +1,91 @@ |
||||
import { useState } from 'react' |
||||
|
||||
import isEmpty from 'lodash/isEmpty' |
||||
import isNil from 'lodash/isNil' |
||||
|
||||
import { useHeaderFiltersStore } from 'features/HeaderFilters' |
||||
import { T9n } from 'features/T9n' |
||||
|
||||
import { MatchesFilters } from '../MatchesFilters' |
||||
import { |
||||
ClearButton, |
||||
CloseButton, |
||||
FilterContainer, |
||||
FiltersCount, |
||||
Modal, |
||||
} from './styled' |
||||
import { DropDown } from '../Dropdown' |
||||
import { ClearFiltersPopup } from '../ClearFiltersPopup' |
||||
|
||||
export const MobileMatchesFilters = () => { |
||||
const [isOpen, setOpen] = useState(false) |
||||
const { |
||||
activeFilters, |
||||
changeInput, |
||||
clearFilters, |
||||
clickClearAll, |
||||
closeDropdownList, |
||||
currentFilters, |
||||
filtersListName, |
||||
filtersSize, |
||||
handleSetFilters, |
||||
inputValue, |
||||
isEmptyFilters, |
||||
matchesList, |
||||
} = useHeaderFiltersStore() |
||||
|
||||
const openFilters = () => setOpen(true) |
||||
|
||||
const clearAllFilters = () => { |
||||
setOpen(false) |
||||
clickClearAll() |
||||
} |
||||
|
||||
const closeFilters = () => { |
||||
setOpen(false) |
||||
closeDropdownList() |
||||
} |
||||
return ( |
||||
<> |
||||
{!isEmpty(currentFilters) && ( |
||||
<> |
||||
<FilterContainer active={filtersSize !== 0} onClick={openFilters}> |
||||
<T9n t='filter' /> |
||||
{filtersSize !== 0 && <FiltersCount>{` ${filtersSize}`}</FiltersCount>} |
||||
</FilterContainer> |
||||
<Modal isOpen={isOpen} withCloseButton={false}> |
||||
{!filtersListName |
||||
? ( |
||||
<> |
||||
<MatchesFilters isMobile /> |
||||
{!isEmptyFilters && ( |
||||
<ClearButton onClick={clearAllFilters}> |
||||
<T9n t='clear' /> |
||||
</ClearButton> |
||||
)} |
||||
<CloseButton onClick={closeFilters} /> |
||||
</> |
||||
) |
||||
: ( |
||||
<> |
||||
{!isNil(matchesList) && ( |
||||
<DropDown |
||||
activeFilters={activeFilters} |
||||
changeInput={changeInput} |
||||
clearFilters={clearFilters} |
||||
closeDropdownList={closeDropdownList} |
||||
filterTitle={filtersListName} |
||||
inputValue={inputValue} |
||||
setFilters={handleSetFilters} |
||||
/> |
||||
)} |
||||
<CloseButton onClick={closeFilters} /> |
||||
</> |
||||
)} |
||||
</Modal> |
||||
<ClearFiltersPopup /> |
||||
</> |
||||
)} |
||||
</> |
||||
) |
||||
} |
||||
@ -0,0 +1,80 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { OutsideClickWrapper } from 'features/OutsideClick' |
||||
import { ModalWindow } from 'features/Modal/styled' |
||||
import { |
||||
Modal as BaseModal, |
||||
} from 'features/AuthServiceApp/components/RegisterPopup/styled' |
||||
|
||||
export const Modal = styled(BaseModal)` |
||||
${OutsideClickWrapper} { |
||||
height: 100%; |
||||
} |
||||
|
||||
${ModalWindow} { |
||||
min-height: auto; |
||||
max-width: 293px; |
||||
padding: 35px 0 9px 0; |
||||
top: 4vh; |
||||
} |
||||
` |
||||
|
||||
export const CloseButton = styled.div` |
||||
position: absolute; |
||||
width: 13px; |
||||
height: 13px; |
||||
background:url(/images/closeWhite.svg) no-repeat; |
||||
background-size: 13px; |
||||
top: 13px; |
||||
right: 13px; |
||||
` |
||||
|
||||
type TFilterProps = { |
||||
active?: boolean, |
||||
} |
||||
|
||||
export const FilterContainer = styled.div<TFilterProps>` |
||||
font-style: normal; |
||||
font-weight: 700; |
||||
font-size: 10px; |
||||
line-height: 14px; |
||||
letter-spacing: 0.05em; |
||||
text-transform: uppercase; |
||||
color: #FFFFFF; |
||||
padding-left: 22px; |
||||
position: relative; |
||||
|
||||
:before{ |
||||
content: ''; |
||||
display: block; |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
width:14px; |
||||
height: 14px; |
||||
background-size: 14px 14px; |
||||
|
||||
${({ active }) => (active |
||||
? css` |
||||
background: url(/images/filter-white.svg) no-repeat;` |
||||
: css` |
||||
background: url(/images/filter-gray.svg) no-repeat;`)} |
||||
} |
||||
` |
||||
|
||||
export const ClearButton = styled.div` |
||||
font-style: normal; |
||||
position: absolute; |
||||
top: 14px; |
||||
right: 36px; |
||||
font-size: 10px; |
||||
line-height: 12px; |
||||
font-weight: 400; |
||||
letter-spacing: 0.05em; |
||||
text-transform: uppercase; |
||||
color: rgba(255,255,255,0.5); |
||||
` |
||||
|
||||
export const FiltersCount = styled.span` |
||||
font-weight: 400; |
||||
` |
||||
Loading…
Reference in new issue