feat: Viewport API + Documentation Page
This commit is contained in:
parent
5f9535a74b
commit
12d1ff6bea
@ -1,38 +1,62 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { ViewportActionBar, Notification, Button } from '@ohif/ui';
|
import { ViewportActionBar, Notification, Button } from '@ohif/ui';
|
||||||
|
|
||||||
const Viewport = ({
|
const Viewport = ({ viewportIndex, onSeriesChange, studyData, children }) => {
|
||||||
viewportIndex,
|
|
||||||
onSeriesChange,
|
|
||||||
studyData,
|
|
||||||
isTracked,
|
|
||||||
studyDate,
|
|
||||||
modality,
|
|
||||||
currentSeries,
|
|
||||||
patientInformation,
|
|
||||||
activeViewportIndex,
|
|
||||||
children,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full relative">
|
||||||
<ViewportActionBar isTracked modality={modality} />
|
<div className="absolute top-0 left-0 w-full">
|
||||||
|
<ViewportActionBar
|
||||||
|
onSeriesChange={onSeriesChange}
|
||||||
|
studyData={studyData}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* TODO: NOTIFICATION API DEFINITION - OHIF-112 */}
|
{/* TODO: NOTIFICATION API DEFINITION - OHIF-112 */}
|
||||||
<Notification
|
<Notification
|
||||||
text="Track all measurement for this series?"
|
text="Track all measurement for this series?"
|
||||||
type="info"
|
type="info"
|
||||||
actionButtons={
|
actionButtons={
|
||||||
<div>
|
<div>
|
||||||
<Button>No</Button>
|
<Button>No</Button>
|
||||||
<Button className="ml-2">No, do not ask again</Button>
|
<Button className="ml-2">No, do not ask again</Button>
|
||||||
<Button className="ml-2" color="primary">
|
<Button className="ml-2" color="primary">
|
||||||
Yes
|
Yes
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* STUDY IMAGE */}
|
||||||
|
<div className="w-full h-full" id={`viewport-${viewportIndex}`}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Viewport.propTypes = {
|
||||||
|
viewportIndex: PropTypes.number.isRequired,
|
||||||
|
onSeriesChange: PropTypes.func.isRequired,
|
||||||
|
studyData: PropTypes.shape({
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
isTracked: PropTypes.bool.isRequired,
|
||||||
|
isLocked: PropTypes.bool.isRequired,
|
||||||
|
studyDate: PropTypes.string.isRequired,
|
||||||
|
currentSeries: PropTypes.number.isRequired,
|
||||||
|
seriesDescription: PropTypes.string.isRequired,
|
||||||
|
modality: PropTypes.string.isRequired,
|
||||||
|
patientInformation: PropTypes.shape({
|
||||||
|
patientName: PropTypes.string.isRequired,
|
||||||
|
patientSex: PropTypes.string.isRequired,
|
||||||
|
patientAge: PropTypes.string.isRequired,
|
||||||
|
MRN: PropTypes.string.isRequired,
|
||||||
|
thickness: PropTypes.string.isRequired,
|
||||||
|
spacing: PropTypes.string.isRequired,
|
||||||
|
scanner: PropTypes.string.isRequired,
|
||||||
|
}),
|
||||||
|
}).isRequired,
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default Viewport;
|
export default Viewport;
|
||||||
|
|||||||
@ -10,7 +10,41 @@ const classes = {
|
|||||||
row: 'flex flex-col ml-4',
|
row: 'flex flex-col ml-4',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ViewportActionBar = ({ isTracked, isLocked, modality }) => {
|
// isTracked: true,
|
||||||
|
// studyDate: '07-Sep-2010',
|
||||||
|
// currentSeries: 1,
|
||||||
|
// modality: 'CT',
|
||||||
|
// patientInformation: {
|
||||||
|
// patientName: 'Smith, Jane',
|
||||||
|
// patientSex: 'F',
|
||||||
|
// patientAge: '59',
|
||||||
|
// MRN: '10000001',
|
||||||
|
// thickness: '5.0mm',
|
||||||
|
// spacing: '1.25mm',
|
||||||
|
// scanner: 'Aquilion',
|
||||||
|
|
||||||
|
const ViewportActionBar = ({ studyData, onSeriesChange }) => {
|
||||||
|
const {
|
||||||
|
label,
|
||||||
|
isTracked,
|
||||||
|
isLocked,
|
||||||
|
modality,
|
||||||
|
studyDate,
|
||||||
|
currentSeries,
|
||||||
|
seriesDescription,
|
||||||
|
patientInformation,
|
||||||
|
} = studyData;
|
||||||
|
|
||||||
|
const {
|
||||||
|
patientName,
|
||||||
|
patientSex,
|
||||||
|
patientAge,
|
||||||
|
MRN,
|
||||||
|
thickness,
|
||||||
|
spacing,
|
||||||
|
scanner,
|
||||||
|
} = patientInformation;
|
||||||
|
|
||||||
const renderIconStatus = () => {
|
const renderIconStatus = () => {
|
||||||
if (modality === 'SR') {
|
if (modality === 'SR') {
|
||||||
return (
|
return (
|
||||||
@ -73,28 +107,36 @@ const ViewportActionBar = ({ isTracked, isLocked, modality }) => {
|
|||||||
<div className="flex flex-grow">
|
<div className="flex flex-grow">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{renderIconStatus()}
|
{renderIconStatus()}
|
||||||
<span className="text-large text-white ml-2">A</span>
|
<span className="text-large text-white ml-2">{label}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-start ml-4">
|
<div className="flex flex-col justify-start ml-4">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<span className="text-base text-white">07-Sep-2010</span>
|
<span className="text-base text-white">{studyDate}</span>
|
||||||
<span className="border-l border-primary-light ml-2 pl-2 text-base text-primary-light">
|
<span className="border-l border-primary-light ml-2 pl-2 text-base text-primary-light">
|
||||||
S: 1
|
S: {currentSeries}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<span className="text-base text-primary-light">
|
<p className="text-base truncate max-w-sm text-primary-light">
|
||||||
Series description lorem ipsum dolor sit...
|
{seriesDescription}
|
||||||
</span>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2">
|
<div className="ml-2">
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button size="initial" className="py-1 px-2">
|
<Button
|
||||||
|
size="initial"
|
||||||
|
className="py-1 px-2"
|
||||||
|
onClick={() => onSeriesChange('left')}
|
||||||
|
>
|
||||||
<Icon name="chevron-left" className="text-white w-4" />
|
<Icon name="chevron-left" className="text-white w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="initial" className="py-1 px-2">
|
<Button
|
||||||
|
size="initial"
|
||||||
|
className="py-1 px-2"
|
||||||
|
onClick={() => onSeriesChange('right')}
|
||||||
|
>
|
||||||
<Icon name="chevron-right" className="text-white w-4" />
|
<Icon name="chevron-right" className="text-white w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
@ -120,22 +162,24 @@ const ViewportActionBar = ({ isTracked, isLocked, modality }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col ml-2">
|
<div className="flex flex-col ml-2">
|
||||||
<span className="text-base font-bold text-white">
|
<span className="text-base font-bold text-white">
|
||||||
Smith, Jane
|
{patientName}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex mt-4 pb-4 mb-4 border-b border-secondary-main">
|
<div className="flex mt-4 pb-4 mb-4 border-b border-secondary-main">
|
||||||
<div className={classnames(classes.firstRow)}>
|
<div className={classnames(classes.firstRow)}>
|
||||||
<span className={classnames(classes.infoHeader)}>Sex</span>
|
<span className={classnames(classes.infoHeader)}>Sex</span>
|
||||||
<span className={classnames(classes.infoText)}>F</span>
|
<span className={classnames(classes.infoText)}>
|
||||||
|
{patientSex}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
<span className={classnames(classes.infoHeader)}>Age</span>
|
<span className={classnames(classes.infoHeader)}>Age</span>
|
||||||
<span className={classnames(classes.infoText)}>59</span>
|
<span className={classnames(classes.infoText)}>
|
||||||
|
{patientAge}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
<span className={classnames(classes.infoHeader)}>MRN</span>
|
<span className={classnames(classes.infoHeader)}>MRN</span>
|
||||||
<span className={classnames(classes.infoText)}>
|
<span className={classnames(classes.infoText)}>{MRN}</span>
|
||||||
10000001
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
@ -143,14 +187,16 @@ const ViewportActionBar = ({ isTracked, isLocked, modality }) => {
|
|||||||
<span className={classnames(classes.infoHeader)}>
|
<span className={classnames(classes.infoHeader)}>
|
||||||
Thickness
|
Thickness
|
||||||
</span>
|
</span>
|
||||||
<span className={classnames(classes.infoText)}>5.00mm</span>
|
<span className={classnames(classes.infoText)}>
|
||||||
|
{thickness}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
<span className={classnames(classes.infoHeader)}>
|
<span className={classnames(classes.infoHeader)}>
|
||||||
Spacing
|
Spacing
|
||||||
</span>
|
</span>
|
||||||
<span className={classnames(classes.infoText)}>
|
<span className={classnames(classes.infoText)}>
|
||||||
1.25 mm
|
{spacing}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
@ -158,7 +204,7 @@ const ViewportActionBar = ({ isTracked, isLocked, modality }) => {
|
|||||||
Scanner
|
Scanner
|
||||||
</span>
|
</span>
|
||||||
<span className={classnames(classes.infoText)}>
|
<span className={classnames(classes.infoText)}>
|
||||||
Aquilion
|
{scanner}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -181,14 +227,26 @@ const ViewportActionBar = ({ isTracked, isLocked, modality }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ViewportActionBar.defaultProps = {
|
|
||||||
isLocked: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
ViewportActionBar.propTypes = {
|
ViewportActionBar.propTypes = {
|
||||||
isTracked: PropTypes.bool.isRequired,
|
onSeriesChange: PropTypes.func.isRequired,
|
||||||
isLocked: PropTypes.bool,
|
studyData: PropTypes.shape({
|
||||||
modality: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
|
isTracked: PropTypes.bool.isRequired,
|
||||||
|
isLocked: PropTypes.bool.isRequired,
|
||||||
|
studyDate: PropTypes.string.isRequired,
|
||||||
|
currentSeries: PropTypes.number.isRequired,
|
||||||
|
seriesDescription: PropTypes.string.isRequired,
|
||||||
|
modality: PropTypes.string.isRequired,
|
||||||
|
patientInformation: PropTypes.shape({
|
||||||
|
patientName: PropTypes.string.isRequired,
|
||||||
|
patientSex: PropTypes.string.isRequired,
|
||||||
|
patientAge: PropTypes.string.isRequired,
|
||||||
|
MRN: PropTypes.string.isRequired,
|
||||||
|
thickness: PropTypes.string.isRequired,
|
||||||
|
spacing: PropTypes.string.isRequired,
|
||||||
|
scanner: PropTypes.string.isRequired,
|
||||||
|
}),
|
||||||
|
}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ViewportActionBar;
|
export default ViewportActionBar;
|
||||||
|
|||||||
@ -21,25 +21,109 @@ import { ViewportActionBar } from '@ohif/ui';
|
|||||||
|
|
||||||
<Playground>
|
<Playground>
|
||||||
<div className="p-4 h-64">
|
<div className="p-4 h-64">
|
||||||
<ViewportActionBar isTracked={true} modality="CT" />
|
<ViewportActionBar
|
||||||
|
onSeriesChange={(direction) => alert(`Series ${direction}`)}
|
||||||
|
studyData={{
|
||||||
|
label: 'A',
|
||||||
|
isTracked: true,
|
||||||
|
isLocked: false,
|
||||||
|
studyDate: '07-Sep-2010',
|
||||||
|
currentSeries: 1,
|
||||||
|
seriesDescription:
|
||||||
|
'Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ',
|
||||||
|
modality: 'CT',
|
||||||
|
patientInformation: {
|
||||||
|
patientName: 'Smith, Jane',
|
||||||
|
patientSex: 'F',
|
||||||
|
patientAge: '59',
|
||||||
|
MRN: '10000001',
|
||||||
|
thickness: '2.0mm',
|
||||||
|
spacing: '1.25mm',
|
||||||
|
scanner: 'Aquilion',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Playground>
|
</Playground>
|
||||||
|
|
||||||
<Playground>
|
<Playground>
|
||||||
<div className="p-4 h-64">
|
<div className="p-4 h-64">
|
||||||
<ViewportActionBar isTracked={false} modality="CT" />
|
<ViewportActionBar
|
||||||
|
onSeriesChange={(direction) => alert(`Series ${direction}`)}
|
||||||
|
studyData={{
|
||||||
|
label: 'A',
|
||||||
|
isTracked: false,
|
||||||
|
isLocked: true,
|
||||||
|
studyDate: '07-Sep-2010',
|
||||||
|
currentSeries: 1,
|
||||||
|
seriesDescription:
|
||||||
|
'Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ',
|
||||||
|
modality: 'CT',
|
||||||
|
patientInformation: {
|
||||||
|
patientName: 'Smith, Jane',
|
||||||
|
patientSex: 'F',
|
||||||
|
patientAge: '59',
|
||||||
|
MRN: '10000001',
|
||||||
|
thickness: '2.0mm',
|
||||||
|
spacing: '1.25mm',
|
||||||
|
scanner: 'Aquilion',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Playground>
|
</Playground>
|
||||||
|
|
||||||
<Playground>
|
<Playground>
|
||||||
<div className="p-4 h-64">
|
<div className="p-4 h-64">
|
||||||
<ViewportActionBar isTracked={false} isLocked={false} modality="SR" />
|
<ViewportActionBar
|
||||||
|
onSeriesChange={(direction) => alert(`Series ${direction}`)}
|
||||||
|
studyData={{
|
||||||
|
label: 'A',
|
||||||
|
isTracked: false,
|
||||||
|
isLocked: false,
|
||||||
|
studyDate: '07-Sep-2010',
|
||||||
|
currentSeries: 1,
|
||||||
|
seriesDescription:
|
||||||
|
'Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ',
|
||||||
|
modality: 'SR',
|
||||||
|
patientInformation: {
|
||||||
|
patientName: 'Smith, Jane',
|
||||||
|
patientSex: 'F',
|
||||||
|
patientAge: '59',
|
||||||
|
MRN: '10000001',
|
||||||
|
thickness: '2.0mm',
|
||||||
|
spacing: '1.25mm',
|
||||||
|
scanner: 'Aquilion',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Playground>
|
</Playground>
|
||||||
|
|
||||||
<Playground>
|
<Playground>
|
||||||
<div className="p-4 h-64">
|
<div className="p-4 h-64">
|
||||||
<ViewportActionBar isTracked={false} isLocked={true} modality="SR" />
|
<ViewportActionBar
|
||||||
|
onSeriesChange={(direction) => alert(`Series ${direction}`)}
|
||||||
|
studyData={{
|
||||||
|
label: 'A',
|
||||||
|
isTracked: false,
|
||||||
|
isLocked: true,
|
||||||
|
studyDate: '07-Sep-2010',
|
||||||
|
currentSeries: 1,
|
||||||
|
seriesDescription:
|
||||||
|
'Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ',
|
||||||
|
modality: 'SR',
|
||||||
|
patientInformation: {
|
||||||
|
patientName: 'Smith, Jane',
|
||||||
|
patientSex: 'F',
|
||||||
|
patientAge: '59',
|
||||||
|
MRN: '10000001',
|
||||||
|
thickness: '2.0mm',
|
||||||
|
spacing: '1.25mm',
|
||||||
|
scanner: 'Aquilion',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Playground>
|
</Playground>
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,9 @@ const ViewportGrid = ({ rows, cols, viewportContents }) => {
|
|||||||
'border border-secondary-light': !isActive,
|
'border border-secondary-light': !isActive,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
setActiveViewportIndex(index);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{viewportContent}
|
{viewportContent}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -80,11 +80,15 @@ import { tabs } from './studyBrowserMockData';
|
|||||||
viewportContents={[
|
viewportContents={[
|
||||||
<Viewport
|
<Viewport
|
||||||
viewportIndex={0}
|
viewportIndex={0}
|
||||||
onSeriesChange={() => {}}
|
onSeriesChange={(direction) => alert(`Series ${direction}`)}
|
||||||
studyData={{
|
studyData={{
|
||||||
isTracked: false,
|
label: 'A',
|
||||||
studyDate: '07-Sep-2010',
|
isTracked: true,
|
||||||
|
isLocked: false,
|
||||||
|
studyDate: '07-Sep-2011',
|
||||||
currentSeries: 1,
|
currentSeries: 1,
|
||||||
|
seriesDescription:
|
||||||
|
'Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ',
|
||||||
modality: 'CT',
|
modality: 'CT',
|
||||||
patientInformation: {
|
patientInformation: {
|
||||||
patientName: 'Smith, Jane',
|
patientName: 'Smith, Jane',
|
||||||
@ -96,27 +100,38 @@ import { tabs } from './studyBrowserMockData';
|
|||||||
scanner: 'Aquilion',
|
scanner: 'Aquilion',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
isTracked={false}
|
>
|
||||||
activeViewportIndex={activeViewportIndex}
|
<div className="flex justify-center items-center h-full">
|
||||||
/>,
|
CONTENT
|
||||||
|
</div>
|
||||||
|
</Viewport>,
|
||||||
<Viewport
|
<Viewport
|
||||||
viewportIndex={1}
|
viewportIndex={1}
|
||||||
onSeriesChange={() => {}}
|
onSeriesChange={(direction) => alert(`Series ${direction}`)}
|
||||||
studyData={{
|
studyData={{
|
||||||
|
label: 'A',
|
||||||
isTracked: false,
|
isTracked: false,
|
||||||
|
isLocked: true,
|
||||||
studyDate: '07-Sep-2010',
|
studyDate: '07-Sep-2010',
|
||||||
currentSeries: 1,
|
currentSeries: 2,
|
||||||
modality: 'CT',
|
seriesDescription:
|
||||||
|
'Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit Series description lorem ipsum dolor sit ',
|
||||||
|
modality: 'SR',
|
||||||
patientInformation: {
|
patientInformation: {
|
||||||
patientName: 'Smith, Jane',
|
patientName: 'Smith, Jane',
|
||||||
patientSex: 'F',
|
patientSex: 'F',
|
||||||
patientAge: '59',
|
patientAge: '59',
|
||||||
MRN: '10000001',
|
MRN: '10000001',
|
||||||
|
thickness: '2.0mm',
|
||||||
|
spacing: '1.25mm',
|
||||||
|
scanner: 'Aquilion',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
isTracked={false}
|
>
|
||||||
activeViewportIndex={activeViewportIndex}
|
<div className="flex justify-center items-center h-full">
|
||||||
/>,
|
CONTENT
|
||||||
|
</div>
|
||||||
|
</Viewport>,
|
||||||
]}
|
]}
|
||||||
setActiveViewportIndex={setActiveViewportIndex}
|
setActiveViewportIndex={setActiveViewportIndex}
|
||||||
activeViewportIndex={activeViewportIndex}
|
activeViewportIndex={activeViewportIndex}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
import { Toolbar } from '@ohif/ui';
|
import { Toolbar } from '@ohif/ui';
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user