Created
March 7, 2023 01:27
-
-
Save k1-c/56c49be9bf2f7ed9b01fbe7c4067cba6 to your computer and use it in GitHub Desktop.
[react] [hooks] use modal hook sample
This file contains hidden or 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
| import React, { useState } from 'react'; | |
| import { createPortal } from 'react-dom'; | |
| import { ModalCloseButton } from '@/components/buttons/ModalCloseButton/ModalCloseButton'; | |
| import { useClient } from '@/hooks/common/useClient'; | |
| const ModalPortal: React.FC = ({ children }) => { | |
| const isClient = useClient(); | |
| return isClient ? createPortal(children, document.getElementsByTagName('body')[0]!) : <>{children}</>; | |
| }; | |
| type BaseModalProps = { | |
| isModalShown: boolean; | |
| closeModal: () => void; | |
| children: React.ReactNode; | |
| }; | |
| const BaseModal: React.FC<BaseModalProps> = (props) => { | |
| const closeModal = () => { | |
| const modal = document.querySelector('#modal'); | |
| modal?.classList.remove('animate-fadeIn'); | |
| modal?.classList.add('animate-fadeOut'); | |
| // NOTE: アニメーションが終わるまでWaitする | |
| setTimeout(() => { | |
| props.closeModal(); | |
| }, 200); | |
| }; | |
| if (!props.isModalShown) return null; | |
| return ( | |
| <div | |
| className='fixed z-20 top-0 left-0 w-full h-full p-2 sm:p-4 flex items-center justify-center bg-black/20' | |
| onClick={closeModal} | |
| > | |
| <div id='modal' className='animate-fadeIn relative w-full h-full flex justify-center sm:p-4 max-w-[800px]'> | |
| <div className='absolute top-0 right-0 z-50' onClick={closeModal}> | |
| <ModalCloseButton /> | |
| </div> | |
| <div | |
| id='modal' | |
| onClick={(e: React.MouseEvent) => e.stopPropagation()} // NOTE: For closing only when the outside of modal is click, but not when the inside. | |
| className='relative bg-white p-4 w-full sm:p-8 sm:m-4 sm:w-11/12 rounded-xl overflow-y-auto' | |
| > | |
| {props.children} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| type BaseNarrowModalProps = { | |
| isModalShown: boolean; | |
| closeModal: () => void; | |
| children: React.ReactNode; | |
| }; | |
| const BaseNarrowModal: React.FC<BaseNarrowModalProps> = (props) => { | |
| const closeModal = () => { | |
| const modal = document.querySelector('#modal'); | |
| modal?.classList.remove('animate-fadeIn'); | |
| modal?.classList.add('animate-fadeOut'); | |
| // NOTE: アニメーションが終わるまでWaitする | |
| setTimeout(() => { | |
| props.closeModal(); | |
| }, 200); | |
| }; | |
| if (!props.isModalShown) return null; | |
| return ( | |
| <div | |
| className='fixed z-20 top-0 left-0 w-full h-full p-2 sm:p-4 flex items-center justify-center bg-black/20' | |
| onClick={closeModal} | |
| > | |
| <div id='modal' className='animate-fadeIn relative w-full flex justify-center sm:p-4 max-w-[800px]'> | |
| <div className='absolute top-0 right-0 z-50' onClick={closeModal}> | |
| <ModalCloseButton /> | |
| </div> | |
| <div | |
| onClick={(e: React.MouseEvent) => e.stopPropagation()} // NOTE: For closing only when the outside of modal is click, but not when the inside. | |
| className='relative bg-white p-4 w-full sm:p-8 sm:m-4 sm:w-11/12 rounded-xl overflow-y-auto' | |
| > | |
| {props.children} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export const useModal = () => { | |
| const [isModalShown, setIsModalShown] = useState(false); | |
| const openModal = () => { | |
| const scrollBarWidth = window.innerWidth - document.body.clientWidth; | |
| document.body.style.overflow = 'hidden'; | |
| document.body.style.paddingRight = `${scrollBarWidth}px`; | |
| setIsModalShown(true); | |
| }; | |
| const closeModal = () => { | |
| document.body.style.overflow = 'auto'; | |
| document.body.style.paddingRight = '0'; | |
| setIsModalShown(false); | |
| }; | |
| const Modal: React.FC = ({ children }) => ( | |
| <ModalPortal> | |
| <BaseModal isModalShown={isModalShown} closeModal={closeModal}> | |
| {children} | |
| </BaseModal> | |
| </ModalPortal> | |
| ); | |
| const NarrowModal: React.FC = ({ children }) => ( | |
| <ModalPortal> | |
| <BaseNarrowModal isModalShown={isModalShown} closeModal={closeModal}> | |
| {children} | |
| </BaseNarrowModal> | |
| </ModalPortal> | |
| ); | |
| return { | |
| openModal, | |
| Modal, | |
| NarrowModal | |
| }; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment