프론트엔드/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;
};
사이드바 상태 타입