프론트엔드/Component

[Component] MUI 아코디언 컴포넌트 예시 코드

순코딩 2025. 3. 4. 14:33

 

믹스인 코드

src / styles / mixins.ts
import { css } from '@emotion/react';

// 믹스인 정의
export const mixinFlex = (direction : "row" | "column") => css`
  display: flex;
  flex-direction: ${direction};
  align-items: center;
  justify-content: center;
`;

 

컴포넌트 코드

"use client";

import { Accordion, AccordionDetails, AccordionSummary, styled, Typography } from "@mui/material";
import React from "react";
import ExpandMoreOutlinedIcon from "@mui/icons-material/ExpandMoreOutlined";
import { mixinFlex } from "@/styles/mixins";

type AccordionByCollegeType = {
  title: string;
  majors: string[];
  Icon: React.ReactElement;
};

const AccordionByCollege = ({ title, majors, Icon }: AccordionByCollegeType) => {
  return (
    <Container>
      <CollegeAccordion>
        <Summary expandIcon={<ExpandMoreOutlinedIcon />} aria-controls="panel1-content" id="panel1-header">
          <SummaryIconTextWrapper>
            {Icon}
            <Typography variant="body2">{title}</Typography>
          </SummaryIconTextWrapper>
        </Summary>
        <Detail>
          {majors.map((el, idx) => (
            <DetailBox key={idx}>
              {Icon}
              {el}
            </DetailBox>
          ))}
        </Detail>
      </CollegeAccordion>
    </Container>
  );
};

export default AccordionByCollege;

const Container = styled("div")`
  ${mixinFlex("column")};
  flex: 1;
  flex-basis: 150px;
  height: auto;
`;
const CollegeAccordion = styled(Accordion)`
  width: 100%;
  border-radius: 8px;
  box-shadow: none !important;
  &::before {
    display: none;
  }
  & svg {
    color: ${({ theme }) => theme.palette.primary.main};
  }
`;

const Summary = styled(AccordionSummary)``;

const SummaryIconTextWrapper = styled("div")`
  ${mixinFlex("row")}
  column-gap:8px;
`;

const Detail = styled(AccordionDetails)`
  ${mixinFlex("column")};
  align-items: start;
  justify-content: start;
  row-gap: 8px;
  margin-left: 16px;
`;

const DetailBox = styled("div")`
  ${mixinFlex("row")}
  width:100%;
  justify-content: start;
  column-gap: 8px;
  font-size: 14px;
  & svg {
    width: 16px;
    height: 16px;
  }
  &:hover {
    background-color: ${({ theme }) => theme.palette.primary.main};
    color: ${({ theme }) => theme.palette.gray[0]};
    & svg {
      color: ${({ theme }) => theme.palette.gray[0]};
    }
  }
`;

 

 

컴포넌트 사용 예시 코드

"use client";

import { styled, Typography } from "@mui/material";
import AccordionByCollege from "./AccordionByCollege";
import {
  GavelOutlined,
  MonitorHeartOutlined,
  AccountBalanceOutlined,
  SettingsOutlined,
  TimelineOutlined,
  PsychologyOutlined,
  ScienceOutlined,
  PaletteOutlined,
  SchoolOutlined,
  GrassOutlined,
  WavesOutlined,
} from "@mui/icons-material";
import { mixinFlex } from "@/styles/mixins";

const MainContainer = () => {
  const colleges = [
    { title: "인문대학", majors: ["철학", "역사학", "문학", "언어학", "사회학"], Icon: <AccountBalanceOutlined /> },
    { title: "사회과학대학", majors: ["정치학", "경제학", "행정학", "국제학", "심리학"], Icon: <PsychologyOutlined /> },
    { title: "자연과학대학", majors: ["물리학", "화학", "생물학", "수학", "지구과학"], Icon: <ScienceOutlined /> },
    {
      title: "공과대학",
      majors: ["기계공학", "전기전자공학", "컴퓨터공학", "화학공학", "토목공학"],
      Icon: <SettingsOutlined />,
    },
    { title: "의과대학", majors: ["의학", "간호학", "의생명과학", "약학"], Icon: <MonitorHeartOutlined /> },
    { title: "법과대학", majors: ["법학", "경찰행정학", "사회복지학"], Icon: <GavelOutlined /> },
    { title: "예술대학", majors: ["미술", "음악", "무용", "연극", "디자인"], Icon: <PaletteOutlined /> },
    { title: "경상대학", majors: ["경영학", "회계학", "마케팅", "금융학"], Icon: <TimelineOutlined /> },
    { title: "교육대학", majors: ["초등교육", "유아교육", "특수교육"], Icon: <SchoolOutlined /> },
    { title: "농업대학", majors: ["농업경제학", "농업생명과학", "환경농업"], Icon: <GrassOutlined /> },
    { title: "해양과학대학", majors: ["해양학", "해양공학", "해양경찰학"], Icon: <WavesOutlined /> },
  ];

  return (
    <div>
      <Typography variant="h3" color="primary" align="center" sx={{ fontWeight: "bold" }}>
        ALLIN
      </Typography>
      <AccordionByCollegeContainer>
        {colleges.map((el, idx) => (
          <AccordionByCollege key={idx} title={el.title} majors={el.majors} Icon={el.Icon} />
        ))}
      </AccordionByCollegeContainer>
    </div>
  );
};

export default MainContainer;

const AccordionByCollegeContainer = styled("div")`
  ${mixinFlex("row")};
  align-items:start;
  width: 100%;
  flex-wrap: wrap;
`;