FlowWeb / src /components /shared /Modal.jsx
danylokhodus's picture
init
de55c35
Raw
History Blame Contribute Delete
4.09 kB
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { X, Check, Info } from 'lucide-react';
const Modal = ({
isOpen,
onClose,
onConfirm,
title,
message,
type = 'confirm',
initialValue = '',
placeholder = 'Enter something...',
confirmText = 'OK',
cancelText = 'Cancel'
}) => {
const [inputValue, setInputValue] = useState(initialValue);
useEffect(() => {
if (isOpen) {
setInputValue(initialValue);
}
}, [isOpen, initialValue]);
const handleSubmit = (e) => {
e.preventDefault();
if (type === 'prompt') {
onConfirm(inputValue);
} else {
onConfirm();
}
onClose();
};
return (
<AnimatePresence>
{isOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6">
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="absolute inset-0 bg-primary/20 backdrop-blur-sm"
/>
{/* Modal Content */}
<motion.div
initial={{ scale: 0.9, opacity: 0, y: 20, rotate: -1 }}
animate={{ scale: 1, opacity: 1, y: 0, rotate: 0 }}
exit={{ scale: 0.9, opacity: 0, y: 20, rotate: 1 }}
className="relative bg-surface-container-lowest w-full max-w-md p-8 rough-border rough-shadow flex flex-col gap-6"
>
<button
onClick={onClose}
className="absolute top-4 right-4 text-on-surface-variant hover:text-primary transition-colors"
>
<X size={24} />
</button>
<div className="flex flex-col gap-2">
<h2 className="text-4xl font-display-lg text-primary leading-tight">
{title}
</h2>
{message && (
<p className="font-body-md text-on-surface-variant italic">
{message}
</p>
)}
</div>
<form onSubmit={handleSubmit} className="flex flex-col gap-8">
{type === 'prompt' && (
<div className="relative group">
<input
autoFocus
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder={placeholder}
className="w-full bg-surface-container-low border-b-2 border-primary/20 focus:border-primary px-2 py-3 text-xl font-body-md outline-none transition-all"
/>
<div className="absolute bottom-0 left-0 h-0.5 bg-primary w-0 group-focus-within:w-full transition-all duration-300" />
</div>
)}
<div className="flex items-center justify-end gap-4 mt-2">
<button
type="button"
onClick={onClose}
className="px-6 py-2 font-bold text-lg text-on-surface-variant hover:text-primary transition-all"
>
{cancelText}
</button>
<motion.button
whileHover={{ scale: 1.05, rotate: -1 }}
whileTap={{ scale: 0.95 }}
type="submit"
className="bg-primary text-on-primary px-8 py-3 text-xl font-bold rough-border rough-shadow-hover flex items-center gap-2"
>
<Check size={20} />
<span>{confirmText}</span>
</motion.button>
</div>
</form>
{/* Decorative Sketched Element */}
<div className="absolute -bottom-6 -left-6 pointer-events-none opacity-[0.03] overflow-hidden">
<Info size={160} className="-rotate-12 translate-x-4 translate-y-4" />
</div>
</motion.div>
</div>
)}
</AnimatePresence>
);
};
export default Modal;