From 3f15a5b0a18075048e01128c4754f11dc204725c Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:06:47 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20useAnimation=EC=9D=98=20Animation?= =?UTF-8?q?Wrapper=EA=B0=80=20div=20=ED=83=80=EC=9E=85=20props=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/useAnimation/useAnimation.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/useAnimation/useAnimation.tsx b/src/useAnimation/useAnimation.tsx index 8aa34ac..4b72630 100644 --- a/src/useAnimation/useAnimation.tsx +++ b/src/useAnimation/useAnimation.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties, ReactNode, useState } from 'react'; +import React, { useState } from 'react'; export function _useAnimation(mountAnimationClassName?: string, unmountAnimationClassName?: string, unmountCallback?: () => void) { const [animationClassName, setAnimationClassName] = useState(mountAnimationClassName); @@ -31,10 +31,10 @@ export default function useAnimation({ mountClassName, unmountClassName }: { mou const show = () => setIsShow(true); const hide = () => triggerUnmountAnimation(); - const AnimationWrapper = ({ children, style, className }: { children: ReactNode; style?: CSSProperties; className?: string }) => { + const AnimationWrapper = ({ children, className, ...rest }: { className?: string } & React.ComponentProps<'div'>) => { return ( isShow && ( -
+
{children}
) From c5f3798a8580bafef831737e11e33a670438f13d Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:07:49 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20=EC=95=A0=EB=8B=88=EB=A9=94?= =?UTF-8?q?=EC=9D=B4=EC=85=98=EC=9D=84=20=EC=A7=80=EC=9B=90=ED=95=98?= =?UTF-8?q?=EB=8A=94useModal=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/useModal/useModal.tsx | 63 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/useModal/useModal.tsx diff --git a/src/useModal/useModal.tsx b/src/useModal/useModal.tsx new file mode 100644 index 0000000..8e5df0f --- /dev/null +++ b/src/useModal/useModal.tsx @@ -0,0 +1,63 @@ +import React, { CSSProperties, ReactNode } from 'react'; +import { createPortal } from 'react-dom'; +import { useAnimation } from '..'; +type ModalRoot = Element | DocumentFragment; + +export type UseModalAnimations = { + overlayAnimation?: { + showClassName?: string; + hideClassName?: string; + }; + modalAnimation?: { + showClassName?: string; + hideClassName?: string; + }; +}; + +interface UseModalProps extends UseModalAnimations { + modalRoot?: ModalRoot; +} + +interface ModalProps { + overlayClassName?: string; + modalClassName?: string; + style?: CSSProperties; + children: ReactNode; +} + +const DefaultModal = ({ children, modalRoot }: { children: ReactNode; modalRoot?: ModalRoot }) => { + return createPortal(children, modalRoot || document.body); +}; + +export default function useModal(modalProps?: UseModalProps) { + const ModalAnimation = useAnimation({ + mountClassName: modalProps?.modalAnimation?.showClassName, + unmountClassName: modalProps?.modalAnimation?.hideClassName, + }); + const OverlayAnimation = useAnimation({ + mountClassName: modalProps?.overlayAnimation?.showClassName, + unmountClassName: modalProps?.overlayAnimation?.hideClassName, + }); + + const show = () => { + OverlayAnimation.show(); + ModalAnimation.show(); + }; + + const hide = () => { + OverlayAnimation.hide(); + ModalAnimation.hide(); + }; + + const Modal = ({ children, overlayClassName, modalClassName, style }: ModalProps) => ( + + + e.stopPropagation()}> + {children} + + + + ); + + return { Modal, show, hide, isShow: OverlayAnimation.isShow }; +} From 0c9feb681f23dbb4a9240971e52d9512a5e32e79 Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:08:14 +0900 Subject: [PATCH 03/10] =?UTF-8?q?test:=20useModal=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/useModal/useModal.test.tsx | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/useModal/useModal.test.tsx diff --git a/src/useModal/useModal.test.tsx b/src/useModal/useModal.test.tsx new file mode 100644 index 0000000..f7addbe --- /dev/null +++ b/src/useModal/useModal.test.tsx @@ -0,0 +1,86 @@ +import useModal from './useModal'; +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; + +const SHOW_CLASSNAME = 'show'; +const HIDE_CLASSNAME = 'hide'; + +describe('useModal 기능 테스트', () => { + it('modal을 열면 portal을 통해 element 내부에 렌더링 할 수 있다.', async () => { + const Test = () => { + const { Modal, show, hide } = useModal(); + return ( +
+ + + +
+
+
+ ); + }; + render(); + fireEvent.click(await screen.findByRole('show')); + expect(await screen.findByRole('test')).toBeTruthy(); + }); + + it('지정한 element에 modal을 렌더링할 수 있다.', async () => { + const testRoot = document.createElement('div'); + testRoot.id = 'test-root'; + document.body.appendChild(testRoot); + + const Test = () => { + const { Modal, show, hide } = useModal({ + modalRoot: testRoot, + }); + return ( +
+ + + +
+
+
+ ); + }; + render(); + fireEvent.click(await screen.findByRole('show')); + expect(testRoot.querySelector('#test')).toBeTruthy(); + }); + + it('modal에 animation이 적용된 경우, className을 열고 닫을 때 변경할 수 있다.', async () => { + const Test = () => { + const { Modal, show, hide } = useModal({ + modalAnimation: { + showClassName: SHOW_CLASSNAME, + hideClassName: HIDE_CLASSNAME, + }, + }); + return ( +
+ + + +
+
+
+ ); + }; + render(); + fireEvent.click(await screen.findByRole('show')); + const modalInner = await screen.findByRole('test'); + expect(modalInner.parentElement?.className).toContain(SHOW_CLASSNAME); + }); +}); From 5be0d8ec109c7421f87158587ed01c858f947c01 Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:16:15 +0900 Subject: [PATCH 04/10] =?UTF-8?q?docs:=20useModal=20Storybook=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stories/useModal/Docs.mdx | 58 ++++++++++++++++++++ src/stories/useModal/Modal.css.ts | 78 +++++++++++++++++++++++++++ src/stories/useModal/Modal.stories.ts | 21 ++++++++ src/stories/useModal/Modal.tsx | 38 +++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 src/stories/useModal/Docs.mdx create mode 100644 src/stories/useModal/Modal.css.ts create mode 100644 src/stories/useModal/Modal.stories.ts create mode 100644 src/stories/useModal/Modal.tsx diff --git a/src/stories/useModal/Docs.mdx b/src/stories/useModal/Docs.mdx new file mode 100644 index 0000000..daf3280 --- /dev/null +++ b/src/stories/useModal/Docs.mdx @@ -0,0 +1,58 @@ +import { Canvas, Meta, Description } from '@storybook/blocks'; +import * as Modal from './Modal.stories'; + + + +# useModal + +애니메이션이 적용된 Modal을 portal을 통해 간편하게 관리하기 위한 훅입니다. + +## 함수 인자 + +`modalRoot`: 모달을 렌더링할 HTMLElement입니다. document.body가 default입니다. + +`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 ( +
+ + +
모달!
+ +
+
+ ); +} +``` + + diff --git a/src/stories/useModal/Modal.css.ts b/src/stories/useModal/Modal.css.ts new file mode 100644 index 0000000..6d1959c --- /dev/null +++ b/src/stories/useModal/Modal.css.ts @@ -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`, +}); diff --git a/src/stories/useModal/Modal.stories.ts b/src/stories/useModal/Modal.stories.ts new file mode 100644 index 0000000..08e91c3 --- /dev/null +++ b/src/stories/useModal/Modal.stories.ts @@ -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; + +export default meta; + +type Story = StoryObj; + +export const defaultStory: Story = { + args: {}, +}; diff --git a/src/stories/useModal/Modal.tsx b/src/stories/useModal/Modal.tsx new file mode 100644 index 0000000..06982e1 --- /dev/null +++ b/src/stories/useModal/Modal.tsx @@ -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 ( +
+ + +
모달!
+ +
+
+ ); +} From 6b3b9613f3a8e48cf277ec9fbaf9e18d08001b0b Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:18:39 +0900 Subject: [PATCH 05/10] =?UTF-8?q?docs:=20README=20useModal=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 ++++++++++++++++++++++ src/index.ts | 2 ++ 2 files changed, 24 insertions(+) diff --git a/README.md b/README.md index ed69923..b5a7cb7 100644 --- a/README.md +++ b/README.md @@ -328,3 +328,25 @@ const SomeComponent = () => { ); }; ``` + +### useModal + +A hook for easily managing an animated modal through a portal. + +#### Function Arguments + +`modalRoot`: The HTMLElement where the modal will be rendered. The default is `document.body`. + +`overlayAnimation`: The animation className applied to the overlay. It can accept two key-value pairs: `showClassName` and `hideClassName`. + +`modalAnimation`: The animation className applied to the modal. It can accept two key-value pairs: `showClassName` and `hideClassName`. + +#### Return Values + +`Modal`: A component that renders its children to the specified root through a portal. + +`show`: Opens the modal. + +`hide`: Closes the modal. + +`isShow`: Indicates whether the modal is open. diff --git a/src/index.ts b/src/index.ts index 621c9bd..7410274 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import useThrottle from './useThrottle/useThrottle'; import useDebounce from './useDebounce/useDebounce'; import useLocalStorage from './useLocalStorage/useLocalStorage'; import useDisclosure from './useDisclosure/useDisclosure'; +import useModal from './useModal/useModal'; export { useInput, @@ -26,4 +27,5 @@ export { useDebounce, useLocalStorage, useDisclosure, + useModal, }; From 1d1e7ed802284a5c7a53a0bf384d356286fc9fef Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:19:00 +0900 Subject: [PATCH 06/10] =?UTF-8?q?test:=20branch=20test=20coverage=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/useBoolean/_useBoolean.test.ts | 2 +- src/useDisclosure/_useDisclosure.test.ts | 2 +- src/useSwitch/_useSwitch.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/useBoolean/_useBoolean.test.ts b/src/useBoolean/_useBoolean.test.ts index 0bd7858..9b79935 100644 --- a/src/useBoolean/_useBoolean.test.ts +++ b/src/useBoolean/_useBoolean.test.ts @@ -3,7 +3,7 @@ import { renderHook, act } from '@testing-library/react'; describe('useBoolean 기능테스트', () => { it('useBoolean은 boolean상태를 나타내는 값과 그 boolean을 변경할 수 있는 값을 배열로 반환한다.', () => { - const { result } = renderHook(() => useBoolean(false)); + const { result } = renderHook(() => useBoolean()); expect(result.current[0]).toBe(false); act(() => { diff --git a/src/useDisclosure/_useDisclosure.test.ts b/src/useDisclosure/_useDisclosure.test.ts index 6e4f45a..cea0bf3 100644 --- a/src/useDisclosure/_useDisclosure.test.ts +++ b/src/useDisclosure/_useDisclosure.test.ts @@ -3,7 +3,7 @@ import { renderHook, act } from '@testing-library/react'; describe('useDisclosure 기능테스트', () => { it('useDisclosure는 modal, disclosure와 같이 컴포넌트의 열림과 닫힘 상태를 조절할 수 있는 기능들을 반환한다.', () => { - const { result } = renderHook(() => useDisclosure(false)); + const { result } = renderHook(() => useDisclosure()); expect(result.current.isOpen).toBe(false); act(() => { diff --git a/src/useSwitch/_useSwitch.test.ts b/src/useSwitch/_useSwitch.test.ts index 89f81ba..75b0b2c 100644 --- a/src/useSwitch/_useSwitch.test.ts +++ b/src/useSwitch/_useSwitch.test.ts @@ -3,7 +3,7 @@ import { renderHook, act } from '@testing-library/react'; describe('useSwitch 기능테스트', () => { it('useSwitch는 스위치(혹은 토글)의 켜짐 상태와 켜짐상태를 조절할 수 있는 함수들을 반환한다.', () => { - const { result } = renderHook(() => useSwitch(false)); + const { result } = renderHook(() => useSwitch()); expect(result.current.isOn).toBe(false); act(() => { From b40c55ab6fdb76a48daadb0e763a7b0c1c121293 Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:39:02 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20overlayClose=EC=98=B5=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/useModal/useModal.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/useModal/useModal.tsx b/src/useModal/useModal.tsx index 8e5df0f..4702217 100644 --- a/src/useModal/useModal.tsx +++ b/src/useModal/useModal.tsx @@ -16,6 +16,7 @@ export type UseModalAnimations = { interface UseModalProps extends UseModalAnimations { modalRoot?: ModalRoot; + overlayClose?: boolean; } interface ModalProps { @@ -30,6 +31,7 @@ const DefaultModal = ({ children, modalRoot }: { children: ReactNode; modalRoot? }; export default function useModal(modalProps?: UseModalProps) { + const overlayClose = modalProps?.overlayClose || true; const ModalAnimation = useAnimation({ mountClassName: modalProps?.modalAnimation?.showClassName, unmountClassName: modalProps?.modalAnimation?.hideClassName, @@ -51,7 +53,7 @@ export default function useModal(modalProps?: UseModalProps) { const Modal = ({ children, overlayClassName, modalClassName, style }: ModalProps) => ( - + e.stopPropagation()}> {children} From 53544998ffc017ba73517e56f0275e72f13a744f Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:39:24 +0900 Subject: [PATCH 08/10] =?UTF-8?q?test:=20overlayClose=20test=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/useModal/useModal.test.tsx | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/useModal/useModal.test.tsx b/src/useModal/useModal.test.tsx index f7addbe..89bbf62 100644 --- a/src/useModal/useModal.test.tsx +++ b/src/useModal/useModal.test.tsx @@ -28,6 +28,72 @@ describe('useModal 기능 테스트', () => { expect(await screen.findByRole('test')).toBeTruthy(); }); + it('overlayClose가 true인 경우, overlay를 눌러 modal을 닫을 수 있다.', async () => { + const Test = () => { + const { Modal, show, hide } = useModal(); + return ( +
+ + + +
+ +
+
+ ); + }; + render(); + fireEvent.click(await screen.findByRole('show')); + expect(await screen.findByRole('test')).toBeTruthy(); + fireEvent.click(await screen.findByRole('hide')); + await expect(screen.findByRole('test')).rejects.toThrow(); + + fireEvent.click(await screen.findByRole('show')); + expect(await screen.findByRole('test')).toBeTruthy(); + fireEvent.click(await screen.findByRole('modal-hide')); + await expect(screen.findByRole('test')).rejects.toThrow(); + + fireEvent.click(await screen.findByRole('show')); + const modalInner = await screen.findByRole('test'); + fireEvent.click(modalInner!.parentElement!.parentElement!); + await expect(screen.findByRole('test')).rejects.toThrow(); + }); + + it('overlayClose가 false인 경우, overlay를 누르면 modal이 닫히지 않는다.', async () => { + const Test = () => { + const { Modal, show, hide } = useModal({ overlayClose: false }); + return ( +
+ + + +
+ +
+
+ ); + }; + render(); + + fireEvent.click(await screen.findByRole('show')); + expect(await screen.findByRole('test')).toBeTruthy(); + const modalInner = await screen.findByRole('test'); + fireEvent.click(modalInner!.parentElement!.parentElement!); + await expect(screen.findByRole('test')).toBeTruthy(); + }); + it('지정한 element에 modal을 렌더링할 수 있다.', async () => { const testRoot = document.createElement('div'); testRoot.id = 'test-root'; From 730a0e86a2b5f8a304717b740f813d835aaae74b Mon Sep 17 00:00:00 2001 From: d0422 Date: Fri, 7 Jun 2024 19:39:45 +0900 Subject: [PATCH 09/10] =?UTF-8?q?docs:=20Storybook,=20README=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 +++++++++++++++++++ src/stories/useModal/Docs.mdx | 21 ++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b5a7cb7..a0eb988 100644 --- a/README.md +++ b/README.md @@ -335,8 +335,27 @@ A hook for easily managing an animated modal through a portal. #### Function Arguments +modalProps object is accepted. This object is structured as follows: + +```ts +interface UseModalProps { + modalRoot?: ModalRoot; + overlayClose?: boolean; + overlayAnimation?: { + showClassName?: string; + hideClassName?: string; + }; + modalAnimation?: { + showClassName?: string; + hideClassName?: string; + }; +} +``` + `modalRoot`: The HTMLElement where the modal will be rendered. The default is `document.body`. +`overlayClose`: Sets whether clicking on the overlay closes the modal. The default is `true`. + `overlayAnimation`: The animation className applied to the overlay. It can accept two key-value pairs: `showClassName` and `hideClassName`. `modalAnimation`: The animation className applied to the modal. It can accept two key-value pairs: `showClassName` and `hideClassName`. diff --git a/src/stories/useModal/Docs.mdx b/src/stories/useModal/Docs.mdx index daf3280..6bbf053 100644 --- a/src/stories/useModal/Docs.mdx +++ b/src/stories/useModal/Docs.mdx @@ -9,7 +9,26 @@ import * as Modal from './Modal.stories'; ## 함수 인자 -`modalRoot`: 모달을 렌더링할 HTMLElement입니다. document.body가 default입니다. +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를 받을 수 있습니다. From c976ac482b6ad4afc42bc9b20aa3fddd5781ae34 Mon Sep 17 00:00:00 2001 From: d0422 Date: Tue, 11 Jun 2024 12:05:39 +0900 Subject: [PATCH 10/10] =?UTF-8?q?chore:=20UseModalAnimation=20type=20inter?= =?UTF-8?q?face=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/useModal/useModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/useModal/useModal.tsx b/src/useModal/useModal.tsx index 4702217..c630762 100644 --- a/src/useModal/useModal.tsx +++ b/src/useModal/useModal.tsx @@ -3,7 +3,7 @@ import { createPortal } from 'react-dom'; import { useAnimation } from '..'; type ModalRoot = Element | DocumentFragment; -export type UseModalAnimations = { +export interface UseModalAnimations { overlayAnimation?: { showClassName?: string; hideClassName?: string; @@ -12,7 +12,7 @@ export type UseModalAnimations = { showClassName?: string; hideClassName?: string; }; -}; +} interface UseModalProps extends UseModalAnimations { modalRoot?: ModalRoot;