|
import * as RadixDialog from '@radix-ui/react-dialog'; |
|
import { motion, type Variants } from 'framer-motion'; |
|
import React, { memo, type ReactNode } from 'react'; |
|
import { classNames } from '~/utils/classNames'; |
|
import { cubicEasingFn } from '~/utils/easings'; |
|
import { IconButton } from './IconButton'; |
|
|
|
export { Close as DialogClose, Root as DialogRoot } from '@radix-ui/react-dialog'; |
|
|
|
const transition = { |
|
duration: 0.15, |
|
ease: cubicEasingFn, |
|
}; |
|
|
|
export const dialogBackdropVariants = { |
|
closed: { |
|
opacity: 0, |
|
transition, |
|
}, |
|
open: { |
|
opacity: 1, |
|
transition, |
|
}, |
|
} satisfies Variants; |
|
|
|
export const dialogVariants = { |
|
closed: { |
|
x: '-50%', |
|
y: '-40%', |
|
scale: 0.96, |
|
opacity: 0, |
|
transition, |
|
}, |
|
open: { |
|
x: '-50%', |
|
y: '-50%', |
|
scale: 1, |
|
opacity: 1, |
|
transition, |
|
}, |
|
} satisfies Variants; |
|
|
|
interface DialogButtonProps { |
|
type: 'primary' | 'secondary' | 'danger'; |
|
children: ReactNode; |
|
onClick?: (event: React.UIEvent) => void; |
|
} |
|
|
|
export const DialogButton = memo(({ type, children, onClick }: DialogButtonProps) => { |
|
return ( |
|
<button |
|
className={classNames( |
|
'inline-flex h-[35px] items-center justify-center rounded-lg px-4 text-sm leading-none focus:outline-none', |
|
{ |
|
'bg-bolt-elements-button-primary-background text-bolt-elements-button-primary-text hover:bg-bolt-elements-button-primary-backgroundHover': |
|
type === 'primary', |
|
'bg-bolt-elements-button-secondary-background text-bolt-elements-button-secondary-text hover:bg-bolt-elements-button-secondary-backgroundHover': |
|
type === 'secondary', |
|
'bg-bolt-elements-button-danger-background text-bolt-elements-button-danger-text hover:bg-bolt-elements-button-danger-backgroundHover': |
|
type === 'danger', |
|
}, |
|
)} |
|
onClick={onClick} |
|
> |
|
{children} |
|
</button> |
|
); |
|
}); |
|
|
|
export const DialogTitle = memo(({ className, children, ...props }: RadixDialog.DialogTitleProps) => { |
|
return ( |
|
<RadixDialog.Title |
|
className={classNames( |
|
'px-5 py-4 flex items-center justify-between border-b border-bolt-elements-borderColor text-lg font-semibold leading-6 text-bolt-elements-textPrimary', |
|
className, |
|
)} |
|
{...props} |
|
> |
|
{children} |
|
</RadixDialog.Title> |
|
); |
|
}); |
|
|
|
export const DialogDescription = memo(({ className, children, ...props }: RadixDialog.DialogDescriptionProps) => { |
|
return ( |
|
<RadixDialog.Description |
|
className={classNames('px-5 py-4 text-bolt-elements-textPrimary text-md', className)} |
|
{...props} |
|
> |
|
{children} |
|
</RadixDialog.Description> |
|
); |
|
}); |
|
|
|
interface DialogProps { |
|
children: ReactNode | ReactNode[]; |
|
className?: string; |
|
onBackdrop?: (event: React.UIEvent) => void; |
|
onClose?: (event: React.UIEvent) => void; |
|
} |
|
|
|
export const Dialog = memo(({ className, children, onBackdrop, onClose }: DialogProps) => { |
|
return ( |
|
<RadixDialog.Portal> |
|
<RadixDialog.Overlay onClick={onBackdrop} asChild> |
|
<motion.div |
|
className="bg-black/50 fixed inset-0 z-max" |
|
initial="closed" |
|
animate="open" |
|
exit="closed" |
|
variants={dialogBackdropVariants} |
|
/> |
|
</RadixDialog.Overlay> |
|
<RadixDialog.Content asChild> |
|
<motion.div |
|
className={classNames( |
|
'fixed top-[50%] left-[50%] z-max max-h-[85vh] w-[90vw] max-w-[450px] translate-x-[-50%] translate-y-[-50%] border border-bolt-elements-borderColor rounded-lg bg-bolt-elements-background-depth-2 shadow-lg focus:outline-none overflow-hidden', |
|
className, |
|
)} |
|
initial="closed" |
|
animate="open" |
|
exit="closed" |
|
variants={dialogVariants} |
|
> |
|
{children} |
|
<RadixDialog.Close asChild onClick={onClose}> |
|
<IconButton icon="i-ph:x" className="absolute top-[10px] right-[10px]" /> |
|
</RadixDialog.Close> |
|
</motion.div> |
|
</RadixDialog.Content> |
|
</RadixDialog.Portal> |
|
); |
|
}); |
|
|