Refactor grid button styles for improved visibility and interaction

This commit is contained in:
Ross
2026-04-27 09:21:59 +01:00
parent c5a58da6a3
commit 776b5faffe
+26 -9
View File
@@ -4177,7 +4177,7 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
<button <button
style={{ style={{
position: "relative", position: "relative",
top: gridBtnActive ? "0" : "-22px", top: gridBtnActive ? "0" : "-43px",
opacity: gridBtnActive ? 1 : 0.5, opacity: gridBtnActive ? 1 : 0.5,
transition: "top 0.25s cubic-bezier(.4,2,.6,1), opacity 0.2s", transition: "top 0.25s cubic-bezier(.4,2,.6,1), opacity 0.2s",
padding: "6px 16px", padding: "6px 16px",
@@ -4191,11 +4191,13 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
marginLeft: "4px", marginLeft: "4px",
marginRight: "8px", marginRight: "8px",
boxShadow: "0 2px 8px rgba(0,0,0,0.2)", boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
pointerEvents: gridBtnActive ? "auto" : "none", pointerEvents: "auto",
zIndex: 2, zIndex: 2,
display: gridBtnActive ? "block" : "none", display: "block",
}} }}
title="Toggle Fullscreen" title="Toggle Fullscreen"
onMouseEnter={() => setGridBtnActive(true)}
onMouseLeave={() => setGridBtnActive(false)}
onClick={() => { onClick={() => {
const root = document.querySelector(".dicom-viewer-root") as HTMLElement; const root = document.querySelector(".dicom-viewer-root") as HTMLElement;
if (!root) return; if (!root) return;
@@ -4216,7 +4218,7 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
className="grid-menu-btn" className="grid-menu-btn"
style={{ style={{
position: "relative", position: "relative",
top: gridBtnActive ? "0" : "-22px", top: gridBtnActive ? "0" : "-43px",
opacity: gridBtnActive ? 1 : 0.5, opacity: gridBtnActive ? 1 : 0.5,
transition: "top 0.25s cubic-bezier(.4,2,.6,1), opacity 0.2s", transition: "top 0.25s cubic-bezier(.4,2,.6,1), opacity 0.2s",
padding: "6px 16px", padding: "6px 16px",
@@ -4229,10 +4231,11 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
fontSize: "15px", fontSize: "15px",
marginRight: "8px", marginRight: "8px",
boxShadow: "0 2px 8px rgba(0,0,0,0.2)", boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
pointerEvents: gridBtnActive ? "auto" : "none", // Only clickable when active pointerEvents: "auto",
zIndex: 2, zIndex: 2,
display: gridBtnActive ? "block" : "none", display: "block",
}} }}
onMouseEnter={() => setGridBtnActive(true)}
onMouseLeave={() => setGridBtnActive(false)} onMouseLeave={() => setGridBtnActive(false)}
onBlur={() => setGridBtnActive(false)} onBlur={() => setGridBtnActive(false)}
onClick={() => setViewportMenuOpen(open => !open)} onClick={() => setViewportMenuOpen(open => !open)}
@@ -4350,6 +4353,9 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
> >
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', borderBottom: '1px solid #2b2f31' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', borderBottom: '1px solid #2b2f31' }}>
<div style={{ fontWeight: 700 }}>Stacks</div> <div style={{ fontWeight: 700 }}>Stacks</div>
<div style={{ fontSize: 12, color: '#9bb4c8' }}>
Active VP: {activeViewport !== null ? activeViewport + 1 : '-'}
</div>
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}> <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
<button <button
title="Close" title="Close"
@@ -4372,6 +4378,7 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
const count = imageIds.length; const count = imageIds.length;
const invalid = !Array.isArray(imageIds) || count === 0; const invalid = !Array.isArray(imageIds) || count === 0;
const isSelected = selectedStackIdx.includes(idx); const isSelected = selectedStackIdx.includes(idx);
const isActiveViewportStack = activeViewport !== null && selectedStackIdx[activeViewport] === idx;
// Prefer the middle image for a representative thumbnail, fallback to first // Prefer the middle image for a representative thumbnail, fallback to first
const midIndex = Math.floor(imageIds.length / 2); const midIndex = Math.floor(imageIds.length / 2);
const chosenImage = imageIds[midIndex] || imageIds[0]; const chosenImage = imageIds[midIndex] || imageIds[0];
@@ -4387,14 +4394,19 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
e.dataTransfer.setData('text/stack-idx', String(idx)); e.dataTransfer.setData('text/stack-idx', String(idx));
e.dataTransfer.effectAllowed = 'copy'; e.dataTransfer.effectAllowed = 'copy';
}} }}
onClick={() => { if (!invalid) handleLoadStack(0, idx); }} onClick={() => {
if (invalid) return;
const targetViewport = activeViewport ?? 0;
handleLoadStack(targetViewport, idx);
}}
style={{ style={{
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
padding: '8px 10px', padding: '8px 10px',
background: isSelected ? '#1976d2' : 'transparent', background: isActiveViewportStack ? '#0d47a1' : (isSelected ? '#1976d2' : 'transparent'),
color: isSelected ? '#fff' : '#ddd', color: isSelected ? '#fff' : '#ddd',
border: isActiveViewportStack ? '1px solid #64b5f6' : '1px solid transparent',
borderRadius: 6, borderRadius: 6,
cursor: invalid ? 'not-allowed' : 'pointer', cursor: invalid ? 'not-allowed' : 'pointer',
}} }}
@@ -4426,7 +4438,12 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
<div style={{ fontSize: 12, opacity: 0.7 }}>{count} img{count === 1 ? '' : 's'}</div> <div style={{ fontSize: 12, opacity: 0.7 }}>{count} img{count === 1 ? '' : 's'}</div>
</div> </div>
</div> </div>
<div style={{ fontSize: 12, opacity: 0.8 }}>{stack.studyId || ''}</div> <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4 }}>
<div style={{ fontSize: 12, opacity: 0.8 }}>{stack.studyId || ''}</div>
{isActiveViewportStack && (
<div style={{ fontSize: 11, color: '#bbdefb', fontWeight: 700 }}>ACTIVE</div>
)}
</div>
</div> </div>
); );
})} })}