Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/유저 토너먼트 전적 페이지 리그 선택 버튼 #1079 #1107

Merged
40 changes: 40 additions & 0 deletions components/tournament-record/LeagueButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { SetStateAction, useState } from 'react';
import styles from 'styles/tournament-record/LeagueButtonGroup.module.scss';

interface LeagueButtonGroupProps {
onSelect: React.Dispatch<SetStateAction<string>>;
}

export default function LeagueButtonGroup({
onSelect,
}: LeagueButtonGroupProps) {
const [activeButton, setActiveButton] = useState('ROOKIE');

const handleClick = (league: string) => {
onSelect(league);
setActiveButton(league);
};

return (
<div className={styles.leagueButtonWrapper}>
<button
onClick={() => handleClick('ROOKIE')}
className={activeButton === 'ROOKIE' ? styles.active : ''}
>
Rookie
</button>
<button
onClick={() => handleClick('MASTER')}
className={activeButton === 'MASTER' ? styles.active : ''}
>
Master
</button>
<button
onClick={() => handleClick('CUSTOM')}
className={activeButton === 'CUSTOM' ? styles.active : ''}
>
Custom
</button>
</div>
);
}
10 changes: 8 additions & 2 deletions components/tournament-record/WinnerProfileImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ export default function WinnerProfileImage({
};
}, [swiper, slideIndex]);

