-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from hufs-sports-live/feat/match-panel
[FEAT] match panel 컴포넌트 생성
- Loading branch information
Showing
17 changed files
with
318 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,71 @@ | ||
'use client'; | ||
|
||
// import Link from 'next/link'; | ||
|
||
import { Suspense } from 'react'; | ||
|
||
import MatchBanner from '@/components/match/Banner'; | ||
import Cheer from '@/components/match/Cheer'; | ||
// import CommentList from '@/components/detail/CommentList'; | ||
// import GameInfo from '@/components/detail/GameInfo'; | ||
// import GameTimeline from '@/components/detail/GameTimeline'; | ||
import Lineup from '@/components/match/LineupList'; | ||
import Panel from '@/components/match/Panel'; | ||
import RecordList from '@/components/match/RecordList'; | ||
import Video from '@/components/match/Video'; | ||
import MatchByIdFetcher from '@/queries/useMatchById/Fetcher'; | ||
import MatchCheerByIdFetcher from '@/queries/useMatchCheerById/Fetcher'; | ||
import MatchLineupFetcher from '@/queries/useMatchLineupById/Fetcher'; | ||
import MatchTimelineFetcher from '@/queries/useMatchTimelineById/Fetcher'; | ||
|
||
export default function Match({ params }: { params: { id: string } }) { | ||
const options = [ | ||
{ label: '라인업' }, | ||
{ label: '응원댓글' }, | ||
{ label: '경기영상' }, | ||
{ label: '타임라인' }, | ||
]; | ||
|
||
return ( | ||
<section> | ||
<Suspense fallback={<div>배너 로딩 중...</div>}> | ||
<Suspense fallback={<div>배너 로딩중...</div>}> | ||
<MatchByIdFetcher matchId={params.id}> | ||
{data => <MatchBanner {...data} />} | ||
</MatchByIdFetcher> | ||
</Suspense> | ||
<Suspense fallback={<div>응원 로딩 중...</div>}> | ||
<Suspense fallback={<div>응원 로딩중...</div>}> | ||
<MatchCheerByIdFetcher matchId={params.id}> | ||
{data => <Cheer cheers={data} />} | ||
</MatchCheerByIdFetcher> | ||
</Suspense> | ||
|
||
<Panel options={options} defaultValue="라인업"> | ||
{({ selected }) => ( | ||
<Suspense fallback={<div>로딩중...</div>}> | ||
{selected === '라인업' && ( | ||
<MatchLineupFetcher matchId={params.id}> | ||
{([firstTeam, secondTeam]) => ( | ||
<div className="grid grid-cols-2 py-5 [&>*:first-child>ul]:before:absolute [&>*:first-child>ul]:before:right-0 [&>*:first-child>ul]:before:h-full [&>*:first-child>ul]:before:border-l-2 [&>*:first-child>ul]:before:bg-gray-2"> | ||
<Lineup {...firstTeam} /> | ||
<Lineup {...secondTeam} /> | ||
</div> | ||
)} | ||
</MatchLineupFetcher> | ||
)} | ||
{selected === '타임라인' && ( | ||
<MatchTimelineFetcher matchId={params.id}> | ||
{([firstHalf, secondHalf]) => ( | ||
<div className="overflow-y-auto p-5"> | ||
<RecordList {...firstHalf} /> | ||
<RecordList {...secondHalf} /> | ||
</div> | ||
)} | ||
</MatchTimelineFetcher> | ||
)} | ||
{selected === '경기영상' && ( | ||
<div className="overflow-y-auto p-5"> | ||
{/* // TODO VideoId API 업데이트 시 ID를 받아와서 주입하는 형태로 수정 */} | ||
<Video /> | ||
</div> | ||
)} | ||
</Suspense> | ||
)} | ||
</Panel> | ||
</section> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import Item from './units/Item'; | ||
import Menu from './units/Menu'; | ||
import DropdownWrapper from './units/Wrapper'; | ||
|
||
export const Dropdown = Object.assign(DropdownWrapper, { | ||
Menu, | ||
Item, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
'use client'; | ||
|
||
import { ComponentProps, MouseEvent } from 'react'; | ||
|
||
interface DropdownItemProps extends ComponentProps<'button'> { | ||
onClick: (e: MouseEvent<HTMLButtonElement>) => void; | ||
} | ||
|
||
export default function Item({ children, ...props }: DropdownItemProps) { | ||
return <button {...props}>{children}</button>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { ComponentProps } from 'react'; | ||
|
||
import { $ } from '@/utils/core'; | ||
|
||
export default function Menu({ | ||
className, | ||
children, | ||
...props | ||
}: ComponentProps<'div'>) { | ||
return ( | ||
<div className={$('flex items-center', className)} {...props}> | ||
{children} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { ComponentProps, createContext } from 'react'; | ||
|
||
interface DropdownProps extends DropdownContextType, ComponentProps<'div'> {} | ||
|
||
type DropdownContextType = { | ||
label?: string; | ||
}; | ||
|
||
export const DropdownContext = createContext<DropdownContextType>({ | ||
label: '', | ||
}); | ||
|
||
export default function DropdownWrapper({ | ||
className, | ||
children, | ||
...props | ||
}: DropdownProps) { | ||
return ( | ||
<DropdownContext.Provider value={{ ...props }}> | ||
<div className={className}>{children}</div> | ||
</DropdownContext.Provider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { MatchPlayerType } from '@/types/match'; | ||
|
||
export default function LineupItem({ | ||
playerName, | ||
description, | ||
}: MatchPlayerType) { | ||
return ( | ||
<li | ||
className="mb-2 grid items-center gap-4" | ||
style={{ gridTemplateColumns: 'minmax(0, 30px) 1fr' }} | ||
> | ||
<span className="flex aspect-square items-center justify-center rounded-full bg-secondary leading-relaxed text-primary"> | ||
{description} | ||
</span> | ||
<span className="text-gray-5">{playerName}</span> | ||
</li> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { MatchLineupType } from '@/types/match'; | ||
|
||
import LineupItem from '../LineupItem'; | ||
|
||
export default function Lineup({ teamName, gameTeamPlayers }: MatchLineupType) { | ||
return ( | ||
<div> | ||
<div className="mb-3 px-4 text-primary">{teamName}</div> | ||
<ul className="relative px-4"> | ||
{gameTeamPlayers.map((player, idx) => ( | ||
<LineupItem key={player.playerName + idx} {...player} /> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { MouseEvent, ReactNode, useState } from 'react'; | ||
|
||
import { Dropdown } from '@/components/common/Dropdown'; | ||
import { $ } from '@/utils/core'; | ||
|
||
type PanelProps = { | ||
options: Array<{ label: string }>; | ||
children: ({ selected }: { selected: string }) => ReactNode; | ||
defaultValue: string; | ||
}; | ||
|
||
export default function Panel({ defaultValue, options, children }: PanelProps) { | ||
const [selected, setSelected] = useState(defaultValue); | ||
|
||
const handleClickItem = (e: MouseEvent<HTMLButtonElement>) => { | ||
const selectedValue = (e.target as Element).textContent; | ||
|
||
if (!selectedValue) return; | ||
if (selected === selectedValue) return; | ||
|
||
setSelected(selectedValue); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Dropdown className="relative rounded-xl border-2 border-gray-2"> | ||
<Dropdown.Menu className="grid w-full grid-cols-4 rounded-lg bg-gray-2 text-gray-4"> | ||
{options.map(option => ( | ||
<Dropdown.Item | ||
onClick={handleClickItem} | ||
key={option.label} | ||
className={$( | ||
'rounded-xl py-3', | ||
selected === option.label ? 'bg-secondary text-primary' : '', | ||
)} | ||
> | ||
{option.label} | ||
</Dropdown.Item> | ||
))} | ||
</Dropdown.Menu> | ||
{children({ selected })} | ||
</Dropdown> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { MatchRecordsType } from '@/types/match'; | ||
|
||
export default function RecordItem({ | ||
playerName, | ||
score, | ||
scoredAt, | ||
teamName, | ||
}: MatchRecordsType) { | ||
return ( | ||
<li className="relative mb-3 ms-4"> | ||
<time className="absolute -left-1/2">{scoredAt}</time> | ||
<span>[ {teamName} ] </span> | ||
<span> | ||
{playerName} 선수 {score}점 득점 🎉 | ||
</span> | ||
</li> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { MatchTimelineType } from '@/types/match'; | ||
|
||
import RecordItem from '../RecordItem'; | ||
|
||
export default function RecordList({ | ||
gameQuarter, | ||
records, | ||
}: MatchTimelineType) { | ||
return ( | ||
<> | ||
<div className="mb-3 text-primary">{gameQuarter}</div> | ||
<ol className="ms-5 border-s"> | ||
{records.map(record => ( | ||
<RecordItem key={record.scoredAt} {...record} /> | ||
))} | ||
</ol> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export default function Video() { | ||
return ( | ||
<iframe | ||
className="aspect-video w-full" | ||
src="https://www.youtube.com/embed/rLtgA5qQryM?si=w-py0FFuiHisx-k-" | ||
title="Match Video" | ||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" | ||
allowFullScreen | ||
></iframe> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { ReactNode } from 'react'; | ||
|
||
import { MatchLineupType } from '@/types/match'; | ||
|
||
import { useMatchLineupById } from './query'; | ||
|
||
type MatchLineupFetcherProps = { | ||
matchId: string; | ||
children: (data: MatchLineupType[]) => ReactNode; | ||
}; | ||
|
||
export default function MatchLineupFetcher({ | ||
matchId, | ||
children, | ||
}: MatchLineupFetcherProps) { | ||
const { lineup, error } = useMatchLineupById(matchId); | ||
|
||
if (error) throw error; | ||
|
||
return children(lineup); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { useSuspenseQuery } from '@tanstack/react-query'; | ||
|
||
import { getMatchLineupById } from '@/api/match'; | ||
|
||
export const useMatchLineupById = (matchId: string) => { | ||
const { data, error } = useSuspenseQuery({ | ||
queryKey: ['match-lineup', matchId], | ||
queryFn: () => getMatchLineupById(matchId), | ||
}); | ||
|
||
return { | ||
lineup: data, | ||
error, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { ReactNode } from 'react'; | ||
|
||
import { useMatchTimelineById } from './query'; | ||
|
||
type MatchRecordsType = { | ||
scoredAt: number; | ||
playerName: string; | ||
teamName: string; | ||
score: number; | ||
}; | ||
|
||
type MatchTimelineType = { | ||
gameQuarter: string; | ||
records: MatchRecordsType[]; | ||
}; | ||
|
||
type MatchTimelineFetcherProps = { | ||
matchId: string; | ||
children: (data: MatchTimelineType[]) => ReactNode; | ||
}; | ||
|
||
export default function MatchTimelineFetcher({ | ||
matchId, | ||
children, | ||
}: MatchTimelineFetcherProps) { | ||
const { timeline, error } = useMatchTimelineById(matchId); | ||
|
||
if (error) throw error; | ||
|
||
return children(timeline); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { useSuspenseQuery } from '@tanstack/react-query'; | ||
|
||
import { getMatchTimelineById } from '@/api/match'; | ||
|
||
export const useMatchTimelineById = (matchId: string) => { | ||
const { data, error } = useSuspenseQuery({ | ||
queryKey: ['match-timeline', matchId], | ||
queryFn: () => getMatchTimelineById(matchId), | ||
}); | ||
|
||
if (error) throw error; | ||
|
||
return { | ||
timeline: data, | ||
error, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters