Files
portfolio/src/components/utility/OverlayPicture.tsx
Louis e91f55b80d Updates dependencies and implements portfolio
Updates eslint and typescript-eslint dependencies.

Migrates to a new portfolio structure with components, styling, route management, and internationalization.

Adds project display with multiple project types using custom components.

Adds image assets to be displayed in experience sections
2025-10-29 00:13:50 +01:00

76 lines
2.8 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
interface OverlayPictureProps {
src: string;
alt: string;
className?: string;
}
const OverlayPicture: React.FC<OverlayPictureProps> = ({ src, alt, className = '' }) => {
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen) {
// We don't want the escape key event to propagate to the overlay parent
// If the picture overlay is used inside another overlay, this prevents both overlays from closing
event.stopPropagation();
setIsOpen(false);
}
};
if (isOpen) {
// Use capture phase
document.addEventListener('keydown', handleEscape, true);
document.body.style.overflow = 'hidden';
}
return () => {
document.removeEventListener('keydown', handleEscape, true);
document.body.style.overflow = 'unset';
};
}, [isOpen]);
return (
<>
<motion.div className={`${className} flex items-center justify-center`}>
<motion.img
src={src}
alt={alt}
className={`cursor-zoom-in flex self-center max-w-xl max-h-96 rounded-lg shadow-md object-contain`}
onClick={() => setIsOpen(true)}
transition={{ duration: 0.2 }}
layoutId='overlay-image'
/>
</motion.div>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-75 backdrop-blur-sm"
onClick={(e) => {
if (e.target === e.currentTarget) {
setIsOpen(false);
}
}}
>
<motion.img
src={src}
alt={alt}
className="max-w-[90%] max-h-[80%] object-contain rounded-md"
// We don't want the click on the image to close the overlay
layoutId='overlay-image'
/>
</motion.div>
)}
</AnimatePresence>
</>
);
};
export default OverlayPicture;