2024-08-21 05:58:43 +00:00
|
|
|
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';
|
|
|
|
|
|
|
|
interface DialogButtonProps {
|
|
|
|
type: 'primary' | 'secondary' | 'danger';
|
|
|
|
children: ReactNode;
|
2025-01-17 18:33:20 +00:00
|
|
|
onClick?: (event: React.MouseEvent) => void;
|
|
|
|
disabled?: boolean;
|
2024-08-21 05:58:43 +00:00
|
|
|
}
|
|
|
|
|
2025-01-17 18:33:20 +00:00
|
|
|
export const DialogButton = memo(({ type, children, onClick, disabled }: DialogButtonProps) => {
|
2024-08-21 05:58:43 +00:00
|
|
|
return (
|
|
|
|
<button
|
2025-01-28 10:39:12 +00:00
|
|
|
className={classNames('inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm transition-colors', {
|
|
|
|
'bg-purple-500 text-white hover:bg-purple-600 dark:bg-purple-500 dark:hover:bg-purple-600': type === 'primary',
|
|
|
|
'bg-transparent text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-gray-100':
|
|
|
|
type === 'secondary',
|
|
|
|
'bg-transparent text-red-500 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-500/10': type === 'danger',
|
2025-01-17 18:33:20 +00:00
|
|
|
})}
|
2024-08-21 05:58:43 +00:00
|
|
|
onClick={onClick}
|
2025-01-17 18:33:20 +00:00
|
|
|
disabled={disabled}
|
2024-08-21 05:58:43 +00:00
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</button>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
export const DialogTitle = memo(({ className, children, ...props }: RadixDialog.DialogTitleProps) => {
|
|
|
|
return (
|
|
|
|
<RadixDialog.Title
|
2025-01-17 18:33:20 +00:00
|
|
|
className={classNames('text-lg font-medium text-bolt-elements-textPrimary', 'flex items-center gap-2', className)}
|
2024-08-21 05:58:43 +00:00
|
|
|
{...props}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</RadixDialog.Title>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
export const DialogDescription = memo(({ className, children, ...props }: RadixDialog.DialogDescriptionProps) => {
|
|
|
|
return (
|
|
|
|
<RadixDialog.Description
|
2025-01-17 18:33:20 +00:00
|
|
|
className={classNames('text-sm text-bolt-elements-textSecondary', 'mt-1', className)}
|
2024-08-21 05:58:43 +00:00
|
|
|
{...props}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</RadixDialog.Description>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2025-01-17 18:33:20 +00:00
|
|
|
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;
|
|
|
|
|
2024-08-21 05:58:43 +00:00
|
|
|
interface DialogProps {
|
2025-01-17 18:33:20 +00:00
|
|
|
children: ReactNode;
|
2024-08-21 05:58:43 +00:00
|
|
|
className?: string;
|
2025-01-17 18:33:20 +00:00
|
|
|
showCloseButton?: boolean;
|
|
|
|
onClose?: () => void;
|
|
|
|
onBackdrop?: () => void;
|
2024-08-21 05:58:43 +00:00
|
|
|
}
|
|
|
|
|
2025-01-17 18:33:20 +00:00
|
|
|
export const Dialog = memo(({ children, className, showCloseButton = true, onClose, onBackdrop }: DialogProps) => {
|
2024-08-21 05:58:43 +00:00
|
|
|
return (
|
|
|
|
<RadixDialog.Portal>
|
2025-01-17 18:33:20 +00:00
|
|
|
<RadixDialog.Overlay asChild>
|
2024-08-21 05:58:43 +00:00
|
|
|
<motion.div
|
2025-01-17 18:33:20 +00:00
|
|
|
className={classNames(
|
|
|
|
'fixed inset-0 z-[9999]',
|
|
|
|
'bg-[#FAFAFA]/80 dark:bg-[#0A0A0A]/80',
|
|
|
|
'backdrop-blur-[2px]',
|
|
|
|
)}
|
2024-08-21 05:58:43 +00:00
|
|
|
initial="closed"
|
|
|
|
animate="open"
|
|
|
|
exit="closed"
|
|
|
|
variants={dialogBackdropVariants}
|
2025-01-17 18:33:20 +00:00
|
|
|
onClick={onBackdrop}
|
2024-08-21 05:58:43 +00:00
|
|
|
/>
|
|
|
|
</RadixDialog.Overlay>
|
|
|
|
<RadixDialog.Content asChild>
|
|
|
|
<motion.div
|
|
|
|
className={classNames(
|
2025-01-17 18:33:20 +00:00
|
|
|
'fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',
|
|
|
|
'bg-[#FAFAFA] dark:bg-[#0A0A0A]',
|
|
|
|
'rounded-lg shadow-lg',
|
|
|
|
'border border-[#E5E5E5] dark:border-[#1A1A1A]',
|
|
|
|
'z-[9999] w-[520px]',
|
2024-08-21 05:58:43 +00:00
|
|
|
className,
|
|
|
|
)}
|
|
|
|
initial="closed"
|
|
|
|
animate="open"
|
|
|
|
exit="closed"
|
|
|
|
variants={dialogVariants}
|
|
|
|
>
|
2025-01-17 18:33:20 +00:00
|
|
|
<div className="flex flex-col">
|
|
|
|
{children}
|
|
|
|
{showCloseButton && (
|
|
|
|
<RadixDialog.Close asChild onClick={onClose}>
|
|
|
|
<IconButton
|
|
|
|
icon="i-ph:x"
|
|
|
|
className="absolute top-3 right-3 text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary"
|
|
|
|
/>
|
|
|
|
</RadixDialog.Close>
|
|
|
|
)}
|
|
|
|
</div>
|
2024-08-21 05:58:43 +00:00
|
|
|
</motion.div>
|
|
|
|
</RadixDialog.Content>
|
|
|
|
</RadixDialog.Portal>
|
|
|
|
);
|
|
|
|
});
|