-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: useAnimation의 AnimationWrapper가 div 타입 props를 받을 수 있도록 변경 * feat: 애니메이션을 지원하는useModal 기능 추가 * test: useModal 기능 테스트 추가 * docs: useModal Storybook 추가 * docs: README useModal 추가 * test: branch test coverage 개선 * feat: overlayClose옵션 추가 * test: overlayClose test 추가 * docs: Storybook, README 업데이트 * chore: UseModalAnimation type interface로 변경
- Loading branch information
Showing
12 changed files
with
480 additions
and
6 deletions.
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
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,77 @@ | ||
import { Canvas, Meta, Description } from '@storybook/blocks'; | ||
import * as Modal from './Modal.stories'; | ||
|
||
<Meta of={Modal} /> | ||
|
||
# useModal | ||
|
||
애니메이션이 적용된 Modal을 portal을 통해 간편하게 관리하기 위한 훅입니다. | ||
|
||
## 함수 인자 | ||
|
||
modalProps객체를 받습니다. 해당 객체는 아래와 같이 구성됩니다. | ||
|
||
```ts | ||
interface UseModalProps { | ||
modalRoot?: ModalRoot; | ||
overlayClose?: boolean; | ||
overlayAnimation?: { | ||
showClassName?: string; | ||
hideClassName?: string; | ||
}; | ||
modalAnimation?: { | ||
showClassName?: string; | ||
hideClassName?: string; | ||
}; | ||
} | ||
``` | ||
|
||
`modalRoot`: 모달을 렌더링할 HTMLElement입니다. default는 `document.body`입니다. | ||
|
||
`overlayClose`: overlay를 눌러 modal을 닫을지를 설정합니다. default는 `true`입니다. | ||
|
||
`overlayAnimation`: Overlay에 적용될 애니메이션 className입니다. `showClassName`과 `hideClassName` 두 가지 key-value를 받을 수 있습니다. | ||
|
||
`modalAnimation`: Modal에 적용될 애니메이션 className입니다. `showClassName`과 `hideClassName` 두 가지 key-value를 받을 수 있습니다. | ||
|
||
## 반환값 | ||
|
||
`Modal`: 컴포넌트로,해당 컴포넌트로 감싸진 children이 지정한 root에 portal을 통해 렌더링 됩니다. | ||
|
||
`show`: 모달을 엽니다. | ||
|
||
`hide`: 모달을 닫습니다. | ||
|
||
`isShow`: 모달이 열려있는지 상태를 나타냅니다. | ||
|
||
```tsx | ||
function Modal() { | ||
const { Modal, show, isShow, hide } = useModal({ | ||
modalAnimation: { | ||
showClassName: showStyle, | ||
hideClassName: hideStyle, | ||
}, | ||
overlayAnimation: { | ||
showClassName: overlayShow, | ||
hideClassName: overlayHide, | ||
}, | ||
}); | ||
|
||
const handleClick = () => { | ||
if (isShow) hide(); | ||
show(); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<button onClick={handleClick}>{isShow ? 'hide' : 'show'}</button> | ||
<Modal overlayClassName={Overlay} modalClassName={ModalContainer}> | ||
<div>모달!</div> | ||
<button onClick={hide}>닫기</button> | ||
</Modal> | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
<Canvas of={Modal.defaultStory} /> |
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,78 @@ | ||
import { keyframes, style } from '@vanilla-extract/css'; | ||
|
||
export const Overlay = style({ | ||
position: 'fixed', | ||
top: 0, | ||
left: 0, | ||
right: 0, | ||
bottom: 0, | ||
background: 'rgba(0, 0, 0, 0.5)', | ||
display: 'flex', | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
}); | ||
|
||
export const ModalContainer = style({ | ||
backgroundColor: 'white', | ||
padding: '30px 60px 30px 60px', | ||
borderRadius: 25, | ||
display: 'flex', | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
flexDirection: 'column', | ||
gap: 10, | ||
}); | ||
|
||
const showKeyframe = keyframes({ | ||
from: { | ||
opacity: 0, | ||
transform: 'scale(0)', | ||
}, | ||
to: { | ||
opacity: 1, | ||
transform: 'scale(1)', | ||
}, | ||
}); | ||
|
||
const hideKeyframe = keyframes({ | ||
from: { | ||
opacity: 1, | ||
transform: ' scale(1)', | ||
}, | ||
to: { | ||
opacity: 0, | ||
transform: 'scale(0)', | ||
}, | ||
}); | ||
const overlayShowKeyframe = keyframes({ | ||
from: { | ||
opacity: 0, | ||
}, | ||
to: { | ||
opacity: 1, | ||
}, | ||
}); | ||
|
||
const overlayHideKeyframe = keyframes({ | ||
from: { | ||
opacity: 1, | ||
}, | ||
to: { | ||
opacity: 0, | ||
}, | ||
}); | ||
|
||
export const showStyle = style({ | ||
animation: `${showKeyframe} 500ms forwards`, | ||
}); | ||
|
||
export const hideStyle = style({ | ||
animation: `${hideKeyframe} 500ms forwards`, | ||
}); | ||
|
||
export const overlayShow = style({ | ||
animation: `${overlayShowKeyframe} 500ms forwards`, | ||
}); | ||
export const overlayHide = style({ | ||
animation: `${overlayHideKeyframe} 500ms forwards`, | ||
}); |
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,21 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import Modal from './Modal'; | ||
|
||
const meta = { | ||
title: 'hooks/useModal', | ||
component: Modal, | ||
parameters: { | ||
layout: 'centered', | ||
docs: { | ||
canvas: {}, | ||
}, | ||
}, | ||
} satisfies Meta<typeof Modal>; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const defaultStory: Story = { | ||
args: {}, | ||
}; |
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,38 @@ | ||
import useModal from '@/useModal/useModal'; | ||
import React from 'react'; | ||
import { ModalContainer, Overlay, hideStyle, overlayHide, overlayShow, showStyle } from './Modal.css'; | ||
|
||
export default function Modal() { | ||
const { Modal, show, isShow, hide } = useModal({ | ||
modalAnimation: { | ||
showClassName: showStyle, | ||
hideClassName: hideStyle, | ||
}, | ||
overlayAnimation: { | ||
showClassName: overlayShow, | ||
hideClassName: overlayHide, | ||
}, | ||
}); | ||
|
||
const handleClick = () => { | ||
if (isShow) hide(); | ||
show(); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<button onClick={handleClick}>{isShow ? 'hide' : 'show'}</button> | ||
<Modal overlayClassName={Overlay} modalClassName={ModalContainer}> | ||
<div>모달!</div> | ||
<button | ||
style={{ | ||
fontSize: 10, | ||
}} | ||
onClick={hide} | ||
> | ||
닫기 | ||
</button> | ||
</Modal> | ||
</div> | ||
); | ||
} |
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
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
Oops, something went wrong.