Files
ohif-viewer/platform/ui/src/components/studyBrowser/Thumbnail.js
T

218 lines
5.8 KiB
JavaScript
Raw Normal View History

import React, { useState, useEffect } from 'react';
2019-10-30 16:53:58 -03:00
import PropTypes from 'prop-types';
import { useDrag } from 'react-dnd';
import ImageThumbnail from './ImageThumbnail';
import classNames from 'classnames';
import { Icon } from './../../elements/Icon';
import { Tooltip } from './../tooltip';
import { OverlayTrigger } from './../overlayTrigger';
2019-10-30 16:53:58 -03:00
import './Thumbnail.styl';
function ThumbnailFooter({
SeriesDescription,
SeriesNumber,
InstanceNumber,
2019-10-30 16:53:58 -03:00
numImageFrames,
hasWarnings
2019-10-30 16:53:58 -03:00
}) {
const [inconsistencyWarnings, inconsistencyWarningsSet] = useState([]);
useEffect(() => {
let unmounted = false
hasWarnings.then(response => {
if (!unmounted) {
inconsistencyWarningsSet(response)
}
})
return () => {
unmounted = true
}
}, [])
const infoOnly = !SeriesDescription;
2019-10-30 16:53:58 -03:00
const getInfo = (value, icon, className = '') => {
return (
<div className={classNames('item item-series', className)}>
<div className="icon">{icon}</div>
<div className="value">{value}</div>
</div>
);
};
const getWarningContent = (inconsistencyWarnings) => {
if (Array.isArray(inconsistencyWarnings)) {
const listedWarnings = inconsistencyWarnings.map((warn, index) => {
return <li key={index}>{warn}</li>;
});
return <ol>{listedWarnings}</ol>;
} else {
return <React.Fragment>{inconsistencyWarnings}</React.Fragment>;
}
};
const getWarningInfo = (SeriesNumber, inconsistencyWarnings) => {
return(
<React.Fragment>
{inconsistencyWarnings && inconsistencyWarnings.length != 0 ? (
<OverlayTrigger
key={SeriesNumber}
placement="left"
overlay={
<Tooltip
placement="left"
className="in tooltip-warning"
id="tooltip-left"
>
<div className="warningTitle">Series Inconsistencies</div>
<div className="warningContent">{getWarningContent(inconsistencyWarnings)}</div>
</Tooltip>
}
>
<div className={classNames('warning')}>
<span className="warning-icon">
<Icon name="exclamation-triangle" />
</span>
</div>
</OverlayTrigger>
) : (
<React.Fragment></React.Fragment>
)}
</React.Fragment>
);
};
2019-10-30 16:53:58 -03:00
const getSeriesInformation = (
SeriesNumber,
InstanceNumber,
numImageFrames,
inconsistencyWarnings
2019-10-30 16:53:58 -03:00
) => {
if (!SeriesNumber && !InstanceNumber && !numImageFrames) {
2019-10-30 16:53:58 -03:00
return;
}
const seriesInformation =
2019-10-30 16:53:58 -03:00
<div className="series-information">
{getInfo(SeriesNumber, 'S:')}
{getInfo(InstanceNumber, 'I:')}
2019-10-30 16:53:58 -03:00
{getInfo(numImageFrames, '', 'image-frames')}
{getWarningInfo(SeriesNumber, inconsistencyWarnings)}
2019-10-30 16:53:58 -03:00
</div>
return (seriesInformation);
2019-10-30 16:53:58 -03:00
};
return (
<div className={classNames('series-details', { 'info-only': infoOnly })}>
<div className="series-description">{SeriesDescription}</div>
{getSeriesInformation(SeriesNumber, InstanceNumber, numImageFrames, inconsistencyWarnings)}
2019-10-30 16:53:58 -03:00
</div>
);
}
function Thumbnail(props) {
const {
active,
altImageText,
error,
displaySetInstanceUID,
2019-10-30 16:53:58 -03:00
imageId,
imageSrc,
InstanceNumber,
2019-10-30 16:53:58 -03:00
numImageFrames,
SeriesDescription,
SeriesNumber,
hasWarnings,
2019-10-30 16:53:58 -03:00
stackPercentComplete,
StudyInstanceUID,
2019-10-30 16:53:58 -03:00
onClick,
onDoubleClick,
onMouseDown,
supportsDrag,
} = props;
const [collectedProps, drag, dragPreview] = useDrag({
// `droppedItem` in `dropTarget`
// The only data it will have access to
item: {
StudyInstanceUID,
displaySetInstanceUID,
2019-10-30 16:53:58 -03:00
type: 'thumbnail', // Has to match `dropTarget`'s type
},
canDrag: function(monitor) {
return supportsDrag;
},
});
const hasImage = imageSrc || imageId;
const hasAltText = altImageText !== undefined;
return (
<div
ref={drag}
className={classNames('thumbnail', { active: active })}
onClick={onClick}
onDoubleClick={onDoubleClick}
onMouseDown={onMouseDown}
>
{/* SHOW IMAGE */}
{hasImage && (
<ImageThumbnail
active={active}
2019-10-30 16:53:58 -03:00
imageSrc={imageSrc}
imageId={imageId}
error={error}
stackPercentComplete={stackPercentComplete}
/>
)}
{/* SHOW TEXT ALTERNATIVE */}
{!hasImage && hasAltText && (
<div className={'alt-image-text p-x-1'}>
<h1>{altImageText}</h1>
</div>
)}
{ThumbnailFooter(props)}
</div>
);
}
const noop = () => {};
Thumbnail.propTypes = {
supportsDrag: PropTypes.bool,
id: PropTypes.string.isRequired,
displaySetInstanceUID: PropTypes.string.isRequired,
StudyInstanceUID: PropTypes.string.isRequired,
2019-10-30 16:53:58 -03:00
imageSrc: PropTypes.string,
imageId: PropTypes.string,
error: PropTypes.bool,
active: PropTypes.bool,
stackPercentComplete: PropTypes.number,
/**
altImageText will be used when no imageId or imageSrc is provided.
It will be displayed inside the <div>. This is useful when it is difficult
2019-10-30 16:53:58 -03:00
to make a preview for a type of DICOM series (e.g. DICOM-SR)
*/
altImageText: PropTypes.string,
SeriesDescription: PropTypes.string,
SeriesNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
InstanceNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
hasWarnings: PropTypes.instanceOf(Promise),
2019-10-30 16:53:58 -03:00
numImageFrames: PropTypes.number,
onDoubleClick: PropTypes.func,
onClick: PropTypes.func,
onMouseDown: PropTypes.func,
};
Thumbnail.defaultProps = {
supportsDrag: false,
active: false,
error: false,
stackPercentComplete: 0,
onDoubleClick: noop,
onClick: noop,
onMouseDown: noop,
};
export { Thumbnail };