@@ -140,7 +140,7 @@ function CornerstoneViewportOverlay({
if (activeTools.includes('Zoom')) {
return (
-
+
Zoom:
{scale.toFixed(2)}x
@@ -174,7 +174,7 @@ function CornerstoneViewportOverlay({
}
return (
-
+
I:
{instanceNumber !== undefined
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx
index b35f16389ba..476e80a4055 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx
@@ -12,6 +12,7 @@ import promptTrackNewSeries from './promptTrackNewSeries';
import promptTrackNewStudy from './promptTrackNewStudy';
import promptSaveReport from './promptSaveReport';
import promptHydrateStructuredReport from './promptHydrateStructuredReport';
+import hydrateStructuredReport from './hydrateStructuredReport';
const TrackedMeasurementsContext = React.createContext();
TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext';
@@ -106,6 +107,10 @@ function TrackedMeasurementsContextProvider(
servicesManager,
extensionManager,
}),
+ hydrateStructuredReport: hydrateStructuredReport.bind(null, {
+ servicesManager,
+ extensionManager,
+ }),
});
// TODO: IMPROVE
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/hydrateStructuredReport.tsx b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/hydrateStructuredReport.tsx
new file mode 100644
index 00000000000..5a1be430b5e
--- /dev/null
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/hydrateStructuredReport.tsx
@@ -0,0 +1,33 @@
+import { hydrateStructuredReport as baseHydrateStructuredReport } from '@ohif/extension-cornerstone-dicom-sr';
+
+function hydrateStructuredReport(
+ { servicesManager, extensionManager },
+ ctx,
+ evt
+) {
+ const { displaySetService } = servicesManager.services;
+ const { viewportIndex, displaySetInstanceUID } = evt;
+ const srDisplaySet = displaySetService.getDisplaySetByUID(
+ displaySetInstanceUID
+ );
+
+ return new Promise((resolve, reject) => {
+ const hydrationResult = baseHydrateStructuredReport(
+ { servicesManager, extensionManager },
+ displaySetInstanceUID
+ );
+
+ const StudyInstanceUID = hydrationResult.StudyInstanceUID;
+ const SeriesInstanceUIDs = hydrationResult.SeriesInstanceUIDs;
+
+ resolve({
+ displaySetInstanceUID: evt.displaySetInstanceUID,
+ srSeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
+ viewportIndex,
+ StudyInstanceUID,
+ SeriesInstanceUIDs,
+ });
+ });
+}
+
+export default hydrateStructuredReport;
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js
index a6e45850e6f..08c6b8c4ad1 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js
@@ -1,3 +1,4 @@
+import { hydrateStructuredReport } from '@ohif/extension-cornerstone-dicom-sr';
import { assign } from 'xstate';
const RESPONSE = {
@@ -45,6 +46,7 @@ const machineConfiguration = {
cond: 'hasNotIgnoredSRSeriesForHydration',
},
RESTORE_PROMPT_HYDRATE_SR: 'promptHydrateStructuredReport',
+ HYDRATE_SR: 'hydrateStructuredReport',
},
},
promptBeginTracking: {
@@ -232,6 +234,24 @@ const machineConfiguration = {
},
},
},
+ hydrateStructuredReport: {
+ invoke: {
+ src: 'hydrateStructuredReport',
+ onDone: [
+ {
+ target: 'tracking',
+ actions: [
+ 'setTrackedStudyAndMultipleSeries',
+ 'jumpToFirstMeasurementInActiveViewport',
+ 'setIsDirtyToClean',
+ ],
+ },
+ ],
+ onError: {
+ target: 'idle',
+ },
+ },
+ },
},
strict: true,
};
diff --git a/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.tsx b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.tsx
index c732215f448..97ac7affca9 100644
--- a/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.tsx
+++ b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.tsx
@@ -5,8 +5,6 @@ import OHIF, { utils } from '@ohif/core';
import {
Notification,
ViewportActionBar,
- useCine,
- useViewportGrid,
useViewportDialog,
Tooltip,
Icon,
@@ -14,7 +12,6 @@ import {
import { useTranslation } from 'react-i18next';
-import { eventTarget, Enums } from '@cornerstonejs/core';
import { annotation } from '@cornerstonejs/tools';
import { useTrackedMeasurements } from './../getContextModule';
@@ -43,12 +40,9 @@ function TrackedCornerstoneViewport(props) {
const displaySet = displaySets[0];
const [trackedMeasurements] = useTrackedMeasurements();
- const [{ activeViewportIndex }] = useViewportGrid();
- const [{ isCineEnabled, cines }, cineService] = useCine();
const [viewportDialogState] = useViewportDialog();
const [isTracked, setIsTracked] = useState(false);
const [trackedMeasurementUID, setTrackedMeasurementUID] = useState(null);
- const [element, setElement] = useState(null);
const { trackedSeries } = trackedMeasurements.context;
const viewportId = viewportOptions.viewportId;
@@ -71,26 +65,6 @@ function TrackedCornerstoneViewport(props) {
ManufacturerModelName,
} = displaySet.images[0];
- const cineHandler = () => {
- if (!cines || !cines[viewportIndex] || !element) {
- return;
- }
-
- const cine = cines[viewportIndex];
- const isPlaying = cine.isPlaying || false;
- const frameRate = cine.frameRate || 24;
-
- const validFrameRate = Math.max(frameRate, 1);
-
- if (isPlaying) {
- cineService.playClip(element, {
- framesPerSecond: validFrameRate,
- });
- } else {
- cineService.stopClip(element);
- }
- };
-
useEffect(() => {
if (isTracked) {
annotation.config.style.setViewportToolStyles(viewportId, {
@@ -119,50 +93,10 @@ function TrackedCornerstoneViewport(props) {
};
}, [isTracked]);
- // unmount cleanup
- useEffect(() => {
- eventTarget.addEventListener(
- Enums.Events.STACK_VIEWPORT_NEW_STACK,
- cineHandler
- );
-
- return () => {
- cineService.setCine({ id: viewportIndex, isPlaying: false });
- eventTarget.removeEventListener(
- Enums.Events.STACK_VIEWPORT_NEW_STACK,
- cineHandler
- );
- };
- }, [element]);
-
- useEffect(() => {
- if (!cines || !cines[viewportIndex] || !element) {
- return;
- }
-
- cineHandler();
-
- return () => {
- if (element && cines?.[viewportIndex]?.isPlaying) {
- cineService.stopClip(element);
- }
- };
- }, [cines, viewportIndex, cineService, element, cineHandler]);
-
if (trackedSeries.includes(SeriesInstanceUID) !== isTracked) {
setIsTracked(!isTracked);
}
- /**
- * OnElementEnabled callback which is called after the cornerstoneExtension
- * has enabled the element. Note: we delegate all the image rendering to
- * cornerstoneExtension, so we don't need to do anything here regarding
- * the image rendering, element enabling etc.
- */
- const onElementEnabled = evt => {
- setElement(evt.detail.element);
- };
-
function switchMeasurement(direction) {
const newTrackedMeasurementUID = _getNextMeasurementUID(
direction,
@@ -188,12 +122,9 @@ function TrackedCornerstoneViewport(props) {
'@ohif/extension-cornerstone.viewportModule.cornerstone'
);
- return ;
+ return ;
};
- const cine = cines[viewportIndex];
- const isPlaying = (cine && cine.isPlaying) || false;
-
return (
<>
commandsManager.runCommand('toggleCine'),
- onPlayPauseChange: isPlaying =>
- cineService.setCine({
- id: activeViewportIndex,
- isPlaying,
- }),
- onFrameRateChange: frameRate =>
- cineService.setCine({
- id: activeViewportIndex,
- frameRate,
- }),
- }}
/>
{/* TODO: Viewport interface to accept stack or layers of content like this? */}
@@ -281,17 +194,25 @@ function _getNextMeasurementUID(
trackedMeasurementId,
trackedMeasurements
) {
- const { measurementService } = servicesManager.services;
+ const { measurementService, viewportGridService } = servicesManager.services;
const measurements = measurementService.getMeasurements();
+ const { activeViewportIndex, viewports } = viewportGridService.getState();
+ const {
+ displaySetInstanceUIDs: activeViewportDisplaySetInstanceUIDs,
+ } = viewports[activeViewportIndex];
+
const { trackedSeries } = trackedMeasurements.context;
- // Get the potentially trackable measurements for this series,
+ // Get the potentially trackable measurements for the series of the
+ // active viewport.
// The measurements to jump between are the same
// regardless if this series is tracked or not.
- const filteredMeasurements = measurements.filter(m =>
- trackedSeries.includes(m.referenceSeriesUID)
+ const filteredMeasurements = measurements.filter(
+ m =>
+ trackedSeries.includes(m.referenceSeriesUID) &&
+ activeViewportDisplaySetInstanceUIDs.includes(m.displaySetInstanceUID)
);
if (!filteredMeasurements.length) {
@@ -329,7 +250,7 @@ function _getNextMeasurementUID(
}
function _getStatusComponent(isTracked) {
- const trackedIcon = isTracked ? 'tracked' : 'dotted-circle';
+ const trackedIcon = isTracked ? 'status-tracked' : 'status-untracked';
return (
@@ -361,7 +282,7 @@ function _getStatusComponent(isTracked) {
}
>
-
+
);
diff --git a/platform/docs/tailwind.config.js b/platform/docs/tailwind.config.js
index a6cfc19538d..3081fe2e228 100644
--- a/platform/docs/tailwind.config.js
+++ b/platform/docs/tailwind.config.js
@@ -53,11 +53,17 @@ module.exports = {
customgreen: {
100: '#05D97C',
+ 200: '#0FD97C',
},
customblue: {
100: '#c4fdff',
200: '#38daff',
+ 300: '#1D204D',
+ },
+
+ customgray: {
+ 100: '#262943',
},
gray: {
@@ -338,14 +344,15 @@ module.exports = {
full: '100%',
screen: '100vh',
}),
- inset: {
+ inset: theme => ({
+ ...theme('spacing'),
'0': '0',
auto: 'auto',
full: '100%',
viewport: '0.5rem',
'1/2': '50%',
'viewport-scrollbar': '1.3rem',
- },
+ }),
letterSpacing: {
tighter: '-0.05em',
tight: '-0.025em',
diff --git a/platform/i18n/src/locales/en-US/Common.json b/platform/i18n/src/locales/en-US/Common.json
index 64c587150d6..ff2e5d3b170 100644
--- a/platform/i18n/src/locales/en-US/Common.json
+++ b/platform/i18n/src/locales/en-US/Common.json
@@ -2,6 +2,7 @@
"Close": "Close",
"Image": "Image",
"Layout": "Layout",
+ "LOAD": "LOAD",
"Measurements": "Measurements",
"More": "More",
"Next": "Next",
@@ -13,4 +14,4 @@
"Show": "Show",
"Stop": "Stop",
"StudyDate": "Study Date"
-}
\ No newline at end of file
+}
diff --git a/platform/ui/src/assets/icons/arrow-left-small.svg b/platform/ui/src/assets/icons/arrow-left-small.svg
new file mode 100644
index 00000000000..4a86c50e23b
--- /dev/null
+++ b/platform/ui/src/assets/icons/arrow-left-small.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/arrow-right-small.svg b/platform/ui/src/assets/icons/arrow-right-small.svg
new file mode 100644
index 00000000000..6d72336f60e
--- /dev/null
+++ b/platform/ui/src/assets/icons/arrow-right-small.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/chevron-next.svg b/platform/ui/src/assets/icons/chevron-next.svg
new file mode 100644
index 00000000000..197f3f2d424
--- /dev/null
+++ b/platform/ui/src/assets/icons/chevron-next.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/chevron-prev.svg b/platform/ui/src/assets/icons/chevron-prev.svg
new file mode 100644
index 00000000000..dcd01a2b8c6
--- /dev/null
+++ b/platform/ui/src/assets/icons/chevron-prev.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/component-slider.svg b/platform/ui/src/assets/icons/component-slider.svg
new file mode 100644
index 00000000000..b5216568907
--- /dev/null
+++ b/platform/ui/src/assets/icons/component-slider.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/platform/ui/src/assets/icons/icon-close.svg b/platform/ui/src/assets/icons/icon-close.svg
new file mode 100644
index 00000000000..75066f4d005
--- /dev/null
+++ b/platform/ui/src/assets/icons/icon-close.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/icon-pause.svg b/platform/ui/src/assets/icons/icon-pause.svg
new file mode 100644
index 00000000000..00f45ac2987
--- /dev/null
+++ b/platform/ui/src/assets/icons/icon-pause.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/icon-play.svg b/platform/ui/src/assets/icons/icon-play.svg
new file mode 100644
index 00000000000..226ae614d2c
--- /dev/null
+++ b/platform/ui/src/assets/icons/icon-play.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/info-action.svg b/platform/ui/src/assets/icons/info-action.svg
new file mode 100644
index 00000000000..305a6c385a7
--- /dev/null
+++ b/platform/ui/src/assets/icons/info-action.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/status-alert.svg b/platform/ui/src/assets/icons/status-alert.svg
new file mode 100644
index 00000000000..3a88223e96e
--- /dev/null
+++ b/platform/ui/src/assets/icons/status-alert.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/status-locked.svg b/platform/ui/src/assets/icons/status-locked.svg
new file mode 100644
index 00000000000..344ffe329e7
--- /dev/null
+++ b/platform/ui/src/assets/icons/status-locked.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/status-tracked.svg b/platform/ui/src/assets/icons/status-tracked.svg
new file mode 100644
index 00000000000..549063a4968
--- /dev/null
+++ b/platform/ui/src/assets/icons/status-tracked.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/platform/ui/src/assets/icons/status-untracked.svg b/platform/ui/src/assets/icons/status-untracked.svg
new file mode 100644
index 00000000000..cf347223f38
--- /dev/null
+++ b/platform/ui/src/assets/icons/status-untracked.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/platform/ui/src/components/CinePlayer/CinePlayer.css b/platform/ui/src/components/CinePlayer/CinePlayer.css
new file mode 100644
index 00000000000..ffa0a52b56c
--- /dev/null
+++ b/platform/ui/src/components/CinePlayer/CinePlayer.css
@@ -0,0 +1,3 @@
+.cine-fps-range-tooltip .tooltip.tooltip-top {
+ bottom: 85% !important;
+}
diff --git a/platform/ui/src/components/CinePlayer/CinePlayer.tsx b/platform/ui/src/components/CinePlayer/CinePlayer.tsx
index 6e0fe5b964c..6ede1bf11e2 100644
--- a/platform/ui/src/components/CinePlayer/CinePlayer.tsx
+++ b/platform/ui/src/components/CinePlayer/CinePlayer.tsx
@@ -1,11 +1,28 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import debounce from 'lodash.debounce';
-import { IconButton, Icon } from '../';
+import { Icon, Tooltip, InputRange } from '../';
-import './CinePlayerCustomInputRange.css';
+import './CinePlayer.css';
+import classNames from 'classnames';
-const CinePlayer = ({
+export type CinePlayerProps = {
+ className: string;
+ isPlaying: boolean;
+ minFrameRate: number;
+ maxFrameRate: number;
+ stepFrameRate: number;
+ frameRate: number;
+ onFrameRateChange: (value: number) => void;
+ onPlayPauseChange: (value: boolean) => void;
+ onClose: () => void;
+};
+
+const fpsButtonClassNames =
+ 'cursor-pointer text-primary-active active:text-primary-light hover:bg-customblue-300 w-4 flex items-center justify-center';
+
+const CinePlayer: React.FC = ({
+ className,
isPlaying,
minFrameRate,
maxFrameRate,
@@ -18,52 +35,68 @@ const CinePlayer = ({
const [frameRate, setFrameRate] = useState(defaultFrameRate);
const debouncedSetFrameRate = debounce(onFrameRateChange, 300);
- const onFrameRateChangeHandler = ({ target }) => {
- const frameRate = parseFloat(target.value);
- debouncedSetFrameRate(frameRate);
- setFrameRate(frameRate);
- };
-
- const onPlayPauseChangeHandler = () => onPlayPauseChange(!isPlaying);
+ const getPlayPauseIconName = () => (isPlaying ? 'icon-pause' : 'icon-play');
- const action = {
- false: { icon: 'old-play' },
- true: { icon: 'old-stop' },
+ const handleSetFrameRate = (frameRate: number) => {
+ if (frameRate < minFrameRate || frameRate > maxFrameRate) {
+ return;
+ }
+ setFrameRate(frameRate);
+ debouncedSetFrameRate(frameRate);
};
return (
-
-
+ onPlayPauseChange(!isPlaying)}
+ />
+
+ }
>
-
-
-
-
-
{`${frameRate.toFixed(
- 1
- )} fps`}
-
-
+ handleSetFrameRate(frameRate - 1)}
+ >
+
+
+
+ {`${frameRate} FPS`}
+
+ handleSetFrameRate(frameRate + 1)}
+ >
+
+
+
+
+
-
-
+ />
);
};
diff --git a/platform/ui/src/components/CinePlayer/__stories__/cinePlayer.stories.mdx b/platform/ui/src/components/CinePlayer/__stories__/cinePlayer.stories.mdx
deleted file mode 100644
index f53ba7112a9..00000000000
--- a/platform/ui/src/components/CinePlayer/__stories__/cinePlayer.stories.mdx
+++ /dev/null
@@ -1,32 +0,0 @@
-import CinePlayer from '../CinePlayer';
-import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
-import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
-
-export const argTypes = {
- component: CinePlayer,
- title: 'Components/CinePlayer',
-};
-
-
-
-export const cineTemplate = createComponentTemplate(CinePlayer);
-
-
-
-- [Overview](#overview)
-- [Props](#props)
-- [Contribute](#contribute)
-
-## Overview
-
-
- {cineTemplate.bind({})}
-
-
-## Props
-
-
-
-## Contribute
-
-
diff --git a/platform/ui/src/components/Icon/getIcon.js b/platform/ui/src/components/Icon/getIcon.js
index d1b1d886c4f..b4ebdf56297 100644
--- a/platform/ui/src/components/Icon/getIcon.js
+++ b/platform/ui/src/components/Icon/getIcon.js
@@ -3,6 +3,8 @@ import React from 'react';
import arrowDown from './../../assets/icons/arrow-down.svg';
import arrowLeft from './../../assets/icons/arrow-left.svg';
+import arrowLeftSmall from './../../assets/icons/arrow-left-small.svg';
+import arrowRightSmall from './../../assets/icons/arrow-right-small.svg';
import calendar from './../../assets/icons/calendar.svg';
import cancel from './../../assets/icons/cancel.svg';
import clipboard from './../../assets/icons/clipboard.svg';
@@ -11,6 +13,8 @@ import dottedCircle from './../../assets/icons/dotted-circle.svg';
import circledCheckmark from './../../assets/icons/circled-checkmark.svg';
import chevronDown from './../../assets/icons/chevron-down.svg';
import chevronLeft from './../../assets/icons/chevron-left.svg';
+import chevronNext from './../../assets/icons/chevron-next.svg';
+import chevronPrev from './../../assets/icons/chevron-prev.svg';
import chevronRight from './../../assets/icons/chevron-right.svg';
import eyeVisible from './../../assets/icons/eye-visible.svg';
import eyeHidden from './../../assets/icons/eye-hidden.svg';
@@ -18,6 +22,7 @@ import exclamation from './../../assets/icons/exclamation.svg';
import externalLink from './../../assets/icons/external-link.svg';
import groupLayers from './../../assets/icons/group-layers.svg';
import info from './../../assets/icons/info.svg';
+import infoAction from './../../assets/icons/info-action.svg';
import infoLink from './../../assets/icons/info-link.svg';
import launchArrow from './../../assets/icons/launch-arrow.svg';
import launchInfo from './../../assets/icons/launch-info.svg';
@@ -36,12 +41,19 @@ import settings from './../../assets/icons/settings.svg';
import sorting from './../../assets/icons/sorting.svg';
import sortingActiveDown from './../../assets/icons/sorting-active-down.svg';
import sortingActiveUp from './../../assets/icons/sorting-active-up.svg';
+import statusAlert from './../../assets/icons/status-alert.svg';
+import statusLocked from './../../assets/icons/status-locked.svg';
+import statusTracked from './../../assets/icons/status-tracked.svg';
+import statusUntracked from './../../assets/icons/status-untracked.svg';
import tracked from './../../assets/icons/tracked.svg';
import unlink from './../../assets/icons/unlink.svg';
import checkboxChecked from './../../assets/icons/checkbox-checked.svg';
import checkboxUnchecked from './../../assets/icons/checkbox-unchecked.svg';
+import iconClose from './../../assets/icons/icon-close.svg';
import iconNextInactive from './../../assets/icons/icon-next-inactive.svg';
import iconNext from './../../assets/icons/icon-next.svg';
+import iconPlay from './../../assets/icons/icon-play.svg';
+import iconPause from './../../assets/icons/icon-pause.svg';
import iconPrevInactive from './../../assets/icons/icon-prev-inactive.svg';
import iconPrev from './../../assets/icons/icon-prev.svg';
import navigationPanelRightHide from './../../assets/icons/navigation-panel-right-hide.svg';
@@ -107,6 +119,9 @@ import oldStop from './../../assets/icons/old-stop.svg';
const ICONS = {
'arrow-down': arrowDown,
+ 'arrow-left': arrowLeft,
+ 'arrow-left-small': arrowLeftSmall,
+ 'arrow-right-small': arrowRightSmall,
calendar: calendar,
cancel: cancel,
clipboard: clipboard,
@@ -115,12 +130,18 @@ const ICONS = {
'circled-checkmark': circledCheckmark,
'chevron-down': chevronDown,
'chevron-left': chevronLeft,
+ 'chevron-next': chevronNext,
+ 'chevron-prev': chevronPrev,
'chevron-right': chevronRight,
'eye-visible': eyeVisible,
'eye-hidden': eyeHidden,
'external-link': externalLink,
'group-layers': groupLayers,
info: info,
+ 'icon-close': iconClose,
+ 'icon-play': iconPlay,
+ 'icon-pause': iconPause,
+ 'info-action': infoAction,
'info-link': infoLink,
'arrow-left': arrowLeft,
'launch-arrow': launchArrow,
@@ -140,6 +161,10 @@ const ICONS = {
settings: settings,
'sorting-active-down': sortingActiveDown,
'sorting-active-up': sortingActiveUp,
+ 'status-alert': statusAlert,
+ 'status-locked': statusLocked,
+ 'status-tracked': statusTracked,
+ 'status-untracked': statusUntracked,
sorting: sorting,
tracked: tracked,
unlink: unlink,
diff --git a/platform/ui/src/components/IconButton/IconButton.tsx b/platform/ui/src/components/IconButton/IconButton.tsx
index d073443d790..a32df49a6b7 100644
--- a/platform/ui/src/components/IconButton/IconButton.tsx
+++ b/platform/ui/src/components/IconButton/IconButton.tsx
@@ -21,7 +21,7 @@ const disabledClasses = {
const variantClasses = {
text: {
default:
- 'text-white hover:bg-primary-light hover:text-black active:opacity-80 focus:bg-primary-light focus:text-black',
+ 'text-white hover:bg-primary-light hover:text-black active:opacity-80 focus:!bg-primary-light focus:text-black',
primary:
'text-primary-main hover:bg-primary-main hover:text-white active:opacity-80 focus:bg-primary-main focus:text-white',
secondary:
diff --git a/platform/ui/src/components/InputRange/InputRange.tsx b/platform/ui/src/components/InputRange/InputRange.tsx
index e79dcdce3a0..28e98b06f5e 100644
--- a/platform/ui/src/components/InputRange/InputRange.tsx
+++ b/platform/ui/src/components/InputRange/InputRange.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useCallback } from 'react';
+import React, { useState, useCallback, useEffect } from 'react';
import classNames from 'classnames';
import Typography from '../Typography';
import './InputRange.css';
@@ -23,6 +23,7 @@ const InputRange: React.FC<{
inputClassName?: string;
labelClassName?: string;
labelVariant?: string;
+ showLabel: boolean;
}> = ({
value,
onChange,
@@ -34,12 +35,16 @@ const InputRange: React.FC<{
inputClassName,
labelClassName,
labelVariant,
+ showLabel = true,
}) => {
const [rangeValue, setRangeValue] = useState(value);
+ // Allow for the value property to update the range value.
+ useEffect(() => setRangeValue(value), [value]);
+
const handleChange = useCallback(
e => {
- const rangeValue = e.target.value;
+ const rangeValue = Number(e.target.value);
setRangeValue(rangeValue);
onChange(rangeValue);
},
@@ -71,14 +76,16 @@ const InputRange: React.FC<{
id="myRange"
step={step}
/>
-
- {rangeValue}
- {unit}
-
+ {showLabel && (
+
+ {rangeValue}
+ {unit}
+
+ )}
);
};
diff --git a/platform/ui/src/components/LegacyCinePlayer/LegacyCinePlayer.tsx b/platform/ui/src/components/LegacyCinePlayer/LegacyCinePlayer.tsx
new file mode 100644
index 00000000000..465368888f7
--- /dev/null
+++ b/platform/ui/src/components/LegacyCinePlayer/LegacyCinePlayer.tsx
@@ -0,0 +1,99 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import debounce from 'lodash.debounce';
+import { IconButton, Icon } from '../';
+
+import './LegacyCinePlayerCustomInputRange.css';
+
+const LegacyCinePlayer = ({
+ isPlaying,
+ minFrameRate,
+ maxFrameRate,
+ stepFrameRate,
+ frameRate: defaultFrameRate,
+ onFrameRateChange,
+ onPlayPauseChange,
+ onClose,
+}) => {
+ const [frameRate, setFrameRate] = useState(defaultFrameRate);
+ const debouncedSetFrameRate = debounce(onFrameRateChange, 300);
+
+ const onFrameRateChangeHandler = ({ target }) => {
+ const frameRate = parseFloat(target.value);
+ debouncedSetFrameRate(frameRate);
+ setFrameRate(frameRate);
+ };
+
+ const onPlayPauseChangeHandler = () => onPlayPauseChange(!isPlaying);
+
+ const action = {
+ false: { icon: 'old-play' },
+ true: { icon: 'old-stop' },
+ };
+
+ return (
+
+
+
+
+
+
+
{`${frameRate.toFixed(
+ 1
+ )} fps`}
+
+
+
+
+
+ );
+};
+
+const noop = () => {};
+
+LegacyCinePlayer.defaultProps = {
+ isPlaying: false,
+ minFrameRate: 1,
+ maxFrameRate: 90,
+ stepFrameRate: 1,
+ frameRate: 24,
+ onPlayPauseChange: noop,
+ onFrameRateChange: noop,
+ onClose: noop,
+};
+
+LegacyCinePlayer.propTypes = {
+ /** Minimum value for range slider */
+ minFrameRate: PropTypes.number.isRequired,
+ /** Maximum value for range slider */
+ maxFrameRate: PropTypes.number.isRequired,
+ /** Increment range slider can "step" in either direction */
+ stepFrameRate: PropTypes.number.isRequired,
+ frameRate: PropTypes.number.isRequired,
+ /** 'true' if playing, 'false' if paused */
+ isPlaying: PropTypes.bool.isRequired,
+ onPlayPauseChange: PropTypes.func,
+ onFrameRateChange: PropTypes.func,
+ onClose: PropTypes.func,
+};
+
+export default LegacyCinePlayer;
diff --git a/platform/ui/src/components/CinePlayer/CinePlayerCustomInputRange.css b/platform/ui/src/components/LegacyCinePlayer/LegacyCinePlayerCustomInputRange.css
similarity index 66%
rename from platform/ui/src/components/CinePlayer/CinePlayerCustomInputRange.css
rename to platform/ui/src/components/LegacyCinePlayer/LegacyCinePlayerCustomInputRange.css
index 19661d2b9b9..d321b7fa79e 100644
--- a/platform/ui/src/components/CinePlayer/CinePlayerCustomInputRange.css
+++ b/platform/ui/src/components/LegacyCinePlayer/LegacyCinePlayerCustomInputRange.css
@@ -1,9 +1,9 @@
/*
- * This is a custom input style scoped specifically to CinePlayer
+ * This is a custom input style scoped specifically to LegacyCinePlayer
* written in plain CSS with color variables from tailwind
* to avoid complex compatibility configuration.
*/
-.CinePlayer input[type='range'] {
+.LegacyCinePlayer input[type='range'] {
-webkit-appearance: none;
background: transparent;
width: 100%;
@@ -11,11 +11,11 @@
z-index: 5;
}
-.CinePlayer input[type='range']:focus {
+.LegacyCinePlayer input[type='range']:focus {
outline: none;
}
-.CinePlayer input[type='range']::-webkit-slider-runnable-track {
+.LegacyCinePlayer input[type='range']::-webkit-slider-runnable-track {
width: 100%;
height: 2px;
cursor: pointer;
@@ -26,7 +26,7 @@
border: 0px solid #000000;
}
-.CinePlayer input[type='range']::-webkit-slider-thumb {
+.LegacyCinePlayer input[type='range']::-webkit-slider-thumb {
box-shadow: 0px 0px 0px #000000;
border: 4px solid #000000;
height: 18px;
@@ -38,11 +38,11 @@
margin-top: -9px;
}
-.CinePlayer input[type='range']:focus::-webkit-slider-runnable-track {
+.LegacyCinePlayer input[type='range']:focus::-webkit-slider-runnable-track {
@apply bg-primary-light;
}
-.CinePlayer input[type='range']::-moz-range-track {
+.LegacyCinePlayer input[type='range']::-moz-range-track {
width: 100%;
height: 2px;
cursor: pointer;
@@ -53,7 +53,7 @@
border: 0px solid #000000;
}
-.CinePlayer input[type='range']::-moz-range-thumb {
+.LegacyCinePlayer input[type='range']::-moz-range-thumb {
box-shadow: 0px 0px 0px #000000;
border: 2px solid #000000;
height: 12px;
@@ -63,7 +63,7 @@
cursor: pointer;
}
-.CinePlayer input[type='range']::-ms-track {
+.LegacyCinePlayer input[type='range']::-ms-track {
width: 100%;
height: 2px;
cursor: pointer;
@@ -73,21 +73,21 @@
color: transparent;
}
-.CinePlayer input[type='range']::-ms-fill-lower {
+.LegacyCinePlayer input[type='range']::-ms-fill-lower {
@apply bg-primary-light;
border: 0px solid #000000;
border-radius: 10px;
box-shadow: 0px 0px 0px #000000;
}
-.CinePlayer input[type='range']::-ms-fill-upper {
+.LegacyCinePlayer input[type='range']::-ms-fill-upper {
@apply bg-primary-light;
border: 0px solid #000000;
border-radius: 10px;
box-shadow: 0px 0px 0px #000000;
}
-.CinePlayer input[type='range']::-ms-thumb {
+.LegacyCinePlayer input[type='range']::-ms-thumb {
margin-top: 1px;
box-shadow: 0px 0px 0px #000000;
border: 4px solid #000000;
@@ -98,10 +98,10 @@
cursor: pointer;
}
-.CinePlayer input[type='range']:focus::-ms-fill-lower {
+.LegacyCinePlayer input[type='range']:focus::-ms-fill-lower {
@apply bg-primary-light;
}
-.CinePlayer input[type='range']:focus::-ms-fill-upper {
+.LegacyCinePlayer input[type='range']:focus::-ms-fill-upper {
@apply bg-primary-light;
}
diff --git a/platform/ui/src/components/LegacyCinePlayer/__stories__/legacyCinePlayer.stories.mdx b/platform/ui/src/components/LegacyCinePlayer/__stories__/legacyCinePlayer.stories.mdx
new file mode 100644
index 00000000000..35ad2c7f0fe
--- /dev/null
+++ b/platform/ui/src/components/LegacyCinePlayer/__stories__/legacyCinePlayer.stories.mdx
@@ -0,0 +1,35 @@
+import LegacyCinePlayer from '../LegacyCinePlayer';
+import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
+import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
+
+export const argTypes = {
+ component: LegacyCinePlayer,
+ title: 'Components/LegacyCinePlayer',
+};
+
+
+
+export const cineTemplate = createComponentTemplate(LegacyCinePlayer);
+
+
+
+- [Overview](#overview)
+- [Props](#props)
+- [Contribute](#contribute)
+
+## Overview
+
+
+ {cineTemplate.bind({})}
+
+
+## Props
+
+
+
+## Contribute
+
+
diff --git a/platform/ui/src/components/LegacyCinePlayer/index.js b/platform/ui/src/components/LegacyCinePlayer/index.js
new file mode 100644
index 00000000000..6344b92c3de
--- /dev/null
+++ b/platform/ui/src/components/LegacyCinePlayer/index.js
@@ -0,0 +1,2 @@
+import LegacyCinePlayer from './LegacyCinePlayer';
+export default LegacyCinePlayer;
diff --git a/platform/ui/src/components/LegacyPatientInfo/LegacyPatientInfo.tsx b/platform/ui/src/components/LegacyPatientInfo/LegacyPatientInfo.tsx
new file mode 100644
index 00000000000..9c0957630ee
--- /dev/null
+++ b/platform/ui/src/components/LegacyPatientInfo/LegacyPatientInfo.tsx
@@ -0,0 +1,149 @@
+import React from 'react';
+import classnames from 'classnames';
+import PropTypes from 'prop-types';
+import { Icon, Tooltip } from '../';
+import { useTranslation } from 'react-i18next';
+
+const classes = {
+ infoHeader: 'text-base text-primary-light',
+ infoText: 'text-base text-white max-w-24 truncate',
+ firstRow: 'flex flex-col',
+ row: 'flex flex-col ml-4',
+};
+
+function LegacyPatientInfo({
+ patientName,
+ patientSex,
+ patientAge,
+ MRN,
+ thickness,
+ spacing,
+ scanner,
+ isOpen,
+ showPatientInfoRef,
+}) {
+ const { t } = useTranslation('PatientInfo');
+
+ while (patientAge.charAt(0) === '0') {
+ patientAge = patientAge.substr(1);
+ }
+
+ return (
+
+
+
+
+
+
+
+ {patientName}
+
+
+
+
+ {t('Sex')}
+
+
+ {patientSex}
+
+
+
+
+ {t('Age')}
+
+
+ {patientAge}
+
+
+
+
+ {t('MRN')}
+
+
+ {MRN}
+
+
+
+
+
+
+ {t('Thickness')}
+
+
+ {thickness}
+
+
+
+
+ {t('Spacing')}
+
+
+ {spacing}
+
+
+
+
+ {t('Scanner')}
+
+
+ {scanner}
+
+
+
+
+
+ )
+ }
+ >
+
+
+
+ );
+}
+
+LegacyPatientInfo.propTypes = {
+ patientName: PropTypes.string,
+ patientSex: PropTypes.string,
+ patientAge: PropTypes.string,
+ MRN: PropTypes.string,
+ thickness: PropTypes.string,
+ spacing: PropTypes.string,
+ scanner: PropTypes.string,
+ isOpen: PropTypes.bool,
+ showPatientInfoRef: PropTypes.object,
+};
+
+export default LegacyPatientInfo;
diff --git a/platform/ui/src/components/LegacyPatientInfo/index.js b/platform/ui/src/components/LegacyPatientInfo/index.js
new file mode 100644
index 00000000000..d4506eeab4d
--- /dev/null
+++ b/platform/ui/src/components/LegacyPatientInfo/index.js
@@ -0,0 +1,2 @@
+import LegacyPatientInfo from './LegacyPatientInfo';
+export default LegacyPatientInfo;
diff --git a/platform/ui/src/components/ViewportActionBar/ViewportActionBar.stories.tsx b/platform/ui/src/components/LegacyViewportActionBar/LegacyViewportActionBar.stories.tsx
similarity index 82%
rename from platform/ui/src/components/ViewportActionBar/ViewportActionBar.stories.tsx
rename to platform/ui/src/components/LegacyViewportActionBar/LegacyViewportActionBar.stories.tsx
index ec6d1a3b12a..fb76d1feac9 100644
--- a/platform/ui/src/components/ViewportActionBar/ViewportActionBar.stories.tsx
+++ b/platform/ui/src/components/LegacyViewportActionBar/LegacyViewportActionBar.stories.tsx
@@ -1,14 +1,14 @@
import React from 'react';
-import ViewportActionBar from './ViewportActionBar';
+import LegacyViewportActionBar from './LegacyViewportActionBar';
export default {
- component: ViewportActionBar,
- title: 'Components/ViewportActionBar',
+ component: LegacyViewportActionBar,
+ title: 'Components/LegacyViewportActionBar',
};
export const Default = () => (
);
diff --git a/platform/ui/src/components/SegmentationGroupTable/SegmentationConfig.tsx b/platform/ui/src/components/SegmentationGroupTable/SegmentationConfig.tsx
index 437d0cdad49..6167ec55df3 100644
--- a/platform/ui/src/components/SegmentationGroupTable/SegmentationConfig.tsx
+++ b/platform/ui/src/components/SegmentationGroupTable/SegmentationConfig.tsx
@@ -13,6 +13,15 @@ const ActiveSegmentationConfig = ({
setFillAlpha,
usePercentage,
}) => {
+ const [
+ useOutlineOpacityPercentage,
+ setUseOutlineOpacityPercentage,
+ ] = useState(usePercentage);
+
+ const [useFillAlphaPercentage, setUseFillAlphaPercentage] = useState(
+ usePercentage
+ );
+
return (