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.
This commit is contained in:
GotthardG 2025-03-05 13:39:04 +01:00
parent 91aebae473
commit 3a63e5a6d8
3 changed files with 70 additions and 9 deletions

View File

@ -299,6 +299,7 @@ const ResultGrid: React.FC<ResultGridProps> = ({ activePgroup }) => {
return ( return (
<RunDetails <RunDetails
run={params.row} run={params.row}
basePath={basePath}
onHeightChange={(height: number) => handleDetailPanelHeightChange(params.row.id, height)} // Pass callback for dynamic height onHeightChange={(height: number) => handleDetailPanelHeightChange(params.row.id, height)} // Pass callback for dynamic height
/> />
); );

View File

@ -5,18 +5,23 @@ import {
AccordionDetails, AccordionDetails,
Typography, Typography,
Grid, Grid,
Modal,
Box
} from '@mui/material'; } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import './SampleImage.css'; import './SampleImage.css';
interface RunDetailsProps { interface RunDetailsProps {
run: ExperimentParameters; run: ExperimentParameters;
basePath: string;
onHeightChange?: (height: number) => void; // Callback to notify the parent about height changes 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 containerRef = useRef<HTMLDivElement | null>(null); // Ref to track component height
const [currentHeight, setCurrentHeight] = useState<number>(0); 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 { beamline_parameters, images } = run;
const { synchrotron, beamline, detector } = beamline_parameters; const { synchrotron, beamline, detector } = beamline_parameters;
@ -49,8 +54,20 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => {
}; };
}, [containerRef]); }, [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 ( return (
<div <div
className="details-panel" // Add the class here
ref={containerRef} // Attach the ref to the main container ref={containerRef} // Attach the ref to the main container
style={{ style={{
display: 'flex', display: 'flex',
@ -61,7 +78,8 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => {
alignItems: 'flex-start', alignItems: 'flex-start',
}} }}
> >
{/* Main Details Section */}
{/* Main Details Section */}
<div style={{ flexGrow: 1 }}> <div style={{ flexGrow: 1 }}>
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
Run {run.run_number} Details Run {run.run_number} Details
@ -144,17 +162,23 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => {
Associated Images Associated Images
</Typography> </Typography>
{images && images.length > 0 ? ( {images && images.length > 0 ? (
<Grid container spacing={2}> <Grid container spacing={1}>
{images.map((img) => ( {images.map((img) => (
<Grid item xs={6} key={img.id}> <Grid item xs={4} key={img.id}>
<div className="image-container"> <div
className="image-container"
onClick={() => handleImageClick(`${basePath || ''}${img.filepath}`)} // Open modal with image
style={{
cursor: 'pointer',
}}
>
<img <img
src={img.filepath} src={`${basePath || ''}${img.filepath}`} // Ensure basePath
alt={img.comment || 'Image'} alt={img.comment || 'Image'}
className="zoom-image" className="zoom-image"
style={{ style={{
width: '100%', width: '100%', // Ensure the image takes the full width of its container
maxWidth: '100%', maxWidth: '100%', // Prevent any overflow
borderRadius: '4px', borderRadius: '4px',
}} }}
/> />
@ -168,6 +192,35 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange }) => {
</Typography> </Typography>
)} )}
</div> </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> </div>
); );
}; };

View File

@ -14,3 +14,10 @@
.image-container { .image-container {
overflow: visible; /* Ensures zoomed images are not cropped by the parent */ overflow: visible; /* Ensures zoomed images are not cropped by the parent */
} }
/* 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 */
}