아코디언 UI
import styled from 'styled-components';
import { BsThreeDots } from "react-icons/bs";
import { MdArrowDropDown } from "react-icons/md";
import { MdArrowDropUp } from "react-icons/md";
import { useState } from 'react';
import { sideQuestList } from '@/shared/dummy';
const MainBox = () => {
const [isAccordion, setisAccordion] = useState(false);
const [checked, setChecked] = useState(Array(sideQuestList.length).fill(false));
const handleCheckboxClick = (index: number) => {
setChecked(prevState => {
const newState = [...prevState];
newState[index] = !newState[index];
return newState;
})
};
const checkedCount = checked.reduce((count, isChecked) => isChecked ? count + 1 : count, 0);
const MainText = `메인 퀘스트 제목`;
const fraction = `${checkedCount} / ${sideQuestList.length}`;
const handleNavigate = () => {
}
const handleToggleAccordion = () => {
setisAccordion(prevState => !prevState);
}
return (
<>
<MainBoxStyle onClick={handleToggleAccordion}>
<header className='aFContainer'>
<button className='aButton'>{isAccordion ? (<MdArrowDropUp size={30} />) : (<MdArrowDropDown size={30} />)}</button>
<p className='fDisplay'>{fraction}</p>
</header>
<h1 className='title'>{MainText}</h1>
<button className='eButton' onClick={handleNavigate}><BsThreeDots /></button>
</MainBoxStyle>
{sideQuestList.map((quest, index) => (
<SideBoxStyle key={index} className={`sideBox ${isAccordion ? 'show' : ''}`}>
<label className='cBox'>
<input type='checkbox' checked={checked[index]} onChange={() => handleCheckboxClick(index)} />
</label>
<h2 className='sTitle'>{quest.content}</h2>
</SideBoxStyle>
))}
</>
);
};
const MainBoxStyle = styled.div`
width: 22rem;
height: 55px;
background: ${({ theme }) => theme.color.white};
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.1);
border-radius: ${({ theme }) => theme.borderRadius.medium};
display: flex;
justify-content: space-between;
align-items: center;
padding: 0px 10px;
cursor: pointer;
.aFContainer {
display: flex;
align-items: center;
gap: 10px;
}
.title {
width: 11rem;
}
`;
const SideBoxStyle = styled.div`
position: relative;
left: 23px;
margin-top: 5px;
display: flex;
align-items: center;
padding: 0px 10px;
width: 20.5rem;
height: 55px;
background: ${({ theme }) => theme.color.white};
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.1);
border-radius: ${({ theme }) => theme.borderRadius.medium};
gap: 20px;
transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
transform: translateY(-100%);
max-height: 0;
overflow: hidden;
opacity: 0;
&.show {
transform: translateY(0%);
max-height: 100%;
opacity: 1;
}
.cBox {
cursor: pointer;
}
`;
export default MainBox;




import styled from 'styled-components';
import { BsThreeDots } from "react-icons/bs";
import { subQuest } from '@/shared/dummy';
const SubBox = () => {
const handleModal = () => {
}
return (
<SubBoxStyle>
<h2 className='title'>{subQuest[0].content}</h2>
<button className='ellipsis' onClick={handleModal}><BsThreeDots /></button>
</SubBoxStyle>
);
};
const SubBoxStyle = styled.div`
width: 22rem;
height: 55px;
background: ${({ theme }) => theme.color.white};
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.1);
border-radius: ${({ theme }) => theme.borderRadius.medium};
display: flex;
justify-content: space-between;
align-items: center;
padding: 0px 10px 0px 20px;
gap: 20px;
margin-bottom: 5px;
.ellipsis {
cursor: pointer;
}
`;
export default SubBox;

import styled from 'styled-components';
const rank = {
"status": 200,
"message": "success",
"updated": 20200520-240000, // 업데이트 시각 [format: YYYYMMDD-HHMMSS]
"count": 3, // ranking 요소 개수
"ranking": [ //최대 100개 출력
{
"userId": 1,
"nickname": "하늘 다람쥐",
"point": 220000
},
{
"userId": 2,
"nickname": "무당 벌레",
"point": 190000
},
{
"userId": 3,
"nickname": "물푸레나무",
"point": 180000
}
]
}
const RankingBox = () => {
const profileURL = "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg";
return (
<>
{rank.ranking.map((rank, index) => (
<RankingBoxStyle key={index}>
<div className='rpContainer'>
<div className='rank'>{index + 1}</div>
<div className='rProfile'><img src={profileURL} alt="" /></div>
</div>
<div className='nlContainer'>
<div className='nickname'>{rank.nickname}</div>
<div className='lpContainer'>
<div className='level'>LV {Math.floor(rank.point / 1024)}</div>
<div className='point'>P {rank.point}</div>
</div>
</div>
</RankingBoxStyle>
))}
</>
);
};
const RankingBoxStyle = styled.div`
width: 22rem;
height: 61px;
background: ${({ theme }) => theme.color.white};
box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.25);
border-radius: ${({ theme }) => theme.borderRadius.small};
display: flex;
align-items: center;
padding: 10px;
font-size: ${({ theme }) => theme.font.xsmall};
margin-bottom: 5px;
.rpContainer {
width: 80px;
display: flex;
align-items: center;
gap: 25px;
}
.rank {
font-size: ${({ theme }) => theme.font.medium};
font-weight: bold;
padding-left: 10px;
}
.rProfile {
width: 30px;
height: 30px;
}
.rProfile img {
width: 100%;
height: 100%;
border-radius: 50%;
}
.nlContainer {
width: 233px;
display: flex;
flex-direction: column;
}
.nickname {
margin-bottom: 5px;
}
.lpContainer {
display: flex;
justify-content: space-between;
}
`;
export default RankingBox;
