83 lines
2.4 KiB
TypeScript
83 lines
2.4 KiB
TypeScript
/* eslint-disable react-hooks/exhaustive-deps */
|
|
import clsx from 'clsx';
|
|
import { useBlocker } from 'react-router';
|
|
import {
|
|
JSX,
|
|
Fragment,
|
|
ReactNode,
|
|
useCallback
|
|
} from 'react';
|
|
import {
|
|
Dialog,
|
|
Transition,
|
|
DialogPanel,
|
|
DialogTitle,
|
|
TransitionChild
|
|
} from '@headlessui/react';
|
|
|
|
export interface BottomSheetProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
afterLeave?: () => void;
|
|
children: ReactNode;
|
|
title?: string | JSX.Element;
|
|
description?: string | JSX.Element;
|
|
centeredTitle?: boolean;
|
|
}
|
|
|
|
const BottomSheet = ({ open, onClose, afterLeave, children, title, centeredTitle, description }: BottomSheetProps) => {
|
|
const whenToBlock = useCallback(() => open, [open]);
|
|
const blocker = useBlocker(whenToBlock);
|
|
const handleClose = useCallback(() => {
|
|
blocker?.reset?.();
|
|
onClose();
|
|
}, []);
|
|
return (
|
|
<Transition show={open} as={Fragment} afterLeave={afterLeave}>
|
|
<Dialog onClose={handleClose}>
|
|
<TransitionChild
|
|
as={Fragment}
|
|
enter="ease-out duration-300"
|
|
enterFrom="opacity-0"
|
|
enterTo="opacity-100"
|
|
leave="ease-in duration-200"
|
|
leaveFrom="opacity-100"
|
|
leaveTo="opacity-0"
|
|
>
|
|
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
|
|
</TransitionChild>
|
|
<TransitionChild
|
|
as={Fragment}
|
|
enter="transition-transform duration-400"
|
|
enterFrom="translate-y-full"
|
|
enterTo="translate-y-0"
|
|
leave="transition-transform duration-200"
|
|
leaveFrom=" translate-y-0"
|
|
leaveTo="translate-y-full"
|
|
>
|
|
<DialogPanel className="fixed bottom-0 w-full rounded-t-[30px] bg-white">
|
|
<DialogTitle className="relative flex px-[30px] pt-[30px]">
|
|
<div>
|
|
<h2
|
|
className={clsx('grow text-lg font-bold', {
|
|
'text-center': centeredTitle,
|
|
})}
|
|
>
|
|
{title}
|
|
</h2>
|
|
{description && <p className="mt-[8px] text-[14px] text-[#888888]">{description}</p>}
|
|
</div>
|
|
<button onClick={handleClose} className="btn close-btn absolute right-[30px] top-[27px]">
|
|
<span>닫기</span>
|
|
</button>
|
|
</DialogTitle>
|
|
{ children }
|
|
</DialogPanel>
|
|
</TransitionChild>
|
|
</Dialog>
|
|
</Transition>
|
|
);
|
|
};
|
|
|
|
export { BottomSheet };
|