Skip to content

Commit

Permalink
Feat/유저 토너먼트 전적 페이지 리그 선택 버튼 #1079 (#1107)
Browse files Browse the repository at this point in the history
* [Fix] TournamentInfo 날짜 타입 수정 #1104

* [Feat] 토너먼트 정보 표시 #1104

* [Feat] 리그선택버튼 기능구현 #1079

* [Style] 활성 버튼 디자인 #1079

* [Feat] 활성 버튼 스타일 변경 #1079

* [Fix] 버튼 스타일 변경 시 위치 이동되지 않도록 고정크기 적용 #1079
  • Loading branch information
Clearsu authored Nov 23, 2023
1 parent 62a768f commit 11c26a9
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 89 deletions.
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

0 comments on commit 11c26a9

Please sign in to comment.