From 3d21a987a80fa220d0589413fa3c00fac18cb56c Mon Sep 17 00:00:00 2001 From: Misha Bragin Date: Fri, 5 Dec 2025 17:01:11 +0100 Subject: [PATCH] add-image-zoom (#512) --- src/components/ImageZoom.jsx | 85 ++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/components/ImageZoom.jsx diff --git a/src/components/ImageZoom.jsx b/src/components/ImageZoom.jsx new file mode 100644 index 00000000..2746c003 --- /dev/null +++ b/src/components/ImageZoom.jsx @@ -0,0 +1,85 @@ +import { useEffect, useState, useCallback } from 'react' +import { createPortal } from 'react-dom' + +export function ImageZoom() { + const [zoomedImage, setZoomedImage] = useState(null) + const [isClosing, setIsClosing] = useState(false) + + const closeZoom = useCallback(() => { + setIsClosing(true) + setTimeout(() => { + setZoomedImage(null) + setIsClosing(false) + }, 200) + }, []) + + useEffect(() => { + const handleImageClick = (e) => { + const target = e.target + if ( + target.tagName === 'IMG' && + (target.classList.contains('imagewrapper') || + target.classList.contains('imagewrapper-big') || + target.closest('.imagewrapper') || + target.closest('.imagewrapper-big')) + ) { + e.preventDefault() + setZoomedImage(target.src) + } + } + + const handleKeyDown = (e) => { + if (e.key === 'Escape' && zoomedImage) { + closeZoom() + } + } + + document.addEventListener('click', handleImageClick) + document.addEventListener('keydown', handleKeyDown) + + return () => { + document.removeEventListener('click', handleImageClick) + document.removeEventListener('keydown', handleKeyDown) + } + }, [zoomedImage, closeZoom]) + + if (!zoomedImage) return null + + return createPortal( +
+ + Zoomed view +
, + document.body + ) +} \ No newline at end of file