fix(#720): timeline tile size

test-c
Rakov 2 years ago
parent c0d06f8ef9
commit 42623654bb
  1. 1
      src/features/Common/Image/index.tsx
  2. 2
      src/features/HeaderFilters/store/hooks/index.tsx
  3. 25
      src/features/MatchesSlider/hooks.tsx
  4. 38
      src/features/MatchesSlider/index.tsx
  5. 40
      src/features/MatchesSlider/styled.tsx
  6. 55
      src/features/MatchesTimeline/index.tsx
  7. 35
      src/features/MatchesTimeline/styled.tsx

@ -37,7 +37,6 @@ export const Image = ({
onError={onError} onError={onError}
onLoad={onLoad} onLoad={onLoad}
title={title} title={title}
loading='lazy'
/> />
) )
} }

@ -31,7 +31,7 @@ export const useFilters = () => {
}) })
const sportList = getLocalStorageItem(querieKeys.sportsList) const sportList = getLocalStorageItem(querieKeys.sportsList)
const [selectedMode, setSelectedMode] = useState<Tabs>(isMobileDevice ? Tabs.WEEK : Tabs.TIMELINE) const [selectedMode, setSelectedMode] = useState<Tabs>(Tabs.WEEK)
const [selectedMonthModeDate, setSelectedMonthModeDate] = useState(startOfMonth(new Date())) const [selectedMonthModeDate, setSelectedMonthModeDate] = useState(startOfMonth(new Date()))
const isMonthMode = selectedMode === Tabs.MONTH const isMonthMode = selectedMode === Tabs.MONTH
const isTimelineMode = selectedMode === Tabs.TIMELINE && !isMobileDevice const isTimelineMode = selectedMode === Tabs.TIMELINE && !isMobileDevice

