Dialog Box
The Dialog Box component provides a flexible way to display modal dialogs with various animations and types. It's built on top of Framer Motion for smooth animations and supports different dialog types like alerts, confirmations, and custom content.
- Preview
- Code
<Button
onClick={() => openDialog({
title: 'Alert',
content: 'This is an alert dialog.',
dialogType: 'alert',
animationKey: 'popIn',
})}
>
Show Alert Dialog
</Button>
Installation
- CLI
- Manual
ignix add component dialogbox
import { motion, AnimatePresence } from 'framer-motion';
import { createContext, CSSProperties, useState, useContext } from 'react';
import { X, Check, AlertTriangle, Info, XCircle } from 'lucide-react';
export type DialogAnimationTypes =
| 'popIn'
| 'springPop'
| 'backdropZoom'
| 'flip3D'
| 'skewSlide'
| 'glassBlur'
| 'skyDrop'
| 'morphSlide'
| 'quantumFold'
| 'prismBreak'
| 'liquidFloat'
| 'dimensionRip';
export type DialogTypes = 'success' | 'confirm' | 'error' | 'alert' | 'info' | 'warning';
export const animations = {
popIn: {
initial: { scale: 0.95, opacity: 0, y: 20 },
animate: {
scale: 1,
opacity: 1,
y: 0,
transition: { type: 'spring', stiffness: 300, damping: 25, duration: 0.3 }
},
exit: { scale: 0.95, opacity: 0, y: 20, transition: { duration: 0.2 } }
},
springPop: {
initial: { y: 100, opacity: 0, scale: 0.2, rotateX: 20, filter: 'blur(8px)' },
animate: {
y: 0,
opacity: 1,
scale: 1,
rotateX: 0,
filter: 'blur(0px)',
transition: { type: 'spring', stiffness: 200, damping: 15, mass: 0.5, velocity: 2 }
},
exit: { y: 100, scale: 0.2, opacity: 0, filter: 'blur(4px)', transition: { duration: 0.4, ease: 'easeIn' } }
},
backdropZoom: {
initial: { scale: 1.3, opacity: 0, filter: 'blur(10px)' },
animate: {
scale: 1,
opacity: 1,
filter: 'blur(0px)',
transition: { duration: 0.8, type: 'spring', stiffness: 200, ease: 'easeInOut' }
},
exit: { scale: 0.95, opacity: 0, filter: 'blur(4px)', transition: { duration: 0.3, ease: 'easeIn' } }
},
flip3D: {
initial: { rotateY: -180, opacity: 0, scale: 0.8, filter: 'brightness(0.5)' },
animate: {
rotateY: 0,
opacity: 1,
scale: 1,
filter: 'brightness(1)',
transition: { duration: 0.8, ease: [0.68, -0.55, 0.265, 1.55], times: [0, 0.6, 1] }
},
exit: { rotateY: 180, opacity: 0, scale: 0.8, filter: 'brightness(0.5)', transition: { duration: 0.5, ease: 'easeIn' } }
},
skewSlide: {
initial: { skewY: -30, skewX: -30, scale: 0.2, y: -400, x: -300, opacity: 0, rotateZ: -15, filter: 'blur(8px) brightness(0.3)' },
animate: {
skewY: [30, -5, 0],
skewX: [30, -5, 0],
scale: [0.2, 1.1, 1],
y: [-400, 20, 0],
x: [-300, 15, 0],
opacity: [0, 0.7, 1],
rotateZ: [-15, 2, 0],
filter: 'blur(0px) brightness(1)',
transition: { duration: 1.2, ease: [0.68, -0.55, 0.265, 1.55], times: [0, 0.7, 1] }
},
exit: { skewY: 30, skewX: 30, scale: 0.2, y: -300, x: 400, opacity: 0, rotateZ: 15, filter: 'blur(6px)', transition: { duration: 0.6, ease: 'easeIn' } }
},
glassBlur: {
initial: { opacity: 0, scale: 0.95, filter: 'blur(20px) saturate(1.5)', backdropFilter: 'blur(0px)' },
animate: {
opacity: 1,
scale: 1,
filter: 'blur(0px) saturate(1)',
backdropFilter: 'blur(20px)',
transition: { duration: 0.8, ease: 'easeOut', filter: { duration: 1 } }
},
exit: { opacity: 0, scale: 0.95, filter: 'blur(10px)', transition: { duration: 0.4, ease: 'easeIn' } }
},
skyDrop: {
initial: { y: -500, opacity: 0, scale: 0.7, rotateX: -45, filter: 'blur(10px)' },
animate: {
y: [0, 15, 0],
opacity: [0, 0.8, 1],
scale: [0.7, 1.05, 1],
rotateX: [-45, 5, 0],
filter: 'blur(0px)',
transition: { duration: 1, ease: [0.34, 1.56, 0.64, 1], times: [0, 0.6, 1] }
},
exit: { y: -400, opacity: 0, scale: 0.8, rotateX: -30, filter: 'blur(6px)', transition: { duration: 0.5, ease: 'easeIn' } }
},
morphSlide: {
initial: { scale: 0.3, opacity: 0, x: -200, rotateZ: -45, borderRadius: '50%', filter: 'blur(15px) hue-rotate(180deg)' },
animate: {
scale: [0.3, 1.2, 1],
opacity: [0, 0.6, 1],
x: [-200, 30, 0],
rotateZ: [-45, 10, 0],
borderRadius: ['50%', '20%', '12px'],
filter: 'blur(0px) hue-rotate(0deg)',
transition: { duration: 1.5, ease: [0.68, -0.55, 0.265, 1.55], times: [0, 0.6, 1] }
},
exit: { scale: 0.4, opacity: 0, x: 200, rotateZ: 45, borderRadius: '50%', filter: 'blur(10px)', transition: { duration: 0.8, ease: 'easeIn' } }
},
quantumFold: {
initial: { scaleX: 0, scaleY: 1.5, opacity: 0, rotateY: 90, rotateX: 45, filter: 'brightness(2) contrast(0.5) blur(10px)' },
animate: {
scaleX: [0, 1.3, 1],
scaleY: [1.5, 0.8, 1],
opacity: [0, 0.4, 1],
rotateY: [90, -15, 0],
rotateX: [45, -10, 0],
filter: 'brightness(1) contrast(1) blur(0px)',
transition: { duration: 1.3, ease: [0.68, -0.55, 0.265, 1.55], times: [0, 0.5, 1] }
},
exit: { scaleX: 0, scaleY: 1.5, opacity: 0, rotateY: -90, filter: 'blur(8px)', transition: { duration: 0.6, ease: 'easeIn' } }
},
prismBreak: {
initial: { scale: 0.1, opacity: 0, rotateX: 180, rotateY: 180, rotateZ: 180, filter: 'blur(20px) saturate(3) hue-rotate(90deg)' },
animate: {
scale: [0.1, 1.4, 1],
opacity: [0, 0.3, 1],
rotateX: [180, -20, 0],
rotateY: [180, 20, 0],
rotateZ: [180, -10, 0],
filter: 'blur(0px) saturate(1) hue-rotate(0deg)',
transition: { duration: 1.8, ease: [0.68, -0.55, 0.265, 1.55], times: [0, 0.4, 1] }
},
exit: { scale: 0.2, opacity: 0, rotateX: -90, rotateY: 90, rotateZ: 45, filter: 'blur(15px)', transition: { duration: 0.8, ease: 'easeIn' } }
},
liquidFloat: {
initial: { scale: 0.6, opacity: 0, y: 100, borderRadius: '50%', filter: 'blur(12px)' },
animate: {
scale: [0.6, 1.1, 0.95, 1.02, 1],
opacity: [0, 0.7, 0.9, 1, 1],
y: [100, -20, 10, -5, 0],
borderRadius: ['50%', '30%', '20%', '15%', '12px'],
filter: 'blur(0px)',
transition: { duration: 2, ease: 'easeOut', times: [0, 0.3, 0.6, 0.8, 1] }
},
exit: { scale: 0.7, opacity: 0, y: -50, borderRadius: '50%', filter: 'blur(8px)', transition: { duration: 0.6, ease: 'easeIn' } }
},
dimensionRip: {
initial: { scaleX: 0.1, scaleY: 2, opacity: 0, rotateZ: 45, skewX: 30, filter: 'blur(20px) brightness(3) contrast(2)' },
animate: {
scaleX: [0.1, 2, 1],
scaleY: [2, 0.5, 1],
opacity: [0, 0.5, 1],
rotateZ: [45, -10, 0],
skewX: [30, -5, 0],
filter: 'blur(0px) brightness(1) contrast(1)',
transition: { duration: 1.4, ease: [0.68, -0.55, 0.265, 1.55], times: [0, 0.4, 1] }
},
exit: { scaleX: 0.1, scaleY: 2, opacity: 0, rotateZ: -45, skewX: -30, filter: 'blur(15px)', transition: { duration: 0.7, ease: 'easeIn' } }
}
};
export interface DialogProps {
animationKey?: DialogAnimationTypes;
confirmationCallBack?: (confirm: boolean) => void;
dialogType?: DialogTypes;
title?: string;
confirmButtonText?: string;
cancelButtonText?: string;
headerStyles?: CSSProperties;
children?: React.ReactNode;
defaultButtons?: boolean;
content?: string;
showCloseButton?: boolean;
size?: 'sm' | 'md' | 'lg' | 'xl';
}
const typeConfig = {
success: { icon: Check, gradient: 'from-emerald-500 to-emerald-700', iconBg: 'bg-emerald-500/20', shadowColor: 'shadow-emerald-500/25', borderColor: 'border-emerald-500/30' },
confirm: { icon: Info, gradient: 'from-blue-500 to-blue-700', iconBg: 'bg-blue-500/20', shadowColor: 'shadow-blue-500/25', borderColor: 'border-blue-500/30' },
error: { icon: XCircle, gradient: 'from-rose-500 to-rose-700', iconBg: 'bg-rose-500/20', shadowColor: 'shadow-rose-500/25', borderColor: 'border-rose-500/30' },
alert: { icon: AlertTriangle, gradient: 'from-amber-500 to-amber-700', iconBg: 'bg-amber-500/20', shadowColor: 'shadow-amber-500/25', borderColor: 'border-amber-500/30' },
info: { icon: Info, gradient: 'from-sky-500 to-sky-700', iconBg: 'bg-sky-500/20', shadowColor: 'shadow-sky-500/25', borderColor: 'border-sky-500/30' },
warning: { icon: AlertTriangle, gradient: 'from-orange-500 to-orange-700', iconBg: 'bg-orange-500/20', shadowColor: 'shadow-orange-500/25', borderColor: 'border-orange-500/30' },
};
const sizeConfig = { sm: 'max-w-sm', md: 'max-w-md', lg: 'max-w-lg', xl: 'max-w-xl' };
export const DialogContext = createContext<{
openDialog: (opts: DialogProps) => void;
closeDialog: () => void;
} | undefined>(undefined);
export const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const [options, setOptions] = useState<DialogProps>({});
const openDialog = (opts: DialogProps) => {
setOptions({
animationKey: 'popIn',
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
showCloseButton: true,
size: 'md',
...opts
});
setIsOpen(true);
};
const closeDialog = () => setIsOpen(false);
const handleClose = (confirm = false) => {
setIsOpen(false);
if (options.dialogType === 'confirm' && options.confirmationCallBack) {
options.confirmationCallBack(confirm);
}
};
const anim = animations[options.animationKey || 'popIn'];
const config = typeConfig[options.dialogType || 'alert'];
const IconComponent = config.icon;
return (
<DialogContext.Provider value={{ openDialog, closeDialog }}>
{children}
<AnimatePresence>
{isOpen && (
<motion.div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<motion.div
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => handleClose(false)}
/>
<motion.div
className={`relative bg-background rounded-2xl shadow-2xl ${sizeConfig[options.size || 'md']} w-full flex flex-col border border-border/40 overflow-hidden ${config.shadowColor}`}
{...anim}
>
<div className={`relative flex items-center justify-between p-6 bg-gradient-to-br ${config.gradient} text-white`}>
<div className="flex items-center">
<div className={`p-2 rounded-lg ${config.iconBg} mr-4`}>
<IconComponent className="h-6 w-6 text-white" />
</div>
<h2 className="text-xl font-bold">{options.title}</h2>
</div>
{options.showCloseButton && (
<button onClick={() => handleClose(false)} className="p-2 hover:bg-white/20 rounded-lg transition-colors">
<X className="h-5 w-5" />
</button>
)}
</div>
<div className="p-6 overflow-y-auto">
{options.children ?? <p className="text-muted-foreground">{options.content}</p>}
</div>
{!options.defaultButtons && (
<div className="flex justify-end gap-3 p-6 pt-0">
<button onClick={() => handleClose(false)} className="px-4 py-2 rounded-lg border border-border hover:bg-muted transition-colors">
{options.cancelButtonText}
</button>
{options.dialogType === 'confirm' && (
<button onClick={() => handleClose(true)} className="px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:opacity-90 transition-opacity">
{options.confirmButtonText}
</button>
)}
</div>
)}
</motion.div>
</motion.div>
)}
</AnimatePresence>
</DialogContext.Provider>
);
};
export const useDialog = () => {
const context = useContext(DialogContext);
if (!context) throw new Error('useDialog must be used within a DialogProvider');
return context;
};
Basic Usage
1. Wrap your app with DialogProvider
import { DialogProvider } from '@ignix-ui/dialogbox';
function App() {
return (
<DialogProvider>
{/* Your app components */}
</DialogProvider>
);
}
2. Use the useDialog hook in your components
import { useDialog } from '@ignix-ui/dialogbox';
function MyComponent() {
const { openDialog } = useDialog();
const handleClick = () => {
openDialog({
title: 'Hello',
content: 'This is a dialog!',
dialogType: 'alert'
});
};
return <button onClick={handleClick}>Open Dialog</button>;
}
Props
DialogProps
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | - | Title of the dialog box |
content | string | - | Text content to display (if children is not provided) |
children | React.ReactNode | - | Custom content to display inside the dialog |
dialogType | 'success' | 'confirm' | 'error' | 'alert' | 'info' | 'warning' | 'alert' | Visual style variant of the dialog |
animationKey | DialogAnimationTypes | 'popIn' | Type of entrance/exit animation |
size | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Maximum width of the dialog |
showCloseButton | boolean | true | Whether to show the close button in the header |
confirmButtonText | string | 'Confirm' | Label for the primary action button |
cancelButtonText | string | 'Cancel' | Label for the secondary action button |
defaultButtons | boolean | false | If true, hides the default action buttons |
confirmationCallBack | (confirm: boolean) => void | - | Callback fired when a confirm dialog is resolved |
headerStyles | CSSProperties | - | Custom styles for the dialog header |