Improve dialog logic, add "asDialog" helper
This commit is contained in:
parent
822949408f
commit
a4feed24a8
10 changed files with 131 additions and 77 deletions
|
@ -1,38 +1,37 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Dialog as HDialog } from '@headlessui/react';
|
||||
import { Button } from '@/components/elements/button/index';
|
||||
import { XIcon } from '@heroicons/react/solid';
|
||||
import DialogIcon, { IconPosition } from '@/components/elements/dialog/DialogIcon';
|
||||
import { AnimatePresence, motion, useAnimation } from 'framer-motion';
|
||||
import ConfirmationDialog from '@/components/elements/dialog/ConfirmationDialog';
|
||||
import DialogContext from './context';
|
||||
import DialogFooter from '@/components/elements/dialog/DialogFooter';
|
||||
import styles from './style.module.css';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { DialogContext, IconPosition, RenderDialogProps, styles } from './';
|
||||
|
||||
export interface DialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export interface FullDialogProps extends DialogProps {
|
||||
hideCloseIcon?: boolean;
|
||||
preventExternalClose?: boolean;
|
||||
title?: string;
|
||||
description?: string | undefined;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const spring = { type: 'spring', damping: 15, stiffness: 300, duration: 0.15 };
|
||||
const variants = {
|
||||
open: { opacity: 1, scale: 1, transition: spring },
|
||||
closed: { opacity: 0, scale: 0.85, transition: spring },
|
||||
open: {
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
type: 'spring',
|
||||
damping: 15,
|
||||
stiffness: 300,
|
||||
duration: 0.15,
|
||||
},
|
||||
},
|
||||
closed: {
|
||||
scale: 0.75,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
type: 'easeIn',
|
||||
duration: 0.15,
|
||||
},
|
||||
},
|
||||
bounce: {
|
||||
scale: 0.95,
|
||||
opacity: 1,
|
||||
transition: { type: 'linear', duration: 0.075 },
|
||||
},
|
||||
};
|
||||
|
||||
const Dialog = ({
|
||||
export default ({
|
||||
open,
|
||||
title,
|
||||
description,
|
||||
|
@ -40,28 +39,25 @@ const Dialog = ({
|
|||
hideCloseIcon,
|
||||
preventExternalClose,
|
||||
children,
|
||||
}: FullDialogProps) => {
|
||||
const controls = useAnimation();
|
||||
|
||||
}: RenderDialogProps) => {
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const [icon, setIcon] = useState<React.ReactNode>();
|
||||
const [footer, setFooter] = useState<React.ReactNode>();
|
||||
const [iconPosition, setIconPosition] = useState<IconPosition>('title');
|
||||
const [down, setDown] = useState(false);
|
||||
|
||||
const onContainerClick = (down: boolean, e: React.MouseEvent<HTMLDivElement>): void => {
|
||||
if (e.target instanceof HTMLElement && container.current?.isSameNode(e.target)) {
|
||||
setDown(down);
|
||||
}
|
||||
};
|
||||
|
||||
const onDialogClose = (): void => {
|
||||
if (!preventExternalClose) {
|
||||
return onClose();
|
||||
}
|
||||
|
||||
controls
|
||||
.start('bounce')
|
||||
.then(() => controls.start('open'))
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
controls.start(open ? 'open' : 'closed').catch(console.error);
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{open && (
|
||||
|
@ -78,10 +74,17 @@ const Dialog = ({
|
|||
>
|
||||
<div className={'fixed inset-0 bg-gray-900/50 z-40'} />
|
||||
<div className={'fixed inset-0 overflow-y-auto z-50'}>
|
||||
<div className={styles.container}>
|
||||
<div
|
||||
ref={container}
|
||||
className={styles.container}
|
||||
onMouseDown={onContainerClick.bind(this, true)}
|
||||
onMouseUp={onContainerClick.bind(this, false)}
|
||||
>
|
||||
<HDialog.Panel
|
||||
as={motion.div}
|
||||
animate={controls}
|
||||
initial={'closed'}
|
||||
animate={down ? 'bounce' : 'open'}
|
||||
exit={'closed'}
|
||||
variants={variants}
|
||||
className={styles.panel}
|
||||
>
|
||||
|
@ -125,11 +128,3 @@ const Dialog = ({
|
|||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
|
||||
const _Dialog = Object.assign(Dialog, {
|
||||
Confirm: ConfirmationDialog,
|
||||
Footer: DialogFooter,
|
||||
Icon: DialogIcon,
|
||||
});
|
||||
|
||||
export default _Dialog;
|
||||
|
|
Reference in a new issue