function applyStyleAndSetTournamentInfo() {
useEffect(() => {
if (indexDiff === 0) {
setTournamentInfo(tournament);
}
}, [indexDiff, tournament, setTournamentInfo]);

function applyStyle() {
if (indexDiff === 0) {
setTournamentInfo(tournament);
return styles.firstLayer;
Expand All @@ -39,7 +45,7 @@ export default function WinnerProfileImage({
return (
<div
className={`${styles.winnerProfileImage} ${
indexDiff > -2 && indexDiff < 2 && applyStyleAndSetTournamentInfo()
indexDiff > -2 && indexDiff < 2 && applyStyle()
}`}
>
<Image
Expand Down
142 changes: 78 additions & 64 deletions components/tournament-record/WinnerSwiper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import React, { useMemo, useCallback, SetStateAction } from 'react';
import React, {
useMemo,
useCallback,
SetStateAction,
forwardRef,
Ref,
} from 'react';
import { EffectCoverflow } from 'swiper/modules';
import { Swiper, SwiperSlide, SwiperClass } from 'swiper/react';
import { Swiper, SwiperSlide, SwiperClass, SwiperRef } from 'swiper/react';
import { TournamentData, TournamentInfo } from 'types/tournamentTypes';
import { InfiniteScroll } from 'utils/infinityScroll';
import { mockInstance } from 'utils/mockAxios';
Expand All @@ -10,75 +16,83 @@ import 'swiper/css/effect-coverflow';
import WinnerProfileImage from './WinnerProfileImage';

interface WinnerSwiperProps {
type: 'ROOKIE' | 'MASTER' | 'CUSTOM';
type: string;
size: number;
setTournamentInfo: React.Dispatch<SetStateAction<TournamentInfo | undefined>>;
}

export default function WinnerSwiper(props: WinnerSwiperProps) {
const fetchTournamentData = useCallback(
async (page: number) => {
console.log('Fetching more data...');
const res = await mockInstance.get(
`/tournament?page=${page}&type=${props.type}&size=${props.size}`
const WinnerSwiper = forwardRef(
(props: WinnerSwiperProps, ref: Ref<SwiperRef> | undefined) => {
const fetchTournamentData = useCallback(
async (page: number) => {
console.log('Fetching more data...');
const res = await mockInstance.get(
`/tournament?page=${page}&type=${props.type}&size=${props.size}`
);
return res.data;
},
[props.type, props.size]
);

// TODO: error, isLoading 시 return 컴포넌트
const { data, error, isLoading, hasNextPage, fetchNextPage } =
InfiniteScroll<TournamentData>(
['tournamentData', props.type],
fetchTournamentData,
'JC01'
);
return res.data;
},
[props.type, props.size]
);

// TODO: error, isLoading 시 return 컴포넌트
const { data, error, isLoading, hasNextPage, fetchNextPage } =
InfiniteScroll<TournamentData>(
'tournamentData',
fetchTournamentData,
'JC01'
const coverflowEffect = useMemo(
() => ({
rotate: 35,
stretch: 0,
depth: 500,
slideShadows: true,
}),
[]
);

const indexChangeHandler = useCallback(
(swiper: SwiperClass) => {
const slidesLength = swiper.slides.length;
if (hasNextPage && swiper.activeIndex >= slidesLength - 3) {
fetchNextPage();
}
},
[hasNextPage, fetchNextPage]
);

const coverflowEffect = useMemo(
() => ({
rotate: 35,
stretch: 0,
depth: 500,
slideShadows: true,
}),
[]
);
return (
<Swiper
className={styles.swiper}
slidesPerView={2.6}
effect={'coverflow'}
centeredSlides={true}
coverflowEffect={coverflowEffect}
modules={[EffectCoverflow]}
onActiveIndexChange={indexChangeHandler}
ref={ref}
>
{data?.pages.map((page, pageIndex) => (
<React.Fragment key={pageIndex}>
{page.tournaments.length > 0 &&
page.tournaments.map((tournament, index) => (
<SwiperSlide key={tournament.tournamentId}>
<WinnerProfileImage
tournament={tournament}
slideIndex={index + pageIndex * props.size}
setTournamentInfo={props.setTournamentInfo}
/>
</SwiperSlide>
))}
</React.Fragment>
))}
</Swiper>
);
}
);

const indexChangeHandler = useCallback(
(swiper: SwiperClass) => {
const slidesLength = swiper.slides.length;
if (hasNextPage && swiper.activeIndex >= slidesLength - 3) {
fetchNextPage();
}
},
[hasNextPage, fetchNextPage]
);
// forwardRef에 들어가는 익명함수에 대한 name
WinnerSwiper.displayName = 'WinnerSwiper';

return (
<Swiper
className={styles.swiper}
slidesPerView={2.6}
effect={'coverflow'}
centeredSlides={true}
coverflowEffect={coverflowEffect}
modules={[EffectCoverflow]}
onActiveIndexChange={indexChangeHandler}
>
{data?.pages.map((page, pageIndex) => (
<React.Fragment key={pageIndex}>
{page.tournaments.length > 0 &&
page.tournaments.map((tournament, index) => (
<SwiperSlide key={tournament.tournamentId}>
<WinnerProfileImage
tournament={tournament}
slideIndex={index + pageIndex * props.size}
setTournamentInfo={props.setTournamentInfo}
/>
</SwiperSlide>
))}
</React.Fragment>
))}
</Swiper>
);
}
export default WinnerSwiper;
21 changes: 14 additions & 7 deletions pages/tournament-record.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import { useState } from 'react';
import { useState, useRef, useEffect } from 'react';
import { SwiperRef } from 'swiper/react';
import { TournamentInfo } from 'types/tournamentTypes';
import LeagueButtonGroup from 'components/tournament-record/LeagueButtonGroup';
import WinnerSwiper from 'components/tournament-record/WinnerSwiper';
import styles from 'styles/tournament-record/TournamentRecord.module.scss';

export default function TournamentRecord() {
const [tournamentInfo, setTournamentInfo] = useState<TournamentInfo>();
const [selectedType, setSelectedType] = useState<string>('ROOKIE');
const swiperRef = useRef<SwiperRef>(null);
const endTime = tournamentInfo ? new Date(tournamentInfo.endTime) : null;

useEffect(() => {
if (swiperRef) {
swiperRef.current?.swiper.slideTo(0, 0); // index, speed
}
}, [selectedType]);

return (
<div className={styles.pageWrap}>
<h1 className={styles.title}>명예의 전당</h1>
<div className={styles.leagueButtonWrapper}>
<button>Rookie</button>
<button>Master</button>
<button>Custom</button>
</div>
<LeagueButtonGroup onSelect={setSelectedType} />
<WinnerSwiper
type='ROOKIE'
type={selectedType}
size={5}
setTournamentInfo={setTournamentInfo}
ref={swiperRef}
/>
<div className={styles.winnerInfoContainer}>
<p className={styles.userId}>{tournamentInfo?.winnerIntraId}</p>
Expand Down
24 changes: 24 additions & 0 deletions styles/tournament-record/LeagueButtonGroup.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.leagueButtonWrapper {
display: flex;
padding-right: 2rem;
padding-left: 2rem;
margin-top: 1.4rem;
justify-content: space-between;

button {
width: 5rem;
height: 2rem;
font-size: 1rem;
font-weight: 400;
color: #cbcbcb;
background-color: transparent;
border-color: transparent;

&.active {
font-weight: 700;
color: #ffffff;
border: 2px solid #9e00ff;
border-radius: 0.6rem;
}
}
}
16 changes: 0 additions & 16 deletions styles/tournament-record/TournamentRecord.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,6 @@
justify-content: center;
}

.leagueButtonWrapper {
display: flex;
padding-right: 1.5rem;
padding-left: 1.5rem;
margin-top: 1.4rem;
justify-content: space-between;

button {
font-size: 1rem;
font-weight: 400;
color: #cbcbcb;
background-color: transparent;
border-color: transparent;
}
}

.winnerImageContainer {
height: 10rem;
margin-top: 1.8rem;
Expand Down