Replace measurementsPanel w/ a format that reduces nesting
This commit is contained in:
parent
98497f666c
commit
95249928a8
37
extensions/default/src/ActionButtons.jsx
Normal file
37
extensions/default/src/ActionButtons.jsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button, ButtonGroup, Icon, IconButton } from '@ohif/ui';
|
||||||
|
|
||||||
|
function ActionButtons() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<ButtonGroup onClick={() => alert('Export')}>
|
||||||
|
<Button
|
||||||
|
className="px-2 py-2 text-base text-white bg-black border-primary-main"
|
||||||
|
size="initial"
|
||||||
|
color="inherit"
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
className="px-2 text-white bg-black border-primary-main"
|
||||||
|
color="inherit"
|
||||||
|
size="initial"
|
||||||
|
>
|
||||||
|
<Icon name="arrow-down" />
|
||||||
|
</IconButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
<Button
|
||||||
|
className="px-2 py-2 ml-2 text-base text-white bg-black border border-primary-main"
|
||||||
|
variant="outlined"
|
||||||
|
size="initial"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => alert('Create Report')}
|
||||||
|
>
|
||||||
|
Create Report
|
||||||
|
</Button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionButtons;
|
||||||
@ -1,73 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
MeasurementsPanel,
|
|
||||||
Button,
|
|
||||||
ButtonGroup,
|
|
||||||
Icon,
|
|
||||||
IconButton,
|
|
||||||
} from '@ohif/ui';
|
|
||||||
|
|
||||||
export default function MeasurementTable({ servicesManager, commandsManager }) {
|
|
||||||
const { MeasurementService } = servicesManager.services;
|
|
||||||
|
|
||||||
console.log('MeasurementTable rendering!!!!!!!!!!!!!');
|
|
||||||
|
|
||||||
const actionButtons = (
|
|
||||||
<React.Fragment>
|
|
||||||
<ButtonGroup onClick={() => alert('Export')}>
|
|
||||||
<Button
|
|
||||||
className="text-white border-primary-main bg-black text-base py-2 px-2"
|
|
||||||
size="initial"
|
|
||||||
color="inherit"
|
|
||||||
>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
|
||||||
className="bg-black border-primary-main px-2 text-white px-2"
|
|
||||||
color="inherit"
|
|
||||||
size="initial"
|
|
||||||
>
|
|
||||||
<Icon name="arrow-down" />
|
|
||||||
</IconButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
<Button
|
|
||||||
className="text-white border border-primary-main bg-black text-base py-2 px-2 ml-2"
|
|
||||||
variant="outlined"
|
|
||||||
size="initial"
|
|
||||||
color="inherit"
|
|
||||||
onClick={() => alert('Create Report')}
|
|
||||||
>
|
|
||||||
Create Report
|
|
||||||
</Button>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
const descriptionData = {
|
|
||||||
date: '07-Sep-2010',
|
|
||||||
modality: 'CT',
|
|
||||||
description: 'CHEST/ABD/PELVIS W CONTRAST',
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeMeasurementItem = 0;
|
|
||||||
|
|
||||||
const measurementTableData = {
|
|
||||||
title: 'Measurements',
|
|
||||||
amount: 10,
|
|
||||||
data: new Array(10).fill({}).map((el, i) => ({
|
|
||||||
id: i + 1,
|
|
||||||
label: 'Label short description',
|
|
||||||
displayText: '24.0 x 24.0 mm (S:4, I:22)',
|
|
||||||
isActive: activeMeasurementItem === i + 1,
|
|
||||||
})),
|
|
||||||
onClick: id => setActiveMeasurementItem(s => (s === id ? null : id)),
|
|
||||||
onEdit: id => alert(`Edit: ${id}`),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MeasurementsPanel
|
|
||||||
descriptionData={descriptionData}
|
|
||||||
measurementTableData={measurementTableData}
|
|
||||||
actionButtons={actionButtons}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
55
extensions/default/src/PanelMeasurementTable.js
Normal file
55
extensions/default/src/PanelMeasurementTable.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { StudySummary, MeasurementTable } from '@ohif/ui';
|
||||||
|
import ActionButtons from './ActionButtons.jsx';
|
||||||
|
|
||||||
|
export default function PanelMeasurementTable({
|
||||||
|
servicesManager,
|
||||||
|
commandsManager,
|
||||||
|
}) {
|
||||||
|
const { MeasurementService } = servicesManager.services;
|
||||||
|
|
||||||
|
console.log('MeasurementTable rendering!!!!!!!!!!!!!');
|
||||||
|
|
||||||
|
const descriptionData = {
|
||||||
|
date: '07-Sep-2010',
|
||||||
|
modality: 'CT',
|
||||||
|
description: 'CHEST/ABD/PELVIS W CONTRAST',
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeMeasurementItem = 0;
|
||||||
|
|
||||||
|
const measurementTableData = {
|
||||||
|
title: 'Measurements',
|
||||||
|
amount: 10,
|
||||||
|
data: new Array(10).fill({}).map((el, i) => ({
|
||||||
|
id: i + 1,
|
||||||
|
label: 'Label short description',
|
||||||
|
displayText: '24.0 x 24.0 mm (S:4, I:22)',
|
||||||
|
isActive: activeMeasurementItem === i + 1,
|
||||||
|
})),
|
||||||
|
onClick: id => setActiveMeasurementItem(s => (s === id ? null : id)),
|
||||||
|
onEdit: id => alert(`Edit: ${id}`),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="overflow-x-hidden overflow-y-auto invisible-scrollbar">
|
||||||
|
<StudySummary
|
||||||
|
date={descriptionData.date}
|
||||||
|
modality={descriptionData.modality}
|
||||||
|
description={descriptionData.description}
|
||||||
|
/>
|
||||||
|
<MeasurementTable
|
||||||
|
title="Measurements"
|
||||||
|
amount={measurementTableData.data.length}
|
||||||
|
data={measurementTableData.data}
|
||||||
|
onClick={() => {}}
|
||||||
|
onEdit={id => alert(`Edit: ${id}`)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center p-4">
|
||||||
|
<ActionButtons />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { WrappedPanelStudyBrowser } from './Panels';
|
import { WrappedPanelStudyBrowser } from './Panels';
|
||||||
|
|
||||||
import MeasurementTable from './MeasurementTable.js';
|
import PanelMeasurementTable from './PanelMeasurementTable.js';
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - No loading UI exists yet
|
// - No loading UI exists yet
|
||||||
@ -15,7 +15,7 @@ function getPanelModule({
|
|||||||
}) {
|
}) {
|
||||||
const wrappedMeasurementPanel = () => {
|
const wrappedMeasurementPanel = () => {
|
||||||
return (
|
return (
|
||||||
<MeasurementTable
|
<PanelMeasurementTable
|
||||||
commandsManager={commandsManager}
|
commandsManager={commandsManager}
|
||||||
servicesManager={servicesManager}
|
servicesManager={servicesManager}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
|
||||||
const SRC_DIR = path.join(__dirname, '../src');
|
|
||||||
const DIST_DIR = path.join(__dirname, '../dist');
|
|
||||||
|
|
||||||
module.exports = (env, argv) => {
|
|
||||||
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
|
||||||
};
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
const merge = require('webpack-merge');
|
|
||||||
const path = require('path');
|
|
||||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
|
||||||
const pkg = require('./../package.json');
|
|
||||||
|
|
||||||
const ROOT_DIR = path.join(__dirname, './..');
|
|
||||||
const SRC_DIR = path.join(__dirname, '../src');
|
|
||||||
const DIST_DIR = path.join(__dirname, '../dist');
|
|
||||||
|
|
||||||
module.exports = (env, argv) => {
|
|
||||||
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
|
||||||
|
|
||||||
return merge(commonConfig, {
|
|
||||||
devtool: 'source-map',
|
|
||||||
stats: {
|
|
||||||
colors: true,
|
|
||||||
hash: true,
|
|
||||||
timings: true,
|
|
||||||
assets: true,
|
|
||||||
chunks: false,
|
|
||||||
chunkModules: false,
|
|
||||||
modules: false,
|
|
||||||
children: false,
|
|
||||||
warnings: true,
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimize: true,
|
|
||||||
sideEffects: true,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
path: ROOT_DIR,
|
|
||||||
library: 'OHIFExtLesionTracker',
|
|
||||||
libraryTarget: 'umd',
|
|
||||||
libraryExport: 'default',
|
|
||||||
filename: pkg.main,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
# Change Log
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
||||||
|
|
||||||
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-lesion-tracker@0.1.0...@ohif/extension-lesion-tracker@0.2.0) (2020-02-10)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Lesion tracker right panel ([#1428](https://github.com/OHIF/Viewers/issues/1428)) ([98a649b](https://github.com/OHIF/Viewers/commit/98a649b455ffc712938fc5035cdef40695e58440))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 0.1.0 (2020-02-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* lesion-tracker extension ([#1420](https://github.com/OHIF/Viewers/issues/1420)) ([73e4409](https://github.com/OHIF/Viewers/commit/73e440968ce4699d081a9c9f2d21dd68095b3056))
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018 Open Health Imaging Foundation
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
# @ohif/extension-lesion-tracker
|
|
||||||
|
|
||||||
This project is an OHIF extension that can be used with the Core OHIF Platform,
|
|
||||||
either at runtime, or as an ES6 dependency, to create a medical image viewing
|
|
||||||
application similar to that of the [legacy Lesion
|
|
||||||
Tracker][legacy-lesion-tracker] viewer.
|
|
||||||
|
|
||||||
## About
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
### Extensions
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
## Build & Deploy
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
## Funding/Support
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
<!--
|
|
||||||
LINKS
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[legacy-lesion-tracker]: http://lesiontracker.ohif.org/studylist
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@ohif/extension-lesion-tracker",
|
|
||||||
"version": "0.2.0",
|
|
||||||
"description": "OHIF extension for Lesion Tracker",
|
|
||||||
"author": "OHIF",
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": "OHIF/Viewers",
|
|
||||||
"main": "dist/index.umd.js",
|
|
||||||
"module": "src/index.js",
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10",
|
|
||||||
"npm": ">=6",
|
|
||||||
"yarn": ">=1.16.0"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist",
|
|
||||||
"README.md"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
|
||||||
"dev:lesion-tracker": "yarn run dev",
|
|
||||||
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
|
|
||||||
"build:package": "yarn run build",
|
|
||||||
"start": "yarn run dev"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@ohif/core": "^0.50.0",
|
|
||||||
"prop-types": "^15.6.2",
|
|
||||||
"react": "^16.11.0",
|
|
||||||
"react-dom": "^16.11.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "7.7.6",
|
|
||||||
"classnames": "^2.2.6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
.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);
|
|
||||||
}
|
|
||||||
@ -1,151 +0,0 @@
|
|||||||
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,52 +0,0 @@
|
|||||||
import MeasurementComparisonTable from './components/MeasurementComparisonTable';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* Only required property. Should be a unique value across all extensions.
|
|
||||||
*/
|
|
||||||
id: 'lesion-tracker',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
*/
|
|
||||||
getPanelModule({ servicesManager, commandsManager }) {
|
|
||||||
return {
|
|
||||||
menuOptions: [
|
|
||||||
{
|
|
||||||
icon: 'th-list',
|
|
||||||
label: 'Measurements',
|
|
||||||
target: 'lesion-tracker-panel',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
id: 'lesion-tracker-panel',
|
|
||||||
component: MeasurementComparisonTable,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
defaultContext: ['VIEWER'],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
* @returns Object
|
|
||||||
*/
|
|
||||||
getToolbarModule() {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
* @returns Object
|
|
||||||
*/
|
|
||||||
getCommandsModule() {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button, ButtonGroup, Icon, IconButton } from '@ohif/ui';
|
||||||
|
|
||||||
|
function ActionButtons() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<ButtonGroup onClick={() => alert('Export')}>
|
||||||
|
<Button
|
||||||
|
className="px-2 py-2 text-base text-white bg-black border-primary-main"
|
||||||
|
size="initial"
|
||||||
|
color="inherit"
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
className="px-2 text-white bg-black border-primary-main"
|
||||||
|
color="inherit"
|
||||||
|
size="initial"
|
||||||
|
>
|
||||||
|
<Icon name="arrow-down" />
|
||||||
|
</IconButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
<Button
|
||||||
|
className="px-2 py-2 ml-2 text-base text-white bg-black border border-primary-main"
|
||||||
|
variant="outlined"
|
||||||
|
size="initial"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => alert('Create Report')}
|
||||||
|
>
|
||||||
|
Create Report
|
||||||
|
</Button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionButtons;
|
||||||
@ -1,12 +1,8 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import { StudySummary, MeasurementTable } from '@ohif/ui';
|
||||||
MeasurementsPanel,
|
import { DicomMetadataStore } from '@ohif/core';
|
||||||
Button,
|
import ActionButtons from './ActionButtons';
|
||||||
ButtonGroup,
|
|
||||||
Icon,
|
|
||||||
IconButton,
|
|
||||||
} from '@ohif/ui';
|
|
||||||
import { useTrackedMeasurements } from '../../getContextModule';
|
import { useTrackedMeasurements } from '../../getContextModule';
|
||||||
|
|
||||||
function PanelMeasurementTableTracking({ servicesManager, commandsManager }) {
|
function PanelMeasurementTableTracking({ servicesManager, commandsManager }) {
|
||||||
@ -15,6 +11,11 @@ function PanelMeasurementTableTracking({ servicesManager, commandsManager }) {
|
|||||||
trackedMeasurements,
|
trackedMeasurements,
|
||||||
sendTrackedMeasurementsEvent,
|
sendTrackedMeasurementsEvent,
|
||||||
] = useTrackedMeasurements();
|
] = useTrackedMeasurements();
|
||||||
|
const [displayStudySummary, setDisplayStudySummary] = useState({
|
||||||
|
date: '', // '07-Sep-2010',
|
||||||
|
modality: '', // 'CT',
|
||||||
|
description: '', // 'CHEST/ABD/PELVIS W CONTRAST',
|
||||||
|
});
|
||||||
const [displayMeasurements, setDisplayMeasurements] = useState([]);
|
const [displayMeasurements, setDisplayMeasurements] = useState([]);
|
||||||
|
|
||||||
// TODO: initial measurements + initial tracked?
|
// TODO: initial measurements + initial tracked?
|
||||||
@ -24,8 +25,8 @@ function PanelMeasurementTableTracking({ servicesManager, commandsManager }) {
|
|||||||
// Initial?
|
// Initial?
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const measurements = MeasurementService.getMeasurements();
|
const measurements = MeasurementService.getMeasurements();
|
||||||
const mappedMeasurements = measurements.map(m =>
|
const mappedMeasurements = measurements.map((m, index) =>
|
||||||
_mapMeasurementToDisplay(m)
|
_mapMeasurementToDisplay(m, index)
|
||||||
);
|
);
|
||||||
setDisplayMeasurements(mappedMeasurements);
|
setDisplayMeasurements(mappedMeasurements);
|
||||||
// eslint-ignore-next-line
|
// eslint-ignore-next-line
|
||||||
@ -51,93 +52,101 @@ function PanelMeasurementTableTracking({ servicesManager, commandsManager }) {
|
|||||||
// return unsubscribe;
|
// return unsubscribe;
|
||||||
}, [MeasurementService, sendTrackedMeasurementsEvent]);
|
}, [MeasurementService, sendTrackedMeasurementsEvent]);
|
||||||
|
|
||||||
console.log('MeasurementTable rendering!!!!!!!!!!!!!');
|
|
||||||
|
|
||||||
const actionButtons = (
|
|
||||||
<React.Fragment>
|
|
||||||
<ButtonGroup onClick={() => alert('Export')}>
|
|
||||||
<Button
|
|
||||||
className="px-2 py-2 text-base text-white bg-black border-primary-main"
|
|
||||||
size="initial"
|
|
||||||
color="inherit"
|
|
||||||
>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
|
||||||
className="px-2 text-white bg-black border-primary-main"
|
|
||||||
color="inherit"
|
|
||||||
size="initial"
|
|
||||||
>
|
|
||||||
<Icon name="arrow-down" />
|
|
||||||
</IconButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
<Button
|
|
||||||
className="px-2 py-2 ml-2 text-base text-white bg-black border border-primary-main"
|
|
||||||
variant="outlined"
|
|
||||||
size="initial"
|
|
||||||
color="inherit"
|
|
||||||
onClick={() => alert('Create Report')}
|
|
||||||
>
|
|
||||||
Create Report
|
|
||||||
</Button>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
const descriptionData = {
|
|
||||||
date: '07-Sep-2010',
|
|
||||||
modality: 'CT',
|
|
||||||
description: 'CHEST/ABD/PELVIS W CONTRAST',
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeMeasurementItem = 0;
|
const activeMeasurementItem = 0;
|
||||||
|
|
||||||
const measurementTableData = {
|
|
||||||
title: 'Measurements',
|
|
||||||
amount: displayMeasurements.length,
|
|
||||||
data: displayMeasurements,
|
|
||||||
// new Array(10).fill({}).map((el, i) => ({
|
|
||||||
// id: i + 1,
|
|
||||||
// label: 'Label short description',
|
|
||||||
// displayText: '24.0 x 24.0 mm (S:4, I:22)',
|
|
||||||
// isActive: activeMeasurementItem === i + 1,
|
|
||||||
// })),
|
|
||||||
// onClick: id => setActiveMeasurementItem(s => (s === id ? null : id)),
|
|
||||||
onClick: () => {},
|
|
||||||
onEdit: id => alert(`Edit: ${id}`),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MeasurementsPanel
|
<>
|
||||||
descriptionData={descriptionData}
|
<div className="overflow-x-hidden overflow-y-auto invisible-scrollbar">
|
||||||
measurementTableData={measurementTableData}
|
<StudySummary
|
||||||
actionButtons={actionButtons}
|
date={displayStudySummary.date}
|
||||||
/>
|
modality={displayStudySummary.modality}
|
||||||
|
description={displayStudySummary.description}
|
||||||
|
/>
|
||||||
|
<MeasurementTable
|
||||||
|
title="Measurements"
|
||||||
|
amount={displayMeasurements.length}
|
||||||
|
data={displayMeasurements}
|
||||||
|
onClick={() => {}}
|
||||||
|
onEdit={id => alert(`Edit: ${id}`)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center p-4">
|
||||||
|
<ActionButtons />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelMeasurementTableTracking.propTypes = {};
|
PanelMeasurementTableTracking.propTypes = {};
|
||||||
|
|
||||||
// 'id',
|
// TODO: This could be a MeasurementService mapper
|
||||||
// 'SOPInstanceUID',
|
function _mapMeasurementToDisplay(measurement, index) {
|
||||||
// 'FrameOfReferenceUID',
|
const {
|
||||||
// 'referenceStudyUID',
|
|
||||||
// 'referenceSeriesUID',
|
|
||||||
// 'label',
|
|
||||||
// 'description',
|
|
||||||
// 'type',
|
|
||||||
// 'unit',
|
|
||||||
// 'area', // TODO: Add concept names instead (descriptor)
|
|
||||||
// 'points',
|
|
||||||
// 'source',
|
|
||||||
function _mapMeasurementToDisplay(measurement) {
|
|
||||||
const { id, label, description: displayText } = measurement;
|
|
||||||
return {
|
|
||||||
id,
|
id,
|
||||||
label, // 'Label short description',
|
label,
|
||||||
displayText, // '24.0 x 24.0 mm (S:4, I:22)',
|
description,
|
||||||
|
// Reference IDs
|
||||||
|
referenceStudyUID,
|
||||||
|
referenceSeriesUID,
|
||||||
|
SOPInstanceUID,
|
||||||
|
} = measurement;
|
||||||
|
const instance = DicomMetadataStore.getInstance(
|
||||||
|
referenceStudyUID,
|
||||||
|
referenceSeriesUID,
|
||||||
|
SOPInstanceUID
|
||||||
|
);
|
||||||
|
const { PixelSpacing, SeriesNumber, InstanceNumber } = instance;
|
||||||
|
|
||||||
|
console.log('mapping....', measurement);
|
||||||
|
console.log(instance);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: index + 1,
|
||||||
|
label: '(empty)', // 'Label short description',
|
||||||
|
displayText: _getDisplayText(
|
||||||
|
measurement.points,
|
||||||
|
PixelSpacing,
|
||||||
|
SeriesNumber,
|
||||||
|
InstanceNumber
|
||||||
|
),
|
||||||
// TODO: handle one layer down
|
// TODO: handle one layer down
|
||||||
isActive: false, // activeMeasurementItem === i + 1,
|
isActive: false, // activeMeasurementItem === i + 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} points
|
||||||
|
* @param {*} pixelSpacing
|
||||||
|
*/
|
||||||
|
function _getDisplayText(points, pixelSpacing, seriesNumber, instanceNumber) {
|
||||||
|
// TODO: determination of shape influences text
|
||||||
|
// Length: 'xx.x unit (S:x, I:x)'
|
||||||
|
// Rectangle: 'xx.x x xx.x unit (S:x, I:x)',
|
||||||
|
// Ellipse?
|
||||||
|
// Bidirectional?
|
||||||
|
// Freehand?
|
||||||
|
|
||||||
|
const hasPixelSpacing =
|
||||||
|
pixelSpacing !== undefined &&
|
||||||
|
Array.isArray(pixelSpacing) &&
|
||||||
|
pixelSpacing.length === 2;
|
||||||
|
const [rowPixelSpacing, colPixelSpacing] = hasPixelSpacing
|
||||||
|
? pixelSpacing
|
||||||
|
: [1, 1];
|
||||||
|
const unit = hasPixelSpacing ? 'mm' : 'px';
|
||||||
|
|
||||||
|
const { x: x1, y: y1 } = points[0];
|
||||||
|
const { x: x2, y: y2 } = points[1];
|
||||||
|
const dx = x2 - x1 * colPixelSpacing;
|
||||||
|
const dy = y2 - y1 * rowPixelSpacing;
|
||||||
|
const length = _round(Math.sqrt(dx * dx + dy * dy), 1);
|
||||||
|
|
||||||
|
return `${length} ${unit} (S:${seriesNumber}, I:${instanceNumber})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _round(value, decimals) {
|
||||||
|
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
|
||||||
|
}
|
||||||
|
|
||||||
export default PanelMeasurementTableTracking;
|
export default PanelMeasurementTableTracking;
|
||||||
|
|||||||
@ -16,14 +16,12 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"cm": "npx git-cz",
|
"cm": "npx git-cz",
|
||||||
"build": "lerna run build:viewer --stream",
|
"build": "lerna run build:viewer --stream",
|
||||||
"build:lt": "lerna run build:viewer:lesion-tracker --stream",
|
|
||||||
"build:ci": "lerna run build:viewer:ci --stream",
|
"build:ci": "lerna run build:viewer:ci --stream",
|
||||||
"build:ui:deploy-preview": "lerna run build:ui:deploy-preview --stream",
|
"build:ui:deploy-preview": "lerna run build:ui:deploy-preview --stream",
|
||||||
"build:demo": "lerna run build:viewer:demo --stream",
|
"build:demo": "lerna run build:viewer:demo --stream",
|
||||||
"build:package": "lerna run build:viewer:package --stream",
|
"build:package": "lerna run build:viewer:package --stream",
|
||||||
"build:package-all": "lerna run build:package --parallel --stream",
|
"build:package-all": "lerna run build:package --parallel --stream",
|
||||||
"dev": "lerna run dev:viewer --stream",
|
"dev": "lerna run dev:viewer --stream",
|
||||||
"dev:lt": "lerna run dev:viewer:lesion-tracker --stream",
|
|
||||||
"dev:project": ".scripts/dev.sh",
|
"dev:project": ".scripts/dev.sh",
|
||||||
"dev:orthanc": "lerna run dev:orthanc --stream",
|
"dev:orthanc": "lerna run dev:orthanc --stream",
|
||||||
"orthanc:up": "docker-compose -f .docker/Nginx-Orthanc/docker-compose.yml up",
|
"orthanc:up": "docker-compose -f .docker/Nginx-Orthanc/docker-compose.yml up",
|
||||||
|
|||||||
@ -41,7 +41,6 @@ export {
|
|||||||
InputMultiSelect,
|
InputMultiSelect,
|
||||||
InputText,
|
InputText,
|
||||||
Label,
|
Label,
|
||||||
MeasurementsPanel,
|
|
||||||
MeasurementTable,
|
MeasurementTable,
|
||||||
Modal,
|
Modal,
|
||||||
NavBar,
|
NavBar,
|
||||||
@ -56,6 +55,7 @@ export {
|
|||||||
StudyListPagination,
|
StudyListPagination,
|
||||||
StudyListTable,
|
StudyListTable,
|
||||||
StudyListTableRow,
|
StudyListTableRow,
|
||||||
|
StudySummary,
|
||||||
Svg,
|
Svg,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { MeasurementTable } from '@ohif/ui';
|
|
||||||
|
|
||||||
const MeasurementsPanel = ({
|
|
||||||
descriptionData,
|
|
||||||
measurementTableData,
|
|
||||||
actionButtons,
|
|
||||||
}) => {
|
|
||||||
const { date, modality, description } = descriptionData;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="overflow-y-auto overflow-x-hidden invisible-scrollbar">
|
|
||||||
<div className="p-2">
|
|
||||||
<div className="leading-none">
|
|
||||||
<span className="text-white text-base mr-2">{date}</span>
|
|
||||||
<span className="px-1 text-black bg-common-bright text-base rounded-sm font-bold">
|
|
||||||
{modality}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="leading-none">
|
|
||||||
<span className="text-base text-primary-light">{description}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<MeasurementTable
|
|
||||||
data={measurementTableData.data}
|
|
||||||
title={measurementTableData.title}
|
|
||||||
amount={measurementTableData.amount}
|
|
||||||
onClick={measurementTableData.onClick}
|
|
||||||
onEdit={measurementTableData.onEdit}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 flex justify-center">{actionButtons}</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
MeasurementsPanel.defaultProps = {
|
|
||||||
actionButtons: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
MeasurementsPanel.propTypes = {
|
|
||||||
descriptionData: PropTypes.shape({
|
|
||||||
date: PropTypes.string,
|
|
||||||
modality: PropTypes.string,
|
|
||||||
description: PropTypes.string,
|
|
||||||
}).isRequired,
|
|
||||||
measurementTableData: PropTypes.shape({
|
|
||||||
title: PropTypes.string,
|
|
||||||
amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
data: PropTypes.arrayOf(
|
|
||||||
PropTypes.shape({
|
|
||||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
label: PropTypes.string,
|
|
||||||
displayText: PropTypes.string,
|
|
||||||
isActive: PropTypes.bool,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
onEdit: PropTypes.func,
|
|
||||||
}).isRequired,
|
|
||||||
actionButtons: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MeasurementsPanel;
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
---
|
|
||||||
name: Measurements Panel
|
|
||||||
menu: Data Display
|
|
||||||
route: components/measurementsPanel
|
|
||||||
---
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { Playground, Props } from 'docz';
|
|
||||||
import {
|
|
||||||
SidePanel,
|
|
||||||
MeasurementsPanel,
|
|
||||||
ButtonGroup,
|
|
||||||
Button,
|
|
||||||
IconButton,
|
|
||||||
Icon,
|
|
||||||
} from '@ohif/ui';
|
|
||||||
|
|
||||||
# Measurements Panel
|
|
||||||
|
|
||||||
## Import
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import { MeasurementsPanel } from '@ohif/ui';
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic usage
|
|
||||||
|
|
||||||
<Playground>
|
|
||||||
{() => {
|
|
||||||
const [activeMeasurementItem, setActiveMeasurementItem] = useState(null);
|
|
||||||
const descriptionData = {
|
|
||||||
date: '07-Sep-2010',
|
|
||||||
modality: 'CT',
|
|
||||||
description: 'CHEST/ABD/PELVIS W CONTRAST',
|
|
||||||
};
|
|
||||||
const measurementTableData = {
|
|
||||||
title: 'Measurements',
|
|
||||||
amount: 10,
|
|
||||||
data: new Array(10).fill({}).map((el, i) => ({
|
|
||||||
id: i + 1,
|
|
||||||
label: 'Label short description',
|
|
||||||
displayText: '24.0 x 24.0 mm (S:4, I:22)',
|
|
||||||
isActive: activeMeasurementItem === i + 1,
|
|
||||||
})),
|
|
||||||
onClick: (id) => setActiveMeasurementItem((s) => (s === id ? null : id)),
|
|
||||||
onEdit: (id) => alert(`Edit: ${id}`),
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="flex flex-row flex-no-wrap flex-1 items-stretch overflow-hidden w-full h-screen">
|
|
||||||
<div className="flex flex-1 h-100 overflow-hidden bg-primary-main items-center justify-center text-white">
|
|
||||||
CONTENT
|
|
||||||
</div>
|
|
||||||
<SidePanel
|
|
||||||
side="right"
|
|
||||||
iconName="list-bullets"
|
|
||||||
iconLabel="Measure"
|
|
||||||
componentLabel="Measurements"
|
|
||||||
defaultIsOpen={true}
|
|
||||||
>
|
|
||||||
<MeasurementsPanel
|
|
||||||
descriptionData={descriptionData}
|
|
||||||
measurementTableData={measurementTableData}
|
|
||||||
actionButtons={
|
|
||||||
<>
|
|
||||||
<ButtonGroup onClick={() => alert('Export')}>
|
|
||||||
<Button
|
|
||||||
className="text-white border-primary-main bg-black text-base py-2 px-2"
|
|
||||||
size="initial"
|
|
||||||
color="inherit"
|
|
||||||
>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
|
||||||
className="bg-black border-primary-main px-2 text-white px-2"
|
|
||||||
color="inherit"
|
|
||||||
size="initial"
|
|
||||||
>
|
|
||||||
<Icon name="arrow-down" />
|
|
||||||
</IconButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
<Button
|
|
||||||
className="text-white border border-primary-main bg-black text-base py-2 px-2 ml-2"
|
|
||||||
variant="outlined"
|
|
||||||
size="initial"
|
|
||||||
color="inherit"
|
|
||||||
onClick={() => alert('Create Report')}
|
|
||||||
>
|
|
||||||
Create Report
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SidePanel>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Playground>
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
<Props of={MeasurementsPanel} />
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
import MeasurementsPanel from './MeasurementsPanel';
|
|
||||||
export default MeasurementsPanel;
|
|
||||||
26
platform/ui/src/components/StudySummary/StudySummary.jsx
Normal file
26
platform/ui/src/components/StudySummary/StudySummary.jsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const StudySummary = ({ date, modality, description }) => {
|
||||||
|
return (
|
||||||
|
<div className="p-2">
|
||||||
|
<div className="leading-none">
|
||||||
|
<span className="mr-2 text-base text-white">{date}</span>
|
||||||
|
<span className="px-1 text-base font-bold text-black rounded-sm bg-common-bright">
|
||||||
|
{modality}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="leading-none">
|
||||||
|
<span className="text-base text-primary-light">{description}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
StudySummary.propTypes = {
|
||||||
|
date: PropTypes.string.isRequired,
|
||||||
|
modality: PropTypes.string.isRequired,
|
||||||
|
description: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StudySummary;
|
||||||
3
platform/ui/src/components/StudySummary/index.js
Normal file
3
platform/ui/src/components/StudySummary/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import StudySummary from './StudySummary.jsx';
|
||||||
|
|
||||||
|
export default StudySummary;
|
||||||
@ -12,7 +12,6 @@ import InputLabelWrapper from './InputLabelWrapper';
|
|||||||
import InputMultiSelect from './InputMultiSelect';
|
import InputMultiSelect from './InputMultiSelect';
|
||||||
import InputText from './InputText';
|
import InputText from './InputText';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
import MeasurementsPanel from './MeasurementsPanel';
|
|
||||||
import MeasurementTable from './MeasurementTable';
|
import MeasurementTable from './MeasurementTable';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import NavBar from './NavBar';
|
import NavBar from './NavBar';
|
||||||
@ -21,12 +20,13 @@ import Select from './Select';
|
|||||||
import SegmentationTable from './SegmentationTable';
|
import SegmentationTable from './SegmentationTable';
|
||||||
import SidePanel from './SidePanel';
|
import SidePanel from './SidePanel';
|
||||||
import StudyBrowser from './StudyBrowser';
|
import StudyBrowser from './StudyBrowser';
|
||||||
|
import StudyItem from './StudyItem';
|
||||||
import StudyListExpandedRow from './StudyListExpandedRow';
|
import StudyListExpandedRow from './StudyListExpandedRow';
|
||||||
|
import StudyListFilter from './StudyListFilter';
|
||||||
import StudyListPagination from './StudyListPagination';
|
import StudyListPagination from './StudyListPagination';
|
||||||
import { StudyListTable, StudyListTableRow } from './StudyListTable';
|
import { StudyListTable, StudyListTableRow } from './StudyListTable';
|
||||||
|
import StudySummary from './StudySummary';
|
||||||
import Svg from './Svg';
|
import Svg from './Svg';
|
||||||
import StudyItem from './StudyItem';
|
|
||||||
import StudyListFilter from './StudyListFilter';
|
|
||||||
import Table from './Table';
|
import Table from './Table';
|
||||||
import TableBody from './TableBody';
|
import TableBody from './TableBody';
|
||||||
import TableCell from './TableCell';
|
import TableCell from './TableCell';
|
||||||
@ -61,7 +61,6 @@ export {
|
|||||||
InputMultiSelect,
|
InputMultiSelect,
|
||||||
InputText,
|
InputText,
|
||||||
Label,
|
Label,
|
||||||
MeasurementsPanel,
|
|
||||||
MeasurementTable,
|
MeasurementTable,
|
||||||
Modal,
|
Modal,
|
||||||
NavBar,
|
NavBar,
|
||||||
@ -76,6 +75,7 @@ export {
|
|||||||
StudyListPagination,
|
StudyListPagination,
|
||||||
StudyListTable,
|
StudyListTable,
|
||||||
StudyListTableRow,
|
StudyListTableRow,
|
||||||
|
StudySummary,
|
||||||
Svg,
|
Svg,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
NavBar,
|
NavBar,
|
||||||
SidePanel,
|
SidePanel,
|
||||||
Svg,
|
Svg,
|
||||||
MeasurementsPanel,
|
|
||||||
SegmentationTable,
|
SegmentationTable,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
Button,
|
Button,
|
||||||
@ -125,10 +124,22 @@ import { tabs } from './studyBrowserMockData';
|
|||||||
label: 'Measurements',
|
label: 'Measurements',
|
||||||
name: 'measurements',
|
name: 'measurements',
|
||||||
content: (
|
content: (
|
||||||
<MeasurementsPanel
|
<>
|
||||||
descriptionData={descriptionData}
|
<div className="overflow-x-hidden overflow-y-auto invisible-scrollbar">
|
||||||
measurementTableData={measurementTableData}
|
<StudySummary
|
||||||
actionButtons={
|
date={descriptionData.date}
|
||||||
|
modality={descriptionData.modality}
|
||||||
|
description={descriptionData.description}
|
||||||
|
/>
|
||||||
|
<MeasurementTable
|
||||||
|
title="Measurements"
|
||||||
|
amount={measurementTableData.data.length}
|
||||||
|
data={measurementTableData.data}
|
||||||
|
onClick={() => {}}
|
||||||
|
onEdit={id => alert(`Edit: ${id}`)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center p-4">
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ButtonGroup onClick={() => alert('Export')}>
|
<ButtonGroup onClick={() => alert('Export')}>
|
||||||
<Button
|
<Button
|
||||||
@ -156,8 +167,8 @@ import { tabs } from './studyBrowserMockData';
|
|||||||
Create Report
|
Create Report
|
||||||
</Button>
|
</Button>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
</div>
|
||||||
/>
|
</>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -19,14 +19,12 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build:package": "cross-env NODE_ENV=production node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.commonjs.js --progress",
|
"build:package": "cross-env NODE_ENV=production node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.commonjs.js --progress",
|
||||||
"build:viewer": "cross-env NODE_ENV=production node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.pwa.js --progress",
|
"build:viewer": "cross-env NODE_ENV=production node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.pwa.js --progress",
|
||||||
"build:viewer:lesion-tracker": "cross-env ENTRY_TARGET=index-lesion-tracker.js NODE_ENV=production node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.pwa.js --progress",
|
|
||||||
"build:viewer:ci": "cross-env NODE_ENV=production PUBLIC_URL=/pwa/ APP_CONFIG=config/netlify.js QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.pwa.js",
|
"build:viewer:ci": "cross-env NODE_ENV=production PUBLIC_URL=/pwa/ APP_CONFIG=config/netlify.js QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --config .webpack/webpack.pwa.js",
|
||||||
"build:viewer:demo": "cross-env NODE_ENV=production APP_CONFIG=config/demo.js HTML_TEMPLATE=rollbar.html QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
|
"build:viewer:demo": "cross-env NODE_ENV=production APP_CONFIG=config/demo.js HTML_TEMPLATE=rollbar.html QUICK_BUILD=true node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
|
||||||
"build:viewer:package": "yarn run build:package",
|
"build:viewer:package": "yarn run build:package",
|
||||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --config .webpack/webpack.pwa.js --watch",
|
"dev": "cross-env NODE_ENV=development webpack-dev-server --config .webpack/webpack.pwa.js --watch",
|
||||||
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js --watch",
|
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js --watch",
|
||||||
"dev:viewer": "yarn run dev",
|
"dev:viewer": "yarn run dev",
|
||||||
"dev:viewer:lesion-tracker": "cross-env ENTRY_TARGET=index-lesion-tracker.js NODE_ENV=development webpack-dev-server --config .webpack/webpack.pwa.js --watch",
|
|
||||||
"start": "yarn run dev",
|
"start": "yarn run dev",
|
||||||
"test:e2e": "cypress open",
|
"test:e2e": "cypress open",
|
||||||
"test:e2e:ci": "percy exec -- cypress run --config video=false --record --browser chrome --spec 'cypress/integration/visual-regression/**/*'",
|
"test:e2e:ci": "percy exec -- cypress run --config video=false --record --browser chrome --spec 'cypress/integration/visual-regression/**/*'",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user