Skip to content

Commit

Permalink
[#470] Menu 컴포넌트 (#471)
Browse files Browse the repository at this point in the history
  • Loading branch information
gxxrxn committed Jun 17, 2024
1 parent 81bc6d8 commit 0a4705d
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 2 deletions.
2 changes: 1 addition & 1 deletion public/icons/hamburger.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions src/stories/base/Menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Meta, StoryObj } from '@storybook/react';
import Menu from '@/v1/base/Menu';

const meta: Meta<typeof Menu> = {
title: 'Base/Menu',
component: Menu,
};

export default meta;

type Story = StoryObj<typeof Menu>;

export const Dropdown: Story = {
render: () => {
return (
<div className="flex justify-end">
<Menu>
<Menu.Toggle />
<Menu.DropdownList>
<Menu.Item>수정하기</Menu.Item>
<Menu.Item>삭제하기</Menu.Item>
</Menu.DropdownList>
</Menu>
</div>
);
},
};

export const Bottomsheet: Story = {
render: () => {
return (
<Menu>
<Menu.Toggle />
<Menu.BottomSheetList>
<Menu.Item>수정하기</Menu.Item>
<Menu.Item>삭제하기</Menu.Item>
</Menu.BottomSheetList>
</Menu>
);
},
};
2 changes: 1 addition & 1 deletion src/v1/base/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const BottomSheet = ({
leaveFrom="translate-y-0"
leaveTo="translate-y-full"
>
<Dialog.Panel className="pointer-events-auto relative max-h-[100dvh] w-[43rem] rounded-t-[1.5rem] bg-white p-[2rem]">
<Dialog.Panel className="pointer-events-auto relative max-h-[100dvh] w-[43rem] rounded-t-[1.5rem] bg-white px-[1rem] py-[1.5rem]">
{children}
</Dialog.Panel>
</Transition.Child>
Expand Down
94 changes: 94 additions & 0 deletions src/v1/base/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use client';

import {
PropsWithChildren,
createContext,
useCallback,
useContext,
useMemo,
useState,
} from 'react';
import { IconHamburger } from '@public/icons';
import BottomSheet from './BottomSheet';

type MenuContextValue = {
isOpen: boolean;
toggle: () => void;
};

const MenuContext = createContext({} as MenuContextValue);

const Menu = ({ children }: { children?: React.ReactNode }) => {
const [isOpen, setOpen] = useState(false);
const toggle = useCallback(() => setOpen(prev => !prev), []);
const value = useMemo(() => ({ isOpen, toggle }), [isOpen, toggle]);

return (
<div className="relative">
<MenuContext.Provider value={value}>{children}</MenuContext.Provider>
</div>
);
};

const Toggle = () => {
const { toggle } = useContext(MenuContext);

return (
<div
className="flex h-[2.3rem] w-[2.3rem] cursor-pointer items-center justify-center"
onClick={toggle}
>
<IconHamburger className="h-[2rem] w-[2rem] hover:fill-black-500" />
</div>
);
};

const BottomSheetList = ({ children }: { children?: React.ReactNode }) => {
const { isOpen, toggle } = useContext(MenuContext);
return (
<BottomSheet isShow={isOpen} onClose={toggle}>
{children}
</BottomSheet>
);
};

const DropdownList = ({ children }: { children?: React.ReactNode }) => {
const { isOpen } = useContext(MenuContext);
return (
<>
{isOpen && (
<ul className="absolute right-0 top-[3rem] min-w-[10rem] rounded-[0.5rem] py-[0.5rem] shadow-[0_0_15px_rgba(0,0,0,0.05),0_1px_2px_rgba(0,0,0,0.1)]">
{children}
</ul>
)}
</>
);
};

const Item = ({
onSelect,
children,
}: PropsWithChildren<{ onSelect?: () => void }>) => {
const { toggle } = useContext(MenuContext);

const handleItemClick = () => {
toggle();
onSelect && onSelect();
};

return (
<li
className="block cursor-pointer list-none truncate whitespace-nowrap rounded-[0.5rem] px-[1rem] py-[0.7rem] text-sm hover:bg-black-100"
onClick={handleItemClick}
>
{children}
</li>
);
};

Menu.Toggle = Toggle;
Menu.BottomSheetList = BottomSheetList;
Menu.DropdownList = DropdownList;
Menu.Item = Item;

export default Menu;

0 comments on commit 0a4705d

Please sign in to comment.