Ott 143 tournaments dropdown (#62)
parent
7c2495daab
commit
1d5b0d5e5b
@ -0,0 +1,35 @@ |
|||||||
|
import map from 'lodash/map' |
||||||
|
|
||||||
|
import type { Tournaments } from 'requests' |
||||||
|
|
||||||
|
import { LOGOS_FALLBACKS } from 'config' |
||||||
|
import { getLogo, SPORT_TYPES } from 'helpers' |
||||||
|
|
||||||
|
type Name = 'name_rus' | 'name_eng' |
||||||
|
type ShortName = 'short_name_rus' | 'short_name_eng' |
||||||
|
|
||||||
|
export const normalizeTournaments = (tournaments: Tournaments, suffix: string) => ( |
||||||
|
map(tournaments, (tournament) => { |
||||||
|
const { c_sport, id } = tournament |
||||||
|
const sportType = SPORT_TYPES[c_sport] |
||||||
|
const name = tournament[`name_${suffix}` as Name] |
||||||
|
const shortName = tournament[`short_name_${suffix}` as ShortName] || name |
||||||
|
const country = tournament.country?.[`name_${suffix}` as Name] |
||||||
|
|
||||||
|
const logo = getLogo({ |
||||||
|
id, |
||||||
|
sportType, |
||||||
|
type: 'tournaments', |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
country, |
||||||
|
fallbackImage: LOGOS_FALLBACKS[sportType].tournaments, |
||||||
|
id, |
||||||
|
logo, |
||||||
|
name, |
||||||
|
shortName, |
||||||
|
sportType, |
||||||
|
} |
||||||
|
}) |
||||||
|
) |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
import { useEffect, useState } from 'react' |
||||||
|
|
||||||
|
import find from 'lodash/find' |
||||||
|
|
||||||
|
import type { Tournaments } from 'requests' |
||||||
|
import { getSportTournaments } from 'requests' |
||||||
|
|
||||||
|
import { useToggle } from 'hooks' |
||||||
|
|
||||||
|
import { useHeaderFiltersStore } from 'features/HeaderFilters' |
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
|
||||||
|
import { normalizeTournaments } from './helpers' |
||||||
|
|
||||||
|
const useTournamentsList = () => { |
||||||
|
const { selectedSportType } = useHeaderFiltersStore() |
||||||
|
const [list, setList] = useState<Tournaments>([]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setList([]) |
||||||
|
getSportTournaments(selectedSportType).then(setList) |
||||||
|
}, [selectedSportType]) |
||||||
|
|
||||||
|
return list |
||||||
|
} |
||||||
|
|
||||||
|
export const useTournamentFilter = () => { |
||||||
|
const { suffix } = useLexicsStore() |
||||||
|
const { |
||||||
|
selectedTournamentId, |
||||||
|
setSelectedTournamentId, |
||||||
|
} = useHeaderFiltersStore() |
||||||
|
const { |
||||||
|
close, |
||||||
|
isOpen, |
||||||
|
open, |
||||||
|
} = useToggle() |
||||||
|
const tournamentList = useTournamentsList() |
||||||
|
const tournaments = normalizeTournaments(tournamentList, suffix) |
||||||
|
const selectedTournament = find( |
||||||
|
tournaments, |
||||||
|
(tournament) => tournament.id === selectedTournamentId, |
||||||
|
) |
||||||
|
|
||||||
|
const onTournamentSelect = (tournamentId: number) => { |
||||||
|
setSelectedTournamentId(tournamentId) |
||||||
|
close() |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
close, |
||||||
|
isOpen, |
||||||
|
onTournamentSelect, |
||||||
|
open, |
||||||
|
selectedTournament, |
||||||
|
tournaments, |
||||||
|
} |
||||||
|
} |
||||||
@ -1,5 +1,51 @@ |
|||||||
import React from 'react' |
import React from 'react' |
||||||
|
|
||||||
import { Wrapper } from './styled' |
import { T9n } from 'features/T9n' |
||||||
|
import { OutsideClick } from 'features/OutsideClick' |
||||||
|
|
||||||
export const TournamentFilter = () => <Wrapper /> |
import { TournamentList } from '../TournamentList' |
||||||
|
import { useTournamentFilter } from './hooks' |
||||||
|
import { |
||||||
|
Wrapper, |
||||||
|
ListWrapper, |
||||||
|
DropdownButton, |
||||||
|
ButtonTitle, |
||||||
|
Arrows, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
export const TournamentFilter = () => { |
||||||
|
const { |
||||||
|
close, |
||||||
|
isOpen, |
||||||
|
onTournamentSelect, |
||||||
|
open, |
||||||
|
selectedTournament, |
||||||
|
tournaments, |
||||||
|
} = useTournamentFilter() |
||||||
|
return ( |
||||||
|
<Wrapper> |
||||||
|
<DropdownButton onClick={open} active={isOpen}> |
||||||
|
<ButtonTitle> |
||||||
|
{ |
||||||
|
selectedTournament |
||||||
|
? selectedTournament.shortName |
||||||
|
: <T9n t='tournament' /> |
||||||
|
} |
||||||
|
</ButtonTitle> |
||||||
|
<Arrows active={isOpen} /> |
||||||
|
</DropdownButton> |
||||||
|
{ |
||||||
|
isOpen && ( |
||||||
|
<OutsideClick onClick={close}> |
||||||
|
<ListWrapper> |
||||||
|
<TournamentList |
||||||
|
tournaments={tournaments} |
||||||
|
onSelect={onTournamentSelect} |
||||||
|
/> |
||||||
|
</ListWrapper> |
||||||
|
</OutsideClick> |
||||||
|
) |
||||||
|
} |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
|
} |
||||||
|
|||||||
@ -1,3 +1,84 @@ |
|||||||
import styled from 'styled-components/macro' |
import styled from 'styled-components/macro' |
||||||
|
|
||||||
export const Wrapper = styled.div`` |
import { сustomScrollbar } from 'features/Common' |
||||||
|
|
||||||
|
export const Wrapper = styled.div` |
||||||
|
height: 100%; |
||||||
|
width: 50%; |
||||||
|
position: relative; |
||||||
|
` |
||||||
|
|
||||||
|
export const ListWrapper = styled.div` |
||||||
|
position: absolute; |
||||||
|
left: -100%; |
||||||
|
top: calc(100% + 8px); |
||||||
|
width: 448px; |
||||||
|
height: 456px; |
||||||
|
overflow-y: scroll; |
||||||
|
|
||||||
|
${сustomScrollbar} |
||||||
|
` |
||||||
|
|
||||||
|
type Props = { |
||||||
|
active?: boolean, |
||||||
|
} |
||||||
|
|
||||||
|
export const DropdownButton = styled.button<Props>` |
||||||
|
position: relative; |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
outline: none; |
||||||
|
border: none; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
background-color: #3F3F3F; |
||||||
|
font-weight: 600; |
||||||
|
font-size: 18px; |
||||||
|
cursor: pointer; |
||||||
|
border-left: 1px solid #222222; |
||||||
|
border-top-right-radius: 2px; |
||||||
|
border-bottom-right-radius: 2px; |
||||||
|
background-size: 12px 12px; |
||||||
|
background-repeat: no-repeat; |
||||||
|
background-position: 113px; |
||||||
|
|
||||||
|
${({ active }) => ( |
||||||
|
active |
||||||
|
? ` |
||||||
|
background-color: #666666; |
||||||
|
color: #fff; |
||||||
|
` |
||||||
|
: ` |
||||||
|
color: #999999; |
||||||
|
|
||||||
|
:hover { |
||||||
|
background-color: #484848; |
||||||
|
} |
||||||
|
` |
||||||
|
)} |
||||||
|
` |
||||||
|
|
||||||
|
export const ButtonTitle = styled.span` |
||||||
|
display: inline-block; |
||||||
|
width: 80%; |
||||||
|
white-space: nowrap; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
line-height: normal; |
||||||
|
` |
||||||
|
|
||||||
|
export const Arrows = styled.span<Props>` |
||||||
|
position: absolute; |
||||||
|
right: 19px; |
||||||
|
display: inline-block; |
||||||
|
width: 12px; |
||||||
|
height: 12px; |
||||||
|
background-repeat: no-repeat; |
||||||
|
background-position: center; |
||||||
|
|
||||||
|
${({ active }) => ( |
||||||
|
active |
||||||
|
? 'background-image: url(/images/arrowUp.svg);' |
||||||
|
: 'background-image: url(/images/arrowDown.svg);' |
||||||
|
)} |
||||||
|
` |
||||||
|
|||||||
@ -0,0 +1,71 @@ |
|||||||
|
import React from 'react' |
||||||
|
|
||||||
|
import map from 'lodash/map' |
||||||
|
|
||||||
|
import { SPORT_COLORS } from 'config' |
||||||
|
import type { SportType } from 'features/Search/config' |
||||||
|
|
||||||
|
import { useItemsList } from 'features/ItemsList/hooks' |
||||||
|
import { |
||||||
|
Logo, |
||||||
|
LogoWrapper, |
||||||
|
ItemInfo, |
||||||
|
Name, |
||||||
|
SportName, |
||||||
|
TeamOrCountry, |
||||||
|
Wrapper, |
||||||
|
} from 'features/ItemsList/styled' |
||||||
|
|
||||||
|
import { ListItem } from './styled' |
||||||
|
|
||||||
|
type Tournament = { |
||||||
|
country?: string, |
||||||
|
fallbackImage: string, |
||||||
|
id: number, |
||||||
|
logo: string, |
||||||
|
name: string, |
||||||
|
sportType: SportType, |
||||||
|
} |
||||||
|
|
||||||
|
type TournamentListProps = { |
||||||
|
onSelect: (id: number) => void, |
||||||
|
tournaments: Array<Tournament>, |
||||||
|
} |
||||||
|
|
||||||
|
export const TournamentList = ({ onSelect, tournaments }: TournamentListProps) => { |
||||||
|
const { |
||||||
|
onError, |
||||||
|
ref, |
||||||
|
} = useItemsList() |
||||||
|
|
||||||
|
return ( |
||||||
|
<Wrapper ref={ref}> |
||||||
|
{map(tournaments, ({ |
||||||
|
country, |
||||||
|
fallbackImage, |
||||||
|
id, |
||||||
|
logo, |
||||||
|
name, |
||||||
|
sportType, |
||||||
|
}) => ( |
||||||
|
<ListItem key={id} onClick={() => onSelect(id)}> |
||||||
|
<LogoWrapper> |
||||||
|
<Logo |
||||||
|
data-src={logo} |
||||||
|
onError={onError(fallbackImage)} |
||||||
|
alt={name} |
||||||
|
/> |
||||||
|
</LogoWrapper> |
||||||
|
<ItemInfo> |
||||||
|
<Name>{name}</Name> |
||||||
|
<SportName |
||||||
|
color={SPORT_COLORS[sportType]} |
||||||
|
t={sportType} |
||||||
|
/> |
||||||
|
<TeamOrCountry>{country}</TeamOrCountry> |
||||||
|
</ItemInfo> |
||||||
|
</ListItem> |
||||||
|
))} |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { Item } from 'features/ItemsList/styled' |
||||||
|
|
||||||
|
export const ListItem = styled(Item)` |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
width: 100%; |
||||||
|
height: 56px; |
||||||
|
background-color: #666; |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
:focus-within, |
||||||
|
:hover { |
||||||
|
background-color: #999; |
||||||
|
outline: none; |
||||||
|
} |
||||||
|
|
||||||
|
:focus { |
||||||
|
background-color: #999; |
||||||
|
} |
||||||
|
` |
||||||
@ -1,14 +0,0 @@ |
|||||||
type TournamentOption = {} |
|
||||||
|
|
||||||
type Tournaments = { |
|
||||||
options: Array<TournamentOption>, |
|
||||||
selected: TournamentOption | null, |
|
||||||
} |
|
||||||
|
|
||||||
const initialState: Tournaments = { |
|
||||||
options: [], |
|
||||||
selected: null, |
|
||||||
} |
|
||||||
|
|
||||||
// доделаем на таске по турнирам
|
|
||||||
export const useTournaments = () => initialState |
|
||||||
@ -0,0 +1,41 @@ |
|||||||
|
import { DATA_URL, PROCEDURES } from 'config' |
||||||
|
import { callApi, getResponseData } from 'helpers' |
||||||
|
import { SportTypes } from 'features/HeaderFilters' |
||||||
|
|
||||||
|
const proc = PROCEDURES.get_tournament_list |
||||||
|
|
||||||
|
type Country = { |
||||||
|
id: number, |
||||||
|
name_eng: string, |
||||||
|
name_rus: string, |
||||||
|
} |
||||||
|
|
||||||
|
export type Tournament = { |
||||||
|
c_gender: number | null, |
||||||
|
c_sport: SportTypes, |
||||||
|
c_tournament_type: number, |
||||||
|
country: Country, |
||||||
|
id: number, |
||||||
|
name_eng: string, |
||||||
|
name_rus: string, |
||||||
|
short_name_eng: string | null, |
||||||
|
short_name_rus: string | null, |
||||||
|
} |
||||||
|
|
||||||
|
export type Tournaments = Array<Tournament> |
||||||
|
|
||||||
|
export const getSportTournaments = (sportId: number): Promise<Tournaments> => { |
||||||
|
const config = { |
||||||
|
body: { |
||||||
|
params: { |
||||||
|
_p_sport: sportId, |
||||||
|
}, |
||||||
|
proc, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
return callApi({ |
||||||
|
config, |
||||||
|
url: DATA_URL, |
||||||
|
}).then(getResponseData(proc)) |
||||||
|
} |
||||||
Loading…
Reference in new issue