Ott 748 popup players list (#281)

* feat(748): split start and bench players

* refactor(748): moved players avatar preloading to upper component and simplified

* refactor(748): renamed component

* fix(748): added scrolling to players list
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent f9702c9d2f
commit dd87080d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      src/features/Common/Image/index.tsx
  2. 1
      src/features/MatchPage/types.tsx
  3. 75
      src/features/MatchPopup/components/GroupedPlayersList/index.tsx
  4. 29
      src/features/MatchPopup/components/GroupedPlayersList/styled.tsx
  5. 49
      src/features/MatchPopup/components/PlayersList/index.tsx
  6. 16
      src/features/MatchPopup/components/PlayersList/styled.tsx
  7. 29
      src/features/MatchPopup/components/PlayersListDesktop/index.tsx
  8. 7
      src/features/MatchPopup/components/PlayersListMobile/index.tsx
  9. 11
      src/features/ProfileLogo/index.tsx
  10. 1
      src/requests/getMatchPlaylists.tsx

@ -1,11 +1,5 @@
import type { BaseSyntheticEvent } from 'react' import type { BaseSyntheticEvent } from 'react'
import { import { useCallback } from 'react'
RefObject,
useCallback,
useEffect,
useRef,
useState,
} from 'react'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
@ -16,8 +10,7 @@ type Props = {
className?: string, className?: string,
dataSrc?: string, dataSrc?: string,
fallbackSrc?: string, fallbackSrc?: string,
imagesList?: Array<RefObject<HTMLImageElement>>, onLoad?: () => void,
setImagesList?: (imagesList: Array<RefObject<HTMLImageElement>>) => void,
src: string, src: string,
title?: string, title?: string,
} }
@ -27,23 +20,10 @@ export const Image = ({
className, className,
dataSrc, dataSrc,
fallbackSrc, fallbackSrc,
imagesList = [], onLoad,
setImagesList,
src, src,
title, title,
}: Props) => { }: Props) => {
const imgRef = useRef<HTMLImageElement>(null)
const [loaded, setLoaded] = useState(false)
useEffect(
() => {
if (!setImagesList || !loaded) return
setImagesList([...imagesList, imgRef])
},
// eslint-disable-next-line
[loaded],
)
const onError = useCallback((e: BaseSyntheticEvent) => { const onError = useCallback((e: BaseSyntheticEvent) => {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
e.target.onError = '' e.target.onError = ''
@ -58,8 +38,7 @@ export const Image = ({
data-src={dataSrc} data-src={dataSrc}
className={className} className={className}
onError={onError} onError={onError}
onLoad={() => setLoaded(true)} onLoad={() => onLoad?.()}
ref={imgRef}
title={title} title={title}
/> />
) )

@ -18,6 +18,7 @@ export type PlayerPlaylistOption = {
id: number, id: number,
name_eng: string, name_eng: string,
name_rus: string, name_rus: string,
start?: boolean,
type: PlaylistTypes.PLAYER, type: PlaylistTypes.PLAYER,
} }

@ -0,0 +1,75 @@
import {
useMemo,
useState,
useCallback,
} from 'react'
import map from 'lodash/map'
import every from 'lodash/every'
import groupBy from 'lodash/groupBy'
import type { PlayerPlaylistOptions } from 'features/MatchPage/types'
import { Loader } from 'features/Loader'
import { Teams } from '../../types'
import { PlayersList } from '../PlayersList'
import { ListsWrapper, LoaderWrapper } from './styled'
const useItemsLoadedState = (items: PlayerPlaylistOptions) => {
const [loadedIds, setLoadedIds] = useState<Record<number, number>>({})
const onLoad = useCallback((id: number) => {
setLoadedIds((state) => ({ ...state, [id]: id }))
}, [])
const allLoaded = useMemo(() => {
const ids = map(items, ({ id }) => id)
return every(ids, (id) => Boolean(loadedIds[id]))
}, [items, loadedIds])
return {
allLoaded,
onLoad,
}
}
type Props = {
players: PlayerPlaylistOptions,
team: Teams,
}
export const GroupedPlayersList = ({
players,
team,
}: Props) => {
const { allLoaded, onLoad } = useItemsLoadedState(players)
const { benchedPlayers, startingPlayers } = groupBy(
players,
({ start }) => (start ? 'startingPlayers' : 'benchedPlayers'),
)
return (
<ListsWrapper>
{
!allLoaded && (
<LoaderWrapper>
<Loader color='#515151' />
</LoaderWrapper>
)
}
<PlayersList
team={team}
players={startingPlayers}
allLoaded={allLoaded}
onLoad={onLoad}
/>
<PlayersList
team={team}
players={benchedPlayers}
allLoaded={allLoaded}
onLoad={onLoad}
/>
</ListsWrapper>
)
}

@ -0,0 +1,29 @@
import styled from 'styled-components/macro'
import { devices } from 'config'
import { List } from '../PlayersList/styled'
export const LoaderWrapper = styled.div`
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`
export const ListsWrapper = styled.div`
position: relative;
width: 576px;
display: flex;
flex-direction: column;
${List}:last-child {
margin-top: 25px;
}
@media ${devices.mobile} {
width: 100%;
}
`

@ -1,22 +1,13 @@
import React, {
RefObject,
useEffect,
useState,
} from 'react'
import map from 'lodash/map' import map from 'lodash/map'
import size from 'lodash/size'
import { ProfileTypes, SportTypes } from 'config' import { ProfileTypes } from 'config'
import { PlayerPlaylistOptions, PlayerPlaylistOption } from 'features/MatchPage/types' import { PlayerPlaylistOptions } from 'features/MatchPage/types'
import { useMatchPopupStore } from 'features/MatchPopup/store' import { useMatchPopupStore } from 'features/MatchPopup/store'
import { Loader } from 'features/Loader'
import { Teams } from '../../types' import { Teams } from '../../types'
import { import {
List, List,
LoaderWrapper,
Item, Item,
Logo, Logo,
PlayerName, PlayerName,
@ -24,52 +15,34 @@ import {
} from './styled' } from './styled'
type Props = { type Props = {
onClick: (player: PlayerPlaylistOption) => void, allLoaded: boolean,
onLoad: (id: number) => void,
players: PlayerPlaylistOptions, players: PlayerPlaylistOptions,
sportType: SportTypes,
team: Teams, team: Teams,
} }
export const PlayersList = ({ export const PlayersList = ({
onClick, allLoaded,
onLoad,
players, players,
sportType,
team, team,
}: Props) => { }: Props) => {
const { match } = useMatchPopupStore() const { handlePlayerClick, match } = useMatchPopupStore()
const [imagesList, setImagesList] = useState<
Array<RefObject<HTMLImageElement>>
>([])
const [allImagesReady, setAllImagesReady] = useState(false)
useEffect(() => {
if (size(imagesList) === size(players)) {
setAllImagesReady(true)
}
}, [imagesList, players])
if (!match) return null if (!match) return null
return ( return (
<List team={team}> <List team={team}>
{!allImagesReady
&& (
<LoaderWrapper>
<Loader color='#515151' />
</LoaderWrapper>
)}
{ {
map(players, (player) => ( map(players, (player) => (
<Item key={player.id} isReady={allImagesReady}> <Item key={player.id} isReady={allLoaded}>
<Button onClick={() => onClick(player)}> <Button onClick={() => handlePlayerClick(player)}>
<Logo <Logo
id={player.id} id={player.id}
sportType={sportType} sportType={match.sportType}
profileType={ProfileTypes.PLAYERS} profileType={ProfileTypes.PLAYERS}
team={team} team={team}
imagesList={imagesList} onLoad={() => onLoad(player.id)}
setImagesList={setImagesList}
/> />
<PlayerName nameObj={player} /> <PlayerName nameObj={player} />
</Button> </Button>

@ -15,23 +15,14 @@ type ItemProps = {
isReady?: boolean, isReady?: boolean,
} }
export const LoaderWrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`
export const List = styled.ul<ListProps>` export const List = styled.ul<ListProps>`
width: calc((100% - 30px) / 2);
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-content: flex-start; align-content: flex-start;
flex-direction: ${({ team }) => ( justify-content: ${({ team }) => (
team === Teams.TEAM1 team === Teams.TEAM1
? 'row-reverse' ? 'flex-end'
: 'row' : ''
)}; )};
@media ${devices.mobile} { @media ${devices.mobile} {
@ -66,6 +57,7 @@ export const Button = styled.button`
border: none; border: none;
background: none; background: none;
cursor: pointer; cursor: pointer;
padding: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;

@ -1,11 +1,12 @@
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { customScrollbar } from 'features/Common'
import { useMatchPopupStore } from 'features/MatchPopup' import { useMatchPopupStore } from 'features/MatchPopup'
import { Teams } from '../../types' import { Teams } from '../../types'
import { BlockTitle } from '../../styled' import { BlockTitle } from '../../styled'
import { PlayersList } from '../PlayersList' import { GroupedPlayersList } from '../GroupedPlayersList'
const Wrapper = styled.div` const Wrapper = styled.div`
display: flex; display: flex;
@ -16,15 +17,29 @@ const Wrapper = styled.div`
const ListsWrapper = styled.div` const ListsWrapper = styled.div`
width: 100%; width: 100%;
height: 325px;
overflow-y: auto;
margin-top: 10px; margin-top: 10px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-around;
${customScrollbar};
::-webkit-scrollbar-thumb {
border-radius: 3px;
background: rgba(196, 196, 196, 0.3);
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
border-radius: 3px;
background: rgba(103, 103, 103, 0.3);
}
` `
export const PlayersListDesktop = () => { export const PlayersListDesktop = () => {
const { const {
handlePlayerClick,
match, match,
matchPlaylists, matchPlaylists,
} = useMatchPopupStore() } = useMatchPopupStore()
@ -37,17 +52,13 @@ export const PlayersListDesktop = () => {
<T9n t='team_players' /> <T9n t='team_players' />
</BlockTitle> </BlockTitle>
<ListsWrapper> <ListsWrapper>
<PlayersList <GroupedPlayersList
team={Teams.TEAM1} team={Teams.TEAM1}
players={matchPlaylists.players.team1} players={matchPlaylists.players.team1}
sportType={match.sportType}
onClick={handlePlayerClick}
/> />
<PlayersList <GroupedPlayersList
team={Teams.TEAM2} team={Teams.TEAM2}
players={matchPlaylists.players.team2} players={matchPlaylists.players.team2}
sportType={match.sportType}
onClick={handlePlayerClick}
/> />
</ListsWrapper> </ListsWrapper>
</Wrapper> </Wrapper>

@ -6,7 +6,7 @@ import { useMatchPopupStore } from 'features/MatchPopup'
import { Teams } from '../../types' import { Teams } from '../../types'
import { BlockTitle } from '../../styled' import { BlockTitle } from '../../styled'
import { PlayersList } from '../PlayersList' import { GroupedPlayersList } from '../GroupedPlayersList'
import { import {
Wrapper, Wrapper,
Tabs, Tabs,
@ -15,7 +15,6 @@ import {
export const PlayersListMobile = () => { export const PlayersListMobile = () => {
const { const {
handlePlayerClick,
match, match,
matchPlaylists, matchPlaylists,
} = useMatchPopupStore() } = useMatchPopupStore()
@ -46,11 +45,9 @@ export const PlayersListMobile = () => {
<Name nameObj={match.team2} /> <Name nameObj={match.team2} />
</Tab> </Tab>
</Tabs> </Tabs>
<PlayersList <GroupedPlayersList
team={selectedTeam} team={selectedTeam}
players={players} players={players}
sportType={match.sportType}
onClick={handlePlayerClick}
/> />
</Wrapper> </Wrapper>
) )

@ -1,5 +1,3 @@
import { RefObject } from 'react'
import { ProfileTypes, SportTypes } from 'config' import { ProfileTypes, SportTypes } from 'config'
import { getProfileFallbackLogo, getProfileLogo } from 'helpers' import { getProfileFallbackLogo, getProfileLogo } from 'helpers'
@ -12,12 +10,11 @@ type ProfileImageProps = {
altNameObj?: ObjectWithName, altNameObj?: ObjectWithName,
className?: string, className?: string,
id: number, id: number,
imagesList?: Array<RefObject<HTMLImageElement>>,
lazy?: boolean, lazy?: boolean,
nameAsTitle?: boolean, nameAsTitle?: boolean,
onLoad?: () => void,
prefix?: string, prefix?: string,
profileType: ProfileTypes, profileType: ProfileTypes,
setImagesList?: (imagesList: Array<RefObject<HTMLImageElement>>) => void,
size?: number, size?: number,
sportType: SportTypes, sportType: SportTypes,
title?: string, title?: string,
@ -28,12 +25,11 @@ export const ProfileLogo = ({
altNameObj, altNameObj,
className, className,
id, id,
imagesList,
lazy = false, lazy = false,
nameAsTitle, nameAsTitle,
onLoad,
prefix, prefix,
profileType, profileType,
setImagesList,
size, size,
sportType, sportType,
title, title,
@ -58,8 +54,7 @@ export const ProfileLogo = ({
dataSrc={lazy ? src : ''} dataSrc={lazy ? src : ''}
fallbackSrc={fallbackSrc} fallbackSrc={fallbackSrc}
className={className} className={className}
imagesList={imagesList} onLoad={onLoad}
setImagesList={setImagesList}
title={titleText} title={titleText}
/> />
) )

@ -40,6 +40,7 @@ type Player = {
name_eng: string, name_eng: string,
name_rus: string, name_rus: string,
num: string, num: string,
start?: boolean,
} }
export type Players = Array<Player> export type Players = Array<Player>

Loading…
Cancel
Save