feat: Lesion tracker right panel (#1428)
* WIP * Finish mock * Update name, remove vendor prefix * Remove lesion tracker from defaults * Refactor measurement table changes * Remove unused code * Update naming of collections
This commit is contained in:
parent
3529474a16
commit
98a649b455
@ -0,0 +1,60 @@
|
|||||||
|
.MeasurementComparisonTable {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .displayTexts {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .displayTexts .measurementDisplayText {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .measurementTableHeader {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .measurementTableHeader .warning-status {
|
||||||
|
left: 0;
|
||||||
|
top: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 40px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .measurementTableHeader .warning-status .warning-border {
|
||||||
|
padding: 2px 3px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .measurementTableHeader .warning-status svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .measurementTableHeader .measurementTableHeaderItem {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .generate-report {
|
||||||
|
background-color: #151a1f;
|
||||||
|
margin-top: 2px;
|
||||||
|
padding: 10px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MeasurementComparisonTable .generate-report button {
|
||||||
|
font-size: 12px;
|
||||||
|
color: black;
|
||||||
|
background-color: var(--active-color);
|
||||||
|
}
|
||||||
@ -0,0 +1,151 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { MeasurementTable } from '@ohif/ui';
|
||||||
|
|
||||||
|
import './MeasurementComparisonTable.css';
|
||||||
|
|
||||||
|
const overallWarnings = {
|
||||||
|
warningList: [
|
||||||
|
'All measurements should have a location',
|
||||||
|
'Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const measurements = [
|
||||||
|
{
|
||||||
|
measurementId: '125',
|
||||||
|
measurementNumber: '125',
|
||||||
|
itemNumber: 1,
|
||||||
|
label: '(No description)',
|
||||||
|
data: [{ displayText: '12.5 x 4.6' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
measurementId: '124',
|
||||||
|
measurementNumber: '124',
|
||||||
|
itemNumber: 2,
|
||||||
|
label: '(No description)',
|
||||||
|
data: [{ displayText: '32.5 x 1.6' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
measurementId: '123',
|
||||||
|
measurementNumber: '123',
|
||||||
|
itemNumber: 3,
|
||||||
|
hasWarnings: true,
|
||||||
|
warningList: [
|
||||||
|
'All measurements should have a location',
|
||||||
|
'Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR',
|
||||||
|
],
|
||||||
|
label: '(No description)',
|
||||||
|
data: [{ displayText: '5.5 x 9.2' }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const additionalFindings = [
|
||||||
|
{
|
||||||
|
measurementId: '122',
|
||||||
|
measurementNumber: '122',
|
||||||
|
itemNumber: 1,
|
||||||
|
hasWarnings: true,
|
||||||
|
warningList: [
|
||||||
|
'All measurements should have a location',
|
||||||
|
'Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR',
|
||||||
|
],
|
||||||
|
label: '(No description)',
|
||||||
|
data: [{ displayText: '23.5 x 9.2' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
measurementId: '121',
|
||||||
|
measurementNumber: '121',
|
||||||
|
itemNumber: 2,
|
||||||
|
hasWarnings: true,
|
||||||
|
warningList: [
|
||||||
|
'All measurements should have a location',
|
||||||
|
'Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR',
|
||||||
|
],
|
||||||
|
label: '(No description)',
|
||||||
|
data: [{ displayText: '11.2 x 9.2' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
measurementId: '120',
|
||||||
|
measurementNumber: '120',
|
||||||
|
itemNumber: 3,
|
||||||
|
label: '(No description)',
|
||||||
|
data: [{ displayText: '2.9 x 9.2' }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const currentCollections = [
|
||||||
|
{
|
||||||
|
selectorAction: () => { },
|
||||||
|
maxMeasurements: 3,
|
||||||
|
groupName: 'Measurements',
|
||||||
|
measurements: measurements,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selectorAction: () => { },
|
||||||
|
groupName: 'Additional Findings',
|
||||||
|
measurements: additionalFindings,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const comparisonColletions = [
|
||||||
|
{
|
||||||
|
selectorAction: () => { },
|
||||||
|
maxMeasurements: 3,
|
||||||
|
groupName: 'Measurements',
|
||||||
|
measurements: measurements,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selectorAction: () => { },
|
||||||
|
groupName: 'Additional Findings',
|
||||||
|
measurements: additionalFindings,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const comparisonCollections = currentCollections.map((group, index) => {
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
measurements: group.measurements.map((measurement, measurementIndex) => {
|
||||||
|
const comparisonCollection = comparisonColletions[index].measurements;
|
||||||
|
if (measurementIndex < comparisonCollection.length) {
|
||||||
|
return {
|
||||||
|
...measurement,
|
||||||
|
data: [
|
||||||
|
...measurement.data,
|
||||||
|
...comparisonCollection[measurementIndex].data,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const comparisonTimepoints = [
|
||||||
|
{
|
||||||
|
key: 'Current',
|
||||||
|
date: '10-Apr-18',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Comparison',
|
||||||
|
date: '15-Jun-18',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const MeasurementComparisonTable = () => {
|
||||||
|
return (
|
||||||
|
<div className="MeasurementComparisonTable">
|
||||||
|
<MeasurementTable
|
||||||
|
timepoints={comparisonTimepoints}
|
||||||
|
overallWarnings={overallWarnings}
|
||||||
|
measurementCollection={comparisonCollections}
|
||||||
|
onRelabelClick={() => { }}
|
||||||
|
onEditDescriptionClick={() => { }}
|
||||||
|
/>
|
||||||
|
<div className="generate-report">
|
||||||
|
<button className="btn btn-primary">Generate Report</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MeasurementComparisonTable;
|
||||||
@ -1,12 +1,4 @@
|
|||||||
// SEE: Async imports (code-splitting)
|
import MeasurementComparisonTable from './components/MeasurementComparisonTable';
|
||||||
// import asyncComponent from './asyncComponent.js';
|
|
||||||
// import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
|
|
||||||
|
|
||||||
// const ConnectedOHIFDicomPDFViewer = asyncComponent(() =>
|
|
||||||
// import(
|
|
||||||
// /* webpackChunkName: "ConnectedOHIFDicomPDFViewer" */ './ConnectedOHIFDicomPDFViewer'
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@ -20,56 +12,21 @@ export default {
|
|||||||
* @param {CommandsManager} params.commandsManager
|
* @param {CommandsManager} params.commandsManager
|
||||||
*/
|
*/
|
||||||
getPanelModule({ servicesManager, commandsManager }) {
|
getPanelModule({ servicesManager, commandsManager }) {
|
||||||
return undefined;
|
|
||||||
// {
|
|
||||||
// TODO:
|
|
||||||
// - Right, alternative StudyBrowser
|
|
||||||
// - Left, alternative Measurements Table
|
|
||||||
// menuOptions: [
|
|
||||||
// {
|
|
||||||
// // A suggested icon
|
|
||||||
// // Available icons determined by consuming app
|
|
||||||
// icon: 'list',
|
|
||||||
// // A suggested label
|
|
||||||
// label: 'Magic',
|
|
||||||
// // 'right' or 'left'
|
|
||||||
// from: 'right',
|
|
||||||
// // The target component to toggle open/close
|
|
||||||
// target: 'target-component-id',
|
|
||||||
// // Overrides `defaultContext`, if specified
|
|
||||||
// context: ['ACTIVE_VIEWPORT:MAGIC'],
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// components: [
|
|
||||||
// {
|
|
||||||
// id: 'target-component-id',
|
|
||||||
// component: MyComponent,
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// defaultContext: ['ROUTE:VIEWER'],
|
|
||||||
// };
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
* @returns Object
|
|
||||||
*/
|
|
||||||
getToolbarModule({ servicesManager, commandsManager }) {
|
|
||||||
// TODO: Zoom, Levels, Pan, More (see lesiontracker.ohif.org)
|
|
||||||
// These may be driven through configuration of `cornerstone` extension instead of defined here
|
|
||||||
return {
|
return {
|
||||||
definitions: [
|
menuOptions: [
|
||||||
{
|
{
|
||||||
id: 'say-hell-world',
|
icon: 'th-list',
|
||||||
label: '🎉 HELLO WORLD 🎉',
|
label: 'Measurements',
|
||||||
icon: 'exclamation-triangle',
|
target: 'lesion-tracker-panel',
|
||||||
type: 'command',
|
|
||||||
commandName: 'sayHelloWorld',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
defaultContext: 'VIEWER',
|
components: [
|
||||||
|
{
|
||||||
|
id: 'lesion-tracker-panel',
|
||||||
|
component: MeasurementComparisonTable,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultContext: ['VIEWER']
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -79,23 +36,17 @@ export default {
|
|||||||
* @param {CommandsManager} params.commandsManager
|
* @param {CommandsManager} params.commandsManager
|
||||||
* @returns Object
|
* @returns Object
|
||||||
*/
|
*/
|
||||||
getCommandsModule({ servicesManager }) {
|
getToolbarModule() {
|
||||||
const { UINotificationService } = servicesManager.services;
|
return null;
|
||||||
return {
|
|
||||||
definitions: {
|
|
||||||
sayHelloWorld: {
|
|
||||||
commandFn: function() {
|
|
||||||
console.log(UINotificationService);
|
|
||||||
UINotificationService.show({
|
|
||||||
title: 'What does a nosey pepper do?',
|
|
||||||
message: 'Gets jalapeno business!',
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
storeContexts: [],
|
|
||||||
options: {},
|
/**
|
||||||
},
|
* @param {object} params
|
||||||
},
|
* @param {ServicesManager} params.servicesManager
|
||||||
defaultContext: ['VIEWER'],
|
* @param {CommandsManager} params.commandsManager
|
||||||
};
|
* @returns Object
|
||||||
|
*/
|
||||||
|
getCommandsModule() {
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -184,10 +184,12 @@ class MeasurementTable extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getTimepointsHeader = () => {
|
getTimepointsHeader = () => {
|
||||||
return this.props.timepoints.map((timepoint, index) => {
|
const { timepoints, t } = this.props;
|
||||||
|
|
||||||
|
return timepoints.map((timepoint, index) => {
|
||||||
return (
|
return (
|
||||||
<div key={index} className="measurementTableHeaderItem">
|
<div key={index} className="measurementTableHeaderItem">
|
||||||
<div className="timepointLabel">{this.props.t(timepoint.key)}</div>
|
<div className="timepointLabel">{t(timepoint.key)}</div>
|
||||||
<div className="timepointDate">{timepoint.date}</div>
|
<div className="timepointDate">{timepoint.date}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -84,7 +84,6 @@
|
|||||||
position: relative
|
position: relative
|
||||||
top: 2px
|
top: 2px
|
||||||
|
|
||||||
|
|
||||||
.tooltip-warning
|
.tooltip-warning
|
||||||
&.in
|
&.in
|
||||||
opacity 1
|
opacity 1
|
||||||
|
|||||||
@ -101,7 +101,7 @@ class MeasurementTableItem extends Component {
|
|||||||
nsSeparator: '|',
|
nsSeparator: '|',
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div>{this.getDataDisplayText()}</div>
|
<div className="displayTexts">{this.getDataDisplayText()}</div>
|
||||||
<div className="rowActions">{actionButtons}</div>
|
<div className="rowActions">{actionButtons}</div>
|
||||||
</div>
|
</div>
|
||||||
</TableListItem>
|
</TableListItem>
|
||||||
|
|||||||
@ -25,6 +25,20 @@ $headerRowHeight = 63px
|
|||||||
text-align: left
|
text-align: left
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
|
.tableListHeaderSelector
|
||||||
|
cursor: pointer
|
||||||
|
max-width: 30px
|
||||||
|
padding-left: 2px
|
||||||
|
align-items: stretch
|
||||||
|
flex: 1
|
||||||
|
justify-content: space-around
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
svg
|
||||||
|
fill: #c1d8e3
|
||||||
|
height: 63px
|
||||||
|
max-width: 11px
|
||||||
|
|
||||||
.numberOfItems
|
.numberOfItems
|
||||||
color: var(--ui-sky-blue)
|
color: var(--ui-sky-blue)
|
||||||
float: right
|
float: right
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user