React 19 + TypeScript SPA with Vite, mobile responsive fixes, GitHub Actions CI/CD pipeline for automated deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import React from 'react';
|
||
import ReactDOM from 'react-dom';
|
||
import { motion, AnimatePresence } from 'framer-motion';
|
||
import './Modal.css';
|
||
|
||
interface ModalProps {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
children: React.ReactNode;
|
||
}
|
||
|
||
const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children }) => {
|
||
return ReactDOM.createPortal(
|
||
<AnimatePresence>
|
||
{isOpen && (
|
||
<motion.div
|
||
className="modal-overlay"
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
onClick={onClose}
|
||
>
|
||
<motion.div
|
||
className="modal-content"
|
||
initial={{ scale: 0.8, opacity: 0 }}
|
||
animate={{ scale: 1, opacity: 1 }}
|
||
exit={{ scale: 0.8, opacity: 0 }}
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
<button className="close-modal" onClick={onClose}>×</button>
|
||
{children}
|
||
</motion.div>
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>,
|
||
document.body
|
||
);
|
||
};
|
||
|
||
export default Modal;
|