-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix: Button 컴포넌트 스타일 오버라이드 가능하도록 수정 * feat: Drawer 구현
- Loading branch information
Showing
3 changed files
with
170 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import useDisclosure from '@/hooks/useDisclosure'; | ||
|
||
import Button from '@/v1/base/Button'; | ||
import Drawer from '@/v1/base/Drawer'; | ||
|
||
const meta: Meta<typeof Drawer> = { | ||
title: 'Base/Drawer', | ||
component: Drawer, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof Drawer>; | ||
|
||
const BaseDrawer = () => { | ||
const { isOpen, onOpen, onClose } = useDisclosure(); | ||
|
||
return ( | ||
<> | ||
<Button onClick={onOpen}>Drawer 열기</Button> | ||
<Drawer isOpen={isOpen} onClose={onClose}> | ||
<Drawer.Header> | ||
<Drawer.CloseButton position="top-right" /> | ||
<Drawer.Title text="Drawer Header" /> | ||
</Drawer.Header> | ||
<Drawer.Content> | ||
<p>Drawer Content</p> | ||
</Drawer.Content> | ||
</Drawer> | ||
</> | ||
); | ||
}; | ||
|
||
const AddCommentDrawer = () => { | ||
const { isOpen, onOpen, onClose } = useDisclosure(); | ||
|
||
return ( | ||
<> | ||
<Button onClick={onOpen}>모임 글 작성하기</Button> | ||
<Drawer isOpen={isOpen} onClose={onClose}> | ||
<Drawer.Header> | ||
<Drawer.CloseButton position="top-left" /> | ||
<Drawer.Title text="모임 게시글 작성하기" /> | ||
<Button | ||
colorScheme="main" | ||
fill={false} | ||
size="medium" | ||
className="flex-shrink-0 border-none !p-0" | ||
onClick={onClose} | ||
> | ||
완료 | ||
</Button> | ||
</Drawer.Header> | ||
<Drawer.Content> | ||
<textarea | ||
className="h-[60vh] w-full resize-none border-none text-md focus:outline-none" | ||
placeholder="책에 대한 여러분의 자유로운 생각을 들려주세요" | ||
/> | ||
</Drawer.Content> | ||
</Drawer> | ||
</> | ||
); | ||
}; | ||
|
||
export const Default: Story = { | ||
render: BaseDrawer, | ||
}; | ||
|
||
export const AddComment: Story = { | ||
render: AddCommentDrawer, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { createContext, PropsWithChildren, ReactNode, useContext } from 'react'; | ||
|
||
import { IconClose } from '@public/icons'; | ||
import Portal from './Portal'; | ||
|
||
interface DrawerProps { | ||
isOpen: boolean; | ||
onClose?: () => void; | ||
} | ||
|
||
type DrawerContextValue = DrawerProps; | ||
|
||
const DrawerContext = createContext({} as DrawerContextValue); | ||
const useDrawerContext = () => useContext(DrawerContext); | ||
|
||
const Drawer = ({ | ||
isOpen, | ||
onClose, | ||
children, | ||
}: PropsWithChildren<DrawerProps>) => { | ||
return ( | ||
<DrawerContext.Provider value={{ isOpen, onClose }}> | ||
<Portal id="drawer"> | ||
<div | ||
className={`absolute inset-0 z-10 transform overflow-hidden ease-in-out ${ | ||
isOpen | ||
? 'translate-x-0 scale-x-100 opacity-100 transition-opacity duration-500' | ||
: 'translate-x-full scale-x-0 opacity-0 transition-all delay-100 duration-500' | ||
}`} | ||
> | ||
<section | ||
className={`duration-400 absolute right-0 flex h-full w-full max-w-[43rem] transform flex-col gap-[2rem] overflow-hidden bg-white p-[2rem] shadow-bookcard transition-all ease-in-out ${ | ||
isOpen ? 'translate-x-0' : 'translate-x-full' | ||
}`} | ||
> | ||
{isOpen && children} | ||
</section> | ||
{/** overlay */} | ||
<section className="h-full w-full" onClick={onClose}></section> | ||
</div> | ||
</Portal> | ||
</DrawerContext.Provider> | ||
); | ||
}; | ||
|
||
const DrawerHeader = ({ children }: { children?: ReactNode }) => { | ||
return ( | ||
<div className="flex items-center justify-between py-[0.5rem]"> | ||
{children} | ||
</div> | ||
); | ||
}; | ||
|
||
const DrawerContent = ({ children }: { children?: ReactNode }) => { | ||
return <div className="w-full text-md">{children}</div>; | ||
}; | ||
|
||
const Title = ({ text }: { text?: string }) => { | ||
return ( | ||
<h1 className="flex-grow truncate pl-[2.5rem] text-center text-md"> | ||
{text} | ||
</h1> | ||
); | ||
}; | ||
|
||
type Position = 'top-left' | 'top-right'; | ||
|
||
const getPositionClasses = (postion: Position) => { | ||
switch (postion) { | ||
case 'top-right': | ||
return 'top-[2.7rem] right-[2rem]'; | ||
case 'top-left': | ||
default: | ||
return 'top-[2.7rem] left-[2rem]'; | ||
} | ||
}; | ||
|
||
const CloseButton = ({ position = 'top-left' }: { position?: Position }) => { | ||
const { onClose } = useDrawerContext(); | ||
const positionClasses = getPositionClasses(position); | ||
|
||
return ( | ||
<IconClose | ||
className={`absolute h-[2rem] w-[2rem] cursor-pointer fill-black-900 ${positionClasses}`} | ||
onClick={onClose} | ||
/> | ||
); | ||
}; | ||
|
||
Drawer.Header = DrawerHeader; | ||
Drawer.Content = DrawerContent; | ||
Drawer.Title = Title; | ||
Drawer.CloseButton = CloseButton; | ||
|
||
export default Drawer; |