From 3a63e5a6d8de38fd11e606a935ec04da213e6e0a Mon Sep 17 00:00:00 2001 From: GotthardG <51994228+GotthardG@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:39:04 +0100 Subject: [PATCH] Add image modal functionality to RunDetails component Introduce a modal for zooming images in the RunDetails component, enhancing UX by allowing users to click and view images in detail. Adjust styling in SampleImage.css to disable zoom effects in the details panel and ensure proper image rendering. Update ResultGrid to pass the basePath prop to support dynamic image paths. --- frontend/src/components/ResultGrid.tsx | 1 + frontend/src/components/RunDetails.tsx | 69 ++++++++++++++++++++++--- frontend/src/components/SampleImage.css | 9 +++- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/ResultGrid.tsx b/frontend/src/components/ResultGrid.tsx index a3aa790..09ad8da 100644 --- a/frontend/src/components/ResultGrid.tsx +++ b/frontend/src/components/ResultGrid.tsx @@ -299,6 +299,7 @@ const ResultGrid: React.FC<ResultGridProps> = ({ activePgroup }) => { return ( <RunDetails run={params.row} + basePath={basePath} onHeightChange={(height: number) => handleDetailPanelHeightChange(params.row.id, height)} // Pass callback for dynamic height /> ); diff --git a/frontend/src/components/RunDetails.tsx b/frontend/src/components/RunDetails.tsx index 02cbf63..b0914a3 100644 --- a/frontend/src/components/RunDetails.tsx +++ b/frontend/src/components/RunDetails.tsx @@ -5,18 +5,23 @@ import { AccordionDetails, Typography, Grid, + Modal, + Box } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import './SampleImage.css'; interface RunDetailsProps { run: ExperimentParameters; + basePath: string; onHeightChange?: (height: number) => void; // Callback to notify the parent about height changes } -const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => { +const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange, basePath }) => { const containerRef = useRef<HTMLDivElement | null>(null); // Ref to track component height const [currentHeight, setCurrentHeight] = useState<number>(0); + const [modalOpen, setModalOpen] = useState<boolean>(false); // For modal state + const [selectedImage, setSelectedImage] = useState<string | null>(null); // Tracks the selected image for the modal const { beamline_parameters, images } = run; const { synchrotron, beamline, detector } = beamline_parameters; @@ -49,8 +54,20 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => { }; }, [containerRef]); + const handleImageClick = (imagePath: string) => { + setSelectedImage(imagePath); + setModalOpen(true); // Open the modal when the image is clicked + }; + + const closeModal = () => { + setSelectedImage(null); // Clear the current image + setModalOpen(false); + }; + + return ( <div + className="details-panel" // Add the class here ref={containerRef} // Attach the ref to the main container style={{ display: 'flex', @@ -61,7 +78,8 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => { alignItems: 'flex-start', }} > - {/* Main Details Section */} + + {/* Main Details Section */} <div style={{ flexGrow: 1 }}> <Typography variant="h6" gutterBottom> Run {run.run_number} Details @@ -144,17 +162,23 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => { Associated Images </Typography> {images && images.length > 0 ? ( - <Grid container spacing={2}> + <Grid container spacing={1}> {images.map((img) => ( - <Grid item xs={6} key={img.id}> - <div className="image-container"> + <Grid item xs={4} key={img.id}> + <div + className="image-container" + onClick={() => handleImageClick(`${basePath || ''}${img.filepath}`)} // Open modal with image + style={{ + cursor: 'pointer', + }} + > <img - src={img.filepath} + src={`${basePath || ''}${img.filepath}`} // Ensure basePath alt={img.comment || 'Image'} className="zoom-image" style={{ - width: '100%', - maxWidth: '100%', + width: '100%', // Ensure the image takes the full width of its container + maxWidth: '100%', // Prevent any overflow borderRadius: '4px', }} /> @@ -168,6 +192,35 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => { </Typography> )} </div> + + {/* Modal for Zoomed Image */} + <Modal open={modalOpen} onClose={closeModal}> + <Box + style={{ + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + backgroundColor: '#fff', + boxShadow: 24, + padding: 16, + maxHeight: '90%', + overflow: 'auto', + }} + > + {selectedImage && ( + <img + src={selectedImage} + alt="Zoomed" + style={{ + maxWidth: '100%', + height: 'auto', + borderRadius: '4px', + }} + /> + )} + </Box> + </Modal> </div> ); }; diff --git a/frontend/src/components/SampleImage.css b/frontend/src/components/SampleImage.css index ba545b9..e42d575 100644 --- a/frontend/src/components/SampleImage.css +++ b/frontend/src/components/SampleImage.css @@ -13,4 +13,11 @@ .image-container { overflow: visible; /* Ensures zoomed images are not cropped by the parent */ -} \ No newline at end of file +} + +/* Disable zoom effect specifically in the details panel */ +.details-panel .zoom-image:hover { + transform: none; /* Disable scaling */ + z-index: 1; /* Reset any z-index adjustment */ + border: none; /* Reset border effect */ +} -- GitLab