프론트엔드/Component

[Component] 2단 사이드바 컴포넌트(MUI + styled-components + Zustand)

순코딩 2024. 12. 26. 21:55

 

 

"use client";

import * as React from "react";
import SwipeableDrawer from "@mui/material/SwipeableDrawer";
import Button from "@mui/material/Button";
import { useSidebarStore } from "@/store/sidebarStore";
import SidebarItem from "./SidebarItem";
import styled from "styled-components";

const Sidebar = () => {
  const { items, items2, isOpen, open, close } = useSidebarStore();
  const isSecondOpen = useSidebarStore((state) => state.isSecondOpen);

  const RenderMenuArr = items.map((el, idx) => {
    return <SidebarItem key={idx} index={idx} text={el.text} isSelect={el.isSelect} query={el.query} type="major"/>;
  });

  const RenderMenuArr2 = items2.map((el, idx) => {
    return <SidebarItem key={idx} index={idx} text={el.text} isSelect={el.isSelect} query={el.query} type="middle"/>;
  });

  return (
    <>
      <Button onClick={() => open()}>사이드바 버튼</Button>
      <Container open={isOpen} onClose={close} onOpen={open}>
        <ItemContainer>
          <MajorCategoryContainer>{RenderMenuArr}</MajorCategoryContainer>
          {isSecondOpen && <MiddleCategoryContainer>{RenderMenuArr2}</MiddleCategoryContainer>}
        </ItemContainer>
      </Container>
    </>
  );
};

export default Sidebar;

const Container = styled(SwipeableDrawer)`
  /* 사이드바 컨테이너 선택자 */
  & > .MuiPaper-root {
    background-color: white;
  }
`;

const ItemContainer = styled.div`
  display: flex;
`;

const MajorCategoryContainer = styled.div``;

const MiddleCategoryContainer = styled.div`
`;

사이드바 컴포넌트

 

"use client";

import { useProductStore } from "@/store";
import { useSidebarStore } from "@/store/sidebarStore";
import { useRouter } from "next/navigation";
import styled from "styled-components";

type PropsType = {
  index: number;
  text: string;
  isSelect: boolean;
  query: string;
  type: string;
};

const SidebarItem = ({ index, text, isSelect, query, type }: PropsType) => {
  const setMajorCategory = useProductStore((state) => state.setMajorCategory);
  const setMiddleCategory = useProductStore((state) => state.setMiddleCategory);
  const { setItem, setItem2, close, secondOpen } = useSidebarStore();
  const router = useRouter();

  function majorHandleClick() {
    setMajorCategory(query);
    setItem(index);
    secondOpen();
  }

  function middleHandleClick() {
    setMiddleCategory(query);
    setItem2(index);
    router.push("/main");
    close();
  }

  return (
    <Container onClick={type === "major" ? majorHandleClick : middleHandleClick} $isSelect={isSelect}>
      {text}
    </Container>
  );
};

export default SidebarItem;

////////////////////////////// 스타일드 컴포넌트

type ContainerType = {
  $isSelect: boolean;
};

const Container = styled.div<ContainerType>`
  width: 150px;
  height: 50px;
  display: flex;
  align-items: center;
  padding: 0px 12px;
  font-size: 16px;
  background-color: ${({ $isSelect }) => ($isSelect === true ? "white" : "#F6F7F9")};
  color: ${({ $isSelect }) => ($isSelect === true ? "var(--black)" : "var(--gray)")};
`;

사이드바 아이템 컴포넌트

 

import { SidebarStoreType } from "@/types/store/sidebar.type";
import { create } from "zustand";

export const useSidebarStore = create<SidebarStoreType>((set) => ({
  isOpen: false,
  isSecondOpen:false,
  items: [
    { text: "제로 칼로리", isSelect: false, query: "zero_calories" },
    { text: "로우 칼로리", isSelect: false, query: "low_calories" },
    { text: "제로 슈가", isSelect: false, query: "zero_sugar" },
    { text: "로우 슈가", isSelect: false, query: "low_sugar" },
  ],
  items2: [
    { text: "전체", query: "", isSelect: false },
    { text: "음료", query: "beverage", isSelect: false },
    { text: "간식", query: "snack", isSelect: false },
    { text: "빵", query: "bread", isSelect: false },
    { text: "면", query: "noodle", isSelect: false },
    { text: "아이스크림", query: "ice_cream", isSelect: false },
    { text: "냉동", query: "frozen", isSelect: false },
    { text: "통조림", query: "canned", isSelect: false },
    { text: "소스", query: "sauce", isSelect: false },
    { text: "밥/죽", query: "rice_porridge", isSelect: false },
    { text: "도시락", query: "lunch_box", isSelect: false },
    { text: "유제품", query: "dairy", isSelect: false },
  ],
  setItem: (index) =>
    set((state) => ({
      items: state.items.map((el, i) => (i === index ? { ...el, isSelect: true } : { ...el, isSelect: false })),
    })),
  setItem2: (index) =>
    set((state) => ({
      items2: state.items2.map((el, i) => (i === index ? { ...el, isSelect: true } : { ...el, isSelect: false })),
    })),
  open: () => set((state) => ({ ...state, isOpen: true })),
  close: () => set((state) => ({ ...state, isOpen: false })),
  secondOpen: () => set((state) => ({ ...state, isSecondOpen: true })),
  secondClose: () => set((state) => ({ ...state, isSecondOpen: false })),
}));

사이드바 상태 스토어

 

export type SidebarStoreType = {
  isOpen: boolean;
  isSecondOpen:boolean;
  items: item[];
  items2: item[];
  setItem: (index : number) => void;
  setItem2: (index : number) => void;
  open: () => void;
  close: () => void;
  secondOpen: () => void;
  secondClose: () => void;
};

type item = {
  text:string;
  isSelect:boolean;
  query:string;
};

사이드바 상태 타입