Skip to content

Commit 07880cc

Browse files
Add display of annotation label when hovering over ann (#355)
* Add display of annotation label when hovering over ann * Add area and multiple rois support * Update tooltip * Update about modal * Single tooltip view * Update dmv
1 parent 4f48c94 commit 07880cc

File tree

6 files changed

+313
-74
lines changed

6 files changed

+313
-74
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@testing-library/react": "^11.2.2",
4545
"@testing-library/user-event": "^7.1.2",
4646
"@types/jest": "^28.1.3",
47+
"@types/lodash": "^4.17.20",
4748
"@types/node": "^14.14.9",
4849
"@types/react": "^18.0.14",
4950
"@types/react-dom": "^18.0.5",
@@ -56,7 +57,7 @@
5657
"craco-less": "^2.0.0",
5758
"dcmjs": "^0.35.0",
5859
"detect-browser": "^5.2.1",
59-
"dicom-microscopy-viewer": "^0.48.13",
60+
"dicom-microscopy-viewer": "^0.48.14",
6061
"dicomweb-client": "^0.10.3",
6162
"gh-pages": "^5.0.0",
6263
"oidc-client": "^1.11.5",

src/components/Header.tsx

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from 'react'
22
import { NavLink } from 'react-router-dom'
33
import {
44
Col,
5-
Descriptions,
65
Dropdown,
76
Input,
87
Layout,
@@ -25,6 +24,7 @@ import {
2524
SettingOutlined
2625
} from '@ant-design/icons'
2726
import { detect } from 'detect-browser'
27+
import appPackageJson from '../../package.json'
2828

2929
import Button from './Button'
3030
import { RouteComponentProps, withRouter } from '../utils/router'
@@ -168,36 +168,57 @@ class Header extends React.Component<HeaderProps, HeaderState> {
168168
}
169169
}
170170

171+
const slimCommit = process.env.REACT_APP_GIT_SHA !== undefined && process.env.REACT_APP_GIT_SHA !== '' ? process.env.REACT_APP_GIT_SHA : null
172+
const viewerCommit = process.env.REACT_APP_DMV_GIT_SHA !== undefined && process.env.REACT_APP_DMV_GIT_SHA !== '' ? process.env.REACT_APP_DMV_GIT_SHA : null
173+
const devDeps = appPackageJson.devDependencies as Record<string, string> | undefined
174+
const deps = appPackageJson.dependencies as Record<string, string> | undefined
175+
const viewerVersionRaw =
176+
devDeps?.['dicom-microscopy-viewer'] ??
177+
deps?.['dicom-microscopy-viewer'] ??
178+
'unknown'
179+
const viewerVersion = viewerVersionRaw.replace(/^[^0-9]*/, '')
180+
171181
Modal.info({
172-
title: 'About',
173-
width: 600,
182+
width: 480,
183+
title: null,
184+
centered: true,
174185
content: (
175-
<>
176-
<Descriptions title='Application' column={1}>
177-
<Descriptions.Item label='Name'>
178-
{this.props.app.name}
179-
</Descriptions.Item>
180-
<Descriptions.Item label='Version'>
181-
{this.props.app.version}
182-
</Descriptions.Item>
183-
<Descriptions.Item label='Homepage'>
186+
<div style={{ textAlign: 'center', lineHeight: 1.6, paddingRight: 25 }}>
187+
<div style={{ fontSize: '2.2rem', fontWeight: 700, marginBottom: 4 }}>{this.props.app.name}</div>
188+
<div style={{ fontSize: '2rem', fontWeight: 600 }}>{this.props.app.version}</div>
189+
<div style={{ fontSize: '1rem', marginBottom: 16 }}>
190+
<a href={this.props.app.homepage} target='_blank' rel='noreferrer'>
184191
{this.props.app.homepage}
185-
</Descriptions.Item>
186-
</Descriptions>
187-
<Descriptions title='Browser' column={1}>
188-
<Descriptions.Item label='Name'>
189-
{environment.browser.name}
190-
</Descriptions.Item>
191-
<Descriptions.Item label='Version'>
192-
{environment.browser.version}
193-
</Descriptions.Item>
194-
</Descriptions>
195-
<Descriptions title='Operating System' column={1}>
196-
<Descriptions.Item label='Name'>
197-
{environment.os.name}
198-
</Descriptions.Item>
199-
</Descriptions>
200-
</>
192+
</a>
193+
</div>
194+
195+
<div style={{ marginBottom: 12 }}>
196+
<div style={{ fontWeight: 600 }}>Commit Hash</div>
197+
<code>{slimCommit ?? 'unknown'}</code>
198+
</div>
199+
200+
<div style={{ marginBottom: 12 }}>
201+
<div style={{ fontWeight: 600 }}>
202+
<a
203+
href='https://github.com/MGHComputationalPathology/dicom-microscopy-viewer'
204+
target='_blank'
205+
rel='noreferrer'
206+
>
207+
DICOM Microscopy Viewer
208+
</a>
209+
</div>
210+
<div>Version {viewerVersion}</div>
211+
<code>{viewerCommit ?? 'unknown'}</code>
212+
</div>
213+
214+
<div style={{ marginBottom: 12 }}>
215+
<div style={{ fontWeight: 600 }}>Current Browser &amp; OS</div>
216+
<div>
217+
{environment.browser.name ?? 'Unknown'} {environment.browser.version ?? ''}
218+
</div>
219+
<div>{environment.os.name ?? 'Unknown OS'}</div>
220+
</div>
221+
</div>
201222
),
202223
onOk (): void {}
203224
})

src/components/HoveredRoiTooltip.tsx

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,78 @@ const HoveredRoiTooltip = ({
55
}: {
66
xPosition: number
77
yPosition: number
8-
rois: Array<{ index: number, roiUid: string, attributes: Array<{ name: string, value: string }>}>
8+
rois: Array<{ index: number, roiUid: string, attributes: Array<{ name: string, value: string }>, seriesDescription?: string }>
99
}): JSX.Element => {
10+
// Always group ROIs by series description
11+
const groupedRois = rois.reduce<{ [key: string]: typeof rois }>((acc, roi) => {
12+
const seriesDesc = (roi.seriesDescription !== null && roi.seriesDescription !== undefined && roi.seriesDescription !== '') ? roi.seriesDescription : 'Unknown Series'
13+
if (acc[seriesDesc] === undefined) {
14+
acc[seriesDesc] = []
15+
}
16+
acc[seriesDesc].push(roi)
17+
return acc
18+
}, {})
19+
20+
const baseStyle: React.CSSProperties = {
21+
position: 'fixed',
22+
top: `${yPosition}px`,
23+
left: `${xPosition}px`,
24+
backgroundColor: 'rgba(230, 230, 230, 0.95)',
25+
padding: '10px',
26+
fontWeight: 'bold',
27+
pointerEvents: 'none',
28+
borderRadius: '4px',
29+
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
30+
zIndex: 10000
31+
}
32+
1033
return (
1134
<div
1235
style={{
13-
position: 'fixed',
14-
top: `${yPosition}px`,
15-
left: `${xPosition}px`,
16-
backgroundColor: 'rgba(230, 230, 230, 0.65)',
17-
minWidth: '150px',
18-
minHeight: '60px',
19-
padding: '20px',
20-
fontWeight: 'bold',
21-
pointerEvents: 'none'
36+
...baseStyle,
37+
minWidth: '200px',
38+
maxWidth: '400px'
2239
}}
2340
>
24-
{rois.map((roi, i) => {
25-
const attributes = roi.attributes
26-
return (
27-
<div key={roi.roiUid}>
28-
<span>ROI {roi.index}</span>
29-
{attributes.map((attr) => {
41+
{Object.entries(groupedRois).map(([seriesDesc, seriesRois], seriesIndex) => (
42+
<div key={seriesDesc} style={{ marginBottom: seriesIndex > 0 ? '12px' : '0' }}>
43+
{seriesIndex > 0 && (
44+
<hr style={{ margin: '10px 0', border: 'none', borderTop: '1px solid rgba(0, 0, 0, 0.2)' }} />
45+
)}
46+
<div style={{ fontSize: '13px', fontWeight: 'bold', color: 'rgba(0, 0, 0, 0.8)', marginBottom: '8px', paddingBottom: '4px', borderBottom: '1px solid rgba(0, 0, 0, 0.15)' }}>
47+
{seriesDesc}
48+
</div>
49+
<div style={{ marginLeft: '4px' }}>
50+
{seriesRois.map((roi: { index: number, roiUid: string, attributes: Array<{ name: string, value: string }>, seriesDescription?: string }, roiIndex: number) => {
51+
const annotationGroupLabelAttr = roi.attributes.find((attr: { name: string, value: string }) => attr.name === 'Annotation Group Label')
52+
const otherAttributes = roi.attributes.filter(
53+
(attr: { name: string, value: string }) => attr.name !== 'Series Description' && attr.name !== 'Annotation Group Label'
54+
)
3055
return (
31-
<div key={attr.name + roi.roiUid}>
32-
{attr.name}: <span style={{ fontWeight: 500 }}>{attr.value}</span>
56+
<div key={roi.roiUid} style={{ marginBottom: roiIndex < seriesRois.length - 1 ? '6px' : '0', fontSize: '12px' }}>
57+
<div style={{ fontWeight: 'bold' }}>
58+
ROI {roi.index}
59+
{(annotationGroupLabelAttr != null) && (
60+
<span style={{ fontWeight: 500, marginLeft: '6px', color: 'rgba(0, 0, 0, 0.7)' }}>
61+
- {annotationGroupLabelAttr.value}
62+
</span>
63+
)}
64+
</div>
65+
{otherAttributes.length > 0 && (
66+
<div style={{ marginLeft: '12px', fontSize: '11px', color: 'rgba(0, 0, 0, 0.8)', marginTop: '2px' }}>
67+
{otherAttributes.map((attr: { name: string, value: string }) => (
68+
<div key={String(attr.name) + '-' + String(roi.roiUid)}>
69+
{attr.name}: <span style={{ fontWeight: 500 }}>{attr.value}</span>
70+
</div>
71+
))}
72+
</div>
73+
)}
3374
</div>
3475
)
3576
})}
3677
</div>
37-
38-
)
39-
})}
78+
</div>
79+
))}
4080
</div>
4181
)
4282
}

0 commit comments

Comments
 (0)