공통 컴포넌트 만들기

장석원·2024년 5월 1일

메인퀘스트 제목 박스, 사이드퀘스트 제목 박스

아코디언 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;

0개의 댓글