@ -9,11 +9,9 @@ import {
import throttle from 'lodash/throttle' import throttle from 'lodash/throttle'
import { MATCH_CARD_WIDTH, MATCH_CARD_GAP } from 'features/MatchCard/config'
import type { Match } from 'helpers' import type { Match } from 'helpers'
const MATCHES_TO_SCROLL = 12 const MATCHES_TO_SCROLL = 3
const SCROLLING_DELAY = 350 const SCROLLING_DELAY = 350
@ -24,6 +22,8 @@ export const useMatchesSlider = (matches: Array<Match>) => {
const [showRightArrow, setShowRigthArrow] = useState(false) const [showRightArrow, setShowRigthArrow] = useState(false)
const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false) const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false)
const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false) const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false)
const [sliderLiWidth, setSliderLiWidth] = useState(0)
const [sliderLiGap, setSliderLiGap] = useState(0)
useEffect(() => { useEffect(() => {
const { const {
@ -70,21 +70,32 @@ export const useMatchesSlider = (matches: Array<Match>) => {
setShowRigthArrow(false) setShowRigthArrow(false)
} }
useEffect(() => {
const liFirst = slidesRef.current?.querySelectorAll('li')[0]?.getBoundingClientRect()
const liWidth = liFirst?.width
const liFirstLeft = liFirst?.left
const liSecondLeft = slidesRef.current?.querySelectorAll('li')[1]?.getBoundingClientRect().left
if (liWidth && liFirstLeft && liSecondLeft) {
setSliderLiWidth(liWidth)
setSliderLiGap(liSecondLeft - liFirstLeft - liWidth)
}
}, [])
const slideLeft = useMemo(() => throttle(() => { const slideLeft = useMemo(() => throttle(() => {
slidesRef.current!.scrollBy({ slidesRef.current!.scrollBy({
behavior: 'smooth', behavior: 'smooth',
left: -((MATCH_CARD_WIDTH + MATCH_CARD_GAP) * MATCHES_TO_SCROLL), left: -((sliderLiWidth + sliderLiGap) * MATCHES_TO_SCROLL),
top: 0, top: 0,
}) })
}, SCROLLING_DELAY), []) }, SCROLLING_DELAY), [sliderLiGap, sliderLiWidth])
const slideRight = useMemo(() => throttle(() => { const slideRight = useMemo(() => throttle(() => {
slidesRef.current!.scrollBy({ slidesRef.current!.scrollBy({
behavior: 'smooth', behavior: 'smooth',
left: (MATCH_CARD_WIDTH + MATCH_CARD_GAP) * MATCHES_TO_SCROLL, left: ((sliderLiWidth + sliderLiGap) * MATCHES_TO_SCROLL),
top: 0, top: 0,
}) })
}, SCROLLING_DELAY), []) }, SCROLLING_DELAY), [sliderLiGap, sliderLiWidth])
return { return {
isLeftArrowDisabled, isLeftArrowDisabled,

@ -16,10 +16,13 @@ import {
} from './styled' } from './styled'
type MatchesSliderProps = { type MatchesSliderProps = {
cardSize?: number,
matches: Array<Match>, matches: Array<Match>,
} }
export const MatchesSlider = ({ matches }: MatchesSliderProps) => { const PADDING_PARENT = 10
export const MatchesSlider = ({ cardSize, matches }: MatchesSliderProps) => {
const { const {
isLeftArrowDisabled, isLeftArrowDisabled,
isRightArrowDisabled, isRightArrowDisabled,
@ -36,15 +39,16 @@ export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
const scrollToMatchByIndex = (index: number) => { const scrollToMatchByIndex = (index: number) => {
const match = slidesRef.current!.querySelectorAll('li')[index] const match = slidesRef.current!.querySelectorAll('li')[index]
const offsetLeftCount = match.offsetLeft const offsetLeftCount = match.offsetLeft
const PADDING_PARENT = 10
const offsetLeft = offsetLeftCount - PADDING_PARENT const offsetLeft = offsetLeftCount - PADDING_PARENT
slidesRef.current!.scrollBy({ setTimeout(() => {
// @ts-ignore slidesRef.current!.scrollBy({
behavior: 'instant', // @ts-ignore
left: offsetLeft, behavior: 'instant',
}) left: offsetLeft,
})
}, 0)
} }
// скролл к лайв матчам или сегодняшней дате // скролл к лайв матчам или сегодняшней дате
@ -63,12 +67,16 @@ export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
const slidesRefClientWidth = slidesRef.current!.clientWidth const slidesRefClientWidth = slidesRef.current!.clientWidth
const slidesRefScrollWidth = slidesRef.current!.scrollWidth const slidesRefScrollWidth = slidesRef.current!.scrollWidth
slidesRef.current!.scrollBy({
setTimeout(() => {
slidesRef.current!.scrollBy({
// @ts-ignore // @ts-ignore
behavior: 'instant', behavior: 'instant',
left: slidesRefScrollWidth - slidesRefClientWidth, left: slidesRefScrollWidth - slidesRefClientWidth + PADDING_PARENT,
}) })
// eslint-disable-next-line react-hooks/exhaustive-deps }, 0)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
if (isEmpty(matches)) return null if (isEmpty(matches)) return null
@ -90,7 +98,11 @@ export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
/> />
</ArrowButton> </ArrowButton>
)} )}
<Slides ref={slidesRef} onScroll={onScroll}> <Slides
ref={slidesRef}
onScroll={onScroll}
size={cardSize}
>
{map(matches, (match) => ( {map(matches, (match) => (
<MatchCard <MatchCard
match={match} match={match}

@ -8,13 +8,15 @@ export const Wrapper = styled.div`
padding-right: 5px; padding-right: 5px;
` `
export const Slides = styled.ul` export const Slides = styled.ul<{
size?: number,
}>`
display: flex; display: flex;
scroll-behavior: smooth; scroll-behavior: smooth;
overflow-x: auto; overflow-x: auto;
gap: 0.9rem; gap: 0.9rem;
padding: 10px 10px 10px 7px; padding: 10px 10px 10px 7px;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
@ -26,37 +28,13 @@ export const Slides = styled.ul`
${CardWrapper} { ${CardWrapper} {
position: relative; position: relative;
height: 12.9rem; height: ${({ size }) => (size ? `${size}px` : '12.9rem')};
width: 12.9rem; width: ${({ size }) => (size ? `${size}px` : '12.9rem')};
transition: scale 0.2s;
&:hover { &:hover {
scale: 1.04; scale: 1.04;
} }
@media screen and (min-width: 1920px) {
height: 13.8rem;
width: 13.8rem;
}
@media screen and (max-width: 1920px) {
height: 13.4rem;
width: 13.4rem;
}
@media screen and (max-width: 1600px) {
height: 13rem;
width: 13rem;
}
@media screen and (max-width: 1440px) {
height: 12.8rem;
width: 12.8rem;
}
@media screen and (max-width: 1280px) {
height: 12.6rem;
width: 12.6rem;
}
} }
` `
@ -99,13 +77,13 @@ export const ArrowButton = styled.button<ArrowProps>`
z-index: 3; z-index: 3;
top: 50%; top: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
left: ${({ direction }) => (direction === 'left' ? '1.6rem' : 'calc(100% - 1.6rem)')}; left: ${({ direction }) => (direction === 'left' ? '1.6rem' : 'calc(100% - 2.3rem)')};
&:hover { &:hover {
${({ direction, disabled }) => (!disabled ? css` ${({ direction, disabled }) => (!disabled ? css`
width: 3rem; width: 3rem;
height: 3rem; height: 3rem;
left: ${direction === 'left' ? '2rem' : 'calc(100% - 2rem)'}; left: ${direction === 'left' ? '2rem' : 'calc(100% - 2.8rem)'};
${Arrow} { ${Arrow} {
width: 1.5rem; width: 1.5rem;

@ -1,10 +1,13 @@
import { import {
useCallback, useCallback,
useEffect, useEffect,
useLayoutEffect,
useRef, useRef,
useState, useState,
} from 'react' } from 'react'
import isEmpty from 'lodash/isEmpty'
import { MatchesSlider } from 'features/MatchesSlider' import { MatchesSlider } from 'features/MatchesSlider'
import { TimelineTournamentList, useTimeline } from 'features/MatchesTimeline/hooks' import { TimelineTournamentList, useTimeline } from 'features/MatchesTimeline/hooks'
import { InfiniteScroll } from 'features/InfiniteScroll' import { InfiniteScroll } from 'features/InfiniteScroll'
@ -24,6 +27,10 @@ import {
} from './styled' } from './styled'
const TOURNAMENT_LIMIT = 10 const TOURNAMENT_LIMIT = 10
const MATCH_COUNT = 6
const PADDING = 30
const GAP_COUNT = 5
const SLIDER_ITEM_GAP = 0.9 // rem
export const MatchesTimeline = () => { export const MatchesTimeline = () => {
const { const {
@ -36,6 +43,25 @@ export const MatchesTimeline = () => {
const pageRef = useRef(0) const pageRef = useRef(0)
const isLastPageRef = useRef(false) const isLastPageRef = useRef(false)
const wrapperRef = useRef<HTMLDivElement>(null)
const [cardSize, setCardSize] = useState(0)
// вычисляем размеры плитки
useLayoutEffect(() => {
const offsetWidth = wrapperRef.current?.offsetWidth
const ulItemGapFromRem = SLIDER_ITEM_GAP * parseFloat(
getComputedStyle(document.documentElement).fontSize,
)
if (offsetWidth) {
const size = Math.round(
((offsetWidth - (ulItemGapFromRem * GAP_COUNT) - PADDING) / MATCH_COUNT),
)
setCardSize(size)
}
}, [])
const getTournaments = useCallback(() => tournamentList.slice( const getTournaments = useCallback(() => tournamentList.slice(
pageRef.current * TOURNAMENT_LIMIT, pageRef.current * TOURNAMENT_LIMIT,
@ -66,20 +92,23 @@ export const MatchesTimeline = () => {
} }
}, [getTournaments, tournamentList]) }, [getTournaments, tournamentList])
if (isTimelineFetching) {
return <Loading><T9n t='loading' />...</Loading>
}
return ( return (
<InfiniteScroll fullPageScroll onFetchMore={getMoreTournaments}> <InfiniteScroll fullPageScroll onFetchMore={getMoreTournaments}>
<Wrapper> <Wrapper ref={wrapperRef}>
{onlineUpcomingMatches.length > 0 && ( {isTimelineFetching && <Loading><T9n t='loading' />...</Loading>}
{(!isEmpty(onlineUpcomingMatches)) && (
<RowWrapper> <RowWrapper>
<Content> <Content>
<Tournament isOnlineUpcoming> <Tournament
size={cardSize}
isOnlineUpcoming
>
LIVE & UPCOMING LIVE & UPCOMING
</Tournament> </Tournament>
<MatchesSlider matches={onlineUpcomingMatches} /> <MatchesSlider
cardSize={cardSize}
matches={onlineUpcomingMatches}
/>
</Content> </Content>
</RowWrapper> </RowWrapper>
)} )}
@ -99,14 +128,20 @@ export const MatchesTimeline = () => {
/> />
</TournamentSubtitleWrapper> </TournamentSubtitleWrapper>
<Content> <Content>
<Tournament gradientColor={tournament.color}> <Tournament
size={cardSize}
gradientColor={tournament.color}
>
<TournamentLogo <TournamentLogo
id={tournament_id} id={tournament_id}
profileType={ProfileTypes.TOURNAMENTS} profileType={ProfileTypes.TOURNAMENTS}
sportType={sport_id} sportType={sport_id}
/> />
</Tournament> </Tournament>
<MatchesSlider matches={matches} /> <MatchesSlider
cardSize={cardSize}
matches={matches}
/>
</Content> </Content>
</RowWrapper> </RowWrapper>
))} ))}

@ -12,12 +12,12 @@ export const RowWrapper = styled.div``
export const Content = styled.div` export const Content = styled.div`
display: flex; display: flex;
gap: 10px; `
`
export const Tournament = styled.div<{ export const Tournament = styled.div<{
gradientColor?: string, gradientColor?: string,
isOnlineUpcoming?: boolean, isOnlineUpcoming?: boolean,
size?: number,
}>` }>`
${({ gradientColor }) => (gradientColor ${({ gradientColor }) => (gradientColor
? css`background: linear-gradient(187deg, ${gradientColor} -4.49%, #000000 68.29%), #000000;` ? css`background: linear-gradient(187deg, ${gradientColor} -4.49%, #000000 68.29%), #000000;`
@ -29,10 +29,10 @@ export const Tournament = styled.div<{
`} `}
position: relative; position: relative;
height: 12.9rem; height: ${({ size }) => (size ? `${size}px` : '12.9rem')};
width: 12.9rem; width: ${({ size }) => (size ? `${size}px` : '12.9rem')};
flex-shrink: 0; flex-shrink: 0;
margin: 10px 0; margin: 10px 0.93rem 10px 0px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -42,31 +42,6 @@ export const Tournament = styled.div<{
color: #FFFFFF; color: #FFFFFF;
text-align: start; text-align: start;
padding: 10px; padding: 10px;
@media screen and (min-width: 1920px) {
height: 13.8rem;
width: 13.8rem;
}
@media screen and (max-width: 1920px) {
height: 13.4rem;
width: 13.4rem;
}
@media screen and (max-width: 1600px) {
height: 13rem;
width: 13rem;
}
@media screen and (max-width: 1440px) {
height: 12.8rem;
width: 12.8rem;
}
@media screen and (max-width: 1280px) {
height: 12.6rem;
width: 12.6rem;
}
` `
export const TournamentLogo = styled(ProfileLogo)` export const TournamentLogo = styled(ProfileLogo)`

Loading…
Cancel
Save