-
Notifications
You must be signed in to change notification settings - Fork 5
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 #89 from codestates-seb/feat_ranking
feat: ranking 컴포넌트 구현(#86)-useusername1
- Loading branch information
Showing
4 changed files
with
263 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { useEffect, useState, useRef } from "react"; | ||
|
||
import { | ||
RankingWrapper, | ||
MainRankingWrapper, | ||
RankingItem, | ||
RankingTitle, | ||
RankingItemWrapper, | ||
RankingItemContent, | ||
} from "./style"; | ||
import rankingData from "../../data/RankingData"; | ||
import { RxDoubleArrowUp as DoubleUpIcon } from "react-icons/rx"; | ||
import { | ||
TiArrowSortedUp as UpIcon, | ||
TiArrowSortedDown as DownIcon, | ||
} from "react-icons/ti"; | ||
import { BsDash as DashIcon } from "react-icons/bs"; | ||
const Ranking = () => { | ||
const [currentAttraction, setCurrentAttraction] = useState(0); | ||
const [startAnimation, setStartAnimation] = useState(false); | ||
const timerIdRef = useRef<NodeJS.Timeout | null>(null); | ||
const newRankingData = [...rankingData, ...rankingData.slice(0, 1)]; | ||
useEffect(() => { | ||
timerIdRef.current = setInterval(() => { | ||
setStartAnimation(true); | ||
setTimeout(() => { | ||
setStartAnimation(false); | ||
setCurrentAttraction((p) => (p + 1) % 10); | ||
}, 700); | ||
}, 5000); | ||
return () => clearInterval(timerIdRef.current as NodeJS.Timeout); | ||
}, []); | ||
console.log(newRankingData.slice(9, 11 % 12), newRankingData); | ||
return ( | ||
<RankingWrapper> | ||
<MainRankingWrapper> | ||
<RankingTitle> | ||
지금 뜨는 곳<DoubleUpIcon className="doubleup-icon" /> | ||
</RankingTitle> | ||
<RankingItemWrapper startAnimation={startAnimation}> | ||
{newRankingData | ||
.slice(currentAttraction, (currentAttraction + 2) % 12) | ||
.map((el) => ( | ||
<RankingItem key={el.id}> | ||
<RankingItemContent currentRank> | ||
{el.currentRank} | ||
</RankingItemContent> | ||
<RankingItemContent name>{el.name}</RankingItemContent> | ||
<RankingItemContent address>{el.address}</RankingItemContent> | ||
<RankingItemContent rankOrder> | ||
<ArrowIconGenerator difference={el.rankOrder} /> | ||
</RankingItemContent> | ||
</RankingItem> | ||
))} | ||
</RankingItemWrapper> | ||
</MainRankingWrapper> | ||
</RankingWrapper> | ||
); | ||
}; | ||
|
||
interface ArrowIconGeneratorProps { | ||
difference: number; | ||
} | ||
const ArrowIconGenerator = ({ difference }: ArrowIconGeneratorProps) => { | ||
if (difference === 0) | ||
return ( | ||
<> | ||
<DashIcon /> | ||
</> | ||
); | ||
if (difference > 0) | ||
return ( | ||
<> | ||
<UpIcon className="up-icon" /> | ||
{difference} | ||
</> | ||
); | ||
if (difference < 0) | ||
return ( | ||
<> | ||
<DownIcon className="down-icon" /> | ||
{Math.abs(difference)} | ||
</> | ||
); | ||
return <></>; | ||
}; | ||
export default Ranking; |
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,101 @@ | ||
import styled, { css } from "styled-components"; | ||
const RankingWrapper = styled.div` | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
width: 100%; | ||
height: 120px; | ||
border: 1px solid black; | ||
background-color: white; | ||
`; | ||
const MainRankingWrapper = styled.ul` | ||
max-width: 785px; | ||
width: 80%; | ||
height: 55px; | ||
border-radius: 50px; | ||
background-color: white; | ||
display: flex; | ||
align-items: center; | ||
padding: 20px; | ||
overflow: hidden; | ||
`; | ||
const RankingTitle = styled.span` | ||
display: flex; | ||
align-items: center; | ||
margin-right: 25px; | ||
font-size: var(--font-sm); | ||
font-weight: var(--fw-bold); | ||
color: var(--black-800); | ||
.doubleup-icon { | ||
color: var(--purple-300); | ||
padding-left: 5px; | ||
width: 17px; | ||
height: 17px; | ||
} | ||
`; | ||
const RankingItemWrapper = styled.div<{ startAnimation: boolean }>` | ||
display: flex; | ||
flex-direction: column; | ||
flex: 1 1 auto; | ||
transition: ${(props) => (props.startAnimation ? "all 0.7s ease" : "none")}; | ||
transform: ${(props) => | ||
props.startAnimation ? "translateY(-60px)" : "none"}; | ||
`; | ||
const RankingItem = styled.li` | ||
transform: translateY(50px); | ||
display: flex; | ||
margin-bottom: 40px; | ||
`; | ||
const RankingItemContent = styled.span<{ | ||
currentRank?: boolean; | ||
name?: boolean; | ||
address?: boolean; | ||
rankOrder?: boolean; | ||
}>` | ||
display: flex; | ||
align-items: center; | ||
${(props) => | ||
props.currentRank && | ||
css` | ||
font-weight: var(--fw-bold); | ||
padding-right: 10px; | ||
font-size: var(--font-sm); | ||
`} | ||
${(props) => | ||
props.name && | ||
css` | ||
font-weight: var(--fw-medium); | ||
padding-top: 1px; | ||
margin-right: 15px; | ||
`} | ||
${(props) => | ||
props.address && | ||
css` | ||
font-size: var(--font-xs); | ||
color: var(--black-700); | ||
`} | ||
${(props) => | ||
props.rankOrder && | ||
css` | ||
font-size: var(--font-sm); | ||
margin-left: auto; | ||
`} | ||
svg.up-icon { | ||
color: var(--pink-heart); | ||
padding-right: 3px; | ||
width: 17px; | ||
} | ||
svg.down-icon { | ||
color: var(--black-400); | ||
padding-right: 3px; | ||
width: 17px; | ||
} | ||
`; | ||
export { | ||
RankingWrapper, | ||
RankingItemWrapper, | ||
MainRankingWrapper, | ||
RankingTitle, | ||
RankingItem, | ||
RankingItemContent, | ||
}; |
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,73 @@ | ||
const rankingData = [ | ||
{ | ||
id: 72, | ||
name: "N서울타워", | ||
address: "용상구 남산공원길 105", | ||
currentRank: 1, | ||
rankOrder: 0, | ||
}, | ||
{ | ||
id: 76, | ||
name: "남산공원", | ||
address: "중구 남산공원길 125-54", | ||
currentRank: 2, | ||
rankOrder: -1, | ||
}, | ||
{ | ||
id: 12, | ||
name: "서울식물원", | ||
address: "강서구 마곡동로 161 식물원", | ||
currentRank: 3, | ||
rankOrder: 0, | ||
}, | ||
{ | ||
id: 17, | ||
name: "어린이대공원", | ||
address: "광진구 능동로 216", | ||
currentRank: 4, | ||
rankOrder: 7, | ||
}, | ||
{ | ||
id: 50, | ||
name: "국립중앙도서관", | ||
address: "서초구 반포대로 201", | ||
currentRank: 5, | ||
rankOrder: 21, | ||
}, | ||
{ | ||
id: 90, | ||
name: "이화동 벽화마을", | ||
address: "종로구 이화장길 70-11", | ||
currentRank: 6, | ||
rankOrder: 10, | ||
}, | ||
{ | ||
id: 18, | ||
name: "커먼그라운드", | ||
address: "광진구 능동로 216", | ||
currentRank: 7, | ||
rankOrder: -3, | ||
}, | ||
{ | ||
id: 36, | ||
name: "노량진수산시장", | ||
address: "동작구 노들로 674 노량진수산물도매시장", | ||
currentRank: 8, | ||
rankOrder: 52, | ||
}, | ||
{ | ||
id: 59, | ||
name: "석촌호수공원", | ||
address: "송파구 잠실로 148", | ||
currentRank: 9, | ||
rankOrder: -2, | ||
}, | ||
{ | ||
id: 98, | ||
name: "용마폭포공원", | ||
address: "중랑구 용마산로 250-12", | ||
currentRank: 10, | ||
rankOrder: 100, | ||
}, | ||
]; | ||
export default rankingData; |