Skip to content

Instantly share code, notes, and snippets.

@k1-c
Created March 7, 2023 01:27
Show Gist options
  • Select an option

  • Save k1-c/56c49be9bf2f7ed9b01fbe7c4067cba6 to your computer and use it in GitHub Desktop.

Select an option

Save k1-c/56c49be9bf2f7ed9b01fbe7c4067cba6 to your computer and use it in GitHub Desktop.
[react] [hooks] use modal hook sample
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