From e80fc6f47708e1d6b1a1e1de438196a4b74ec637 Mon Sep 17 00:00:00 2001 From: Ibrahim <93064150+IbrahimCSAE@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:31:06 -0230 Subject: [PATCH] fix(bugs): enhancements and bug fixes (#4036) --- .../src/utils/promptHydrateRT.ts | 5 + .../src/utils/promptHydrateSEG.ts | 5 + .../src/panels/DynamicVolumeControls.tsx | 2 +- .../WindowLevelActionMenu/Colormap.tsx | 5 + .../WindowLevelActionMenu/VolumeLighting.tsx | 18 +- .../VolumeRenderingOptions.tsx | 4 +- .../VolumeRenderingPresets.tsx | 2 +- .../VolumeRenderingQuality.tsx | 6 +- .../WindowLevelActionMenu/VolumeShift.tsx | 2 + .../WindowLevelActionMenu/WindowLevel.tsx | 33 ++- .../WindowLevelActionMenu.tsx | 12 +- .../getWindowLevelActionMenu.tsx | 8 +- .../cornerstone/src/getToolbarModule.tsx | 4 +- extensions/cornerstone/src/hps/fourUp.ts | 7 +- extensions/cornerstone/src/hps/main3D.ts | 7 +- .../src/hps/mprAnd3DVolumeViewport.ts | 6 +- extensions/cornerstone/src/hps/only3D.ts | 7 +- extensions/cornerstone/src/hps/primary3D.ts | 7 +- extensions/cornerstone/src/init.tsx | 2 +- .../cornerstone/src/initMeasurementService.js | 18 ++ .../CornerstoneViewportService.ts | 8 +- extensions/cornerstone/src/utils/colormaps.js | 32 +-- .../utils/measurementServiceMappings/Probe.ts | 187 ++++++++++++++++ .../UltrasoundDirectional.ts | 206 ++++++++++++++++++ .../constants/supportedTools.js | 2 + .../measurementServiceMappingsFactory.ts | 39 ++++ .../default/src/ViewerLayout/ViewerHeader.tsx | 3 +- extensions/default/src/commandsModule.ts | 1 + .../promptHydrateStructuredReport.js | 6 + .../PanelStudyBrowserTracking.tsx | 72 +++++- extensions/tmtv/src/Panels/PanelPetSUV.tsx | 2 +- .../PanelROIThresholdExport.tsx | 4 +- platform/app/public/config/netlify.js | 1 + platform/app/src/routes/WorkList/WorkList.tsx | 3 +- .../docs/configuration/configurationFiles.md | 3 +- platform/ui/src/assets/icons/tool-layout.svg | 15 +- .../ui/src/assets/icons/tool-seg-brush.svg | 9 + .../ui/src/assets/icons/tool-seg-eraser.svg | 10 + .../ui/src/assets/icons/tool-seg-shape.svg | 9 + .../src/assets/icons/tool-seg-threshold.svg | 17 ++ .../components/ButtonGroup/ButtonGroup.tsx | 45 ++-- platform/ui/src/components/Header/Header.tsx | 3 +- .../HeaderPatientInfo/HeaderPatientInfo.tsx | 7 +- platform/ui/src/components/Icon/getIcon.js | 8 +- .../src/components/IconButton/IconButton.tsx | 3 +- .../src/components/InputRange/InputRange.tsx | 4 +- .../ui/src/components/ListMenu/ListMenu.tsx | 2 +- .../components/Notification/Notification.tsx | 2 +- .../components/PanelSection/PanelSection.tsx | 6 +- .../SegmentationGroupSegment.tsx | 6 +- .../SegmentationGroupTable.tsx | 6 +- .../SegmentationGroupTableExpanded.tsx | 6 +- .../ui/src/components/SidePanel/SidePanel.tsx | 18 +- .../components/SplitButton/SplitButton.tsx | 2 +- .../ui/src/components/StudyItem/StudyItem.tsx | 4 +- .../ToolbarButton/ToolbarButton.tsx | 2 +- .../src/contextProviders/DialogProvider.tsx | 2 +- .../ui/src/types/PatientInfoVisibility.ts | 1 + 58 files changed, 768 insertions(+), 148 deletions(-) create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/UltrasoundDirectional.ts create mode 100644 platform/ui/src/assets/icons/tool-seg-brush.svg create mode 100644 platform/ui/src/assets/icons/tool-seg-eraser.svg create mode 100644 platform/ui/src/assets/icons/tool-seg-shape.svg create mode 100644 platform/ui/src/assets/icons/tool-seg-threshold.svg diff --git a/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts b/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts index 91492cbfb..2baa0305b 100644 --- a/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts +++ b/extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts @@ -66,6 +66,11 @@ function _askHydrate(uiViewportDialogService, viewportId) { uiViewportDialogService.hide(); resolve(RESPONSE.CANCEL); }, + onKeyPress: event => { + if (event.key === 'Enter') { + onSubmit(RESPONSE.HYDRATE_SEG); + } + }, }); }); } diff --git a/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts b/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts index 9f8c1dddf..c42b32e69 100644 --- a/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts +++ b/extensions/cornerstone-dicom-seg/src/utils/promptHydrateSEG.ts @@ -63,6 +63,11 @@ function _askHydrate(uiViewportDialogService, viewportId) { uiViewportDialogService.hide(); resolve(RESPONSE.CANCEL); }, + onKeyPress: event => { + if (event.key === 'Enter') { + onSubmit(RESPONSE.HYDRATE_SEG); + } + }, }); }); } diff --git a/extensions/cornerstone-dynamic-volume/src/panels/DynamicVolumeControls.tsx b/extensions/cornerstone-dynamic-volume/src/panels/DynamicVolumeControls.tsx index 337e9e0a3..b780669f9 100644 --- a/extensions/cornerstone-dynamic-volume/src/panels/DynamicVolumeControls.tsx +++ b/extensions/cornerstone-dynamic-volume/src/panels/DynamicVolumeControls.tsx @@ -22,7 +22,7 @@ const Header = ({ title, tooltip }) => (
{tooltip}
} - position="bottom" + position="bottom-left" tight={true} tooltipBoxClassName="max-w-xs" > diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/Colormap.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/Colormap.tsx index 335e54456..a37eae3a9 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/Colormap.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/Colormap.tsx @@ -68,6 +68,11 @@ export function Colormap({ key: index, style: { minWidth: `calc(100% / ${displaySets.length})`, + fontSize: '0.8rem', + textAlign: 'center', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', }, })); }, [displaySets]); diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx index 80b6452c6..7cd7d4f85 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx @@ -60,7 +60,11 @@ export function VolumeLighting({ min={0} type="range" step={0.1} - style={{ background: calculateBackground(ambient) }} + style={{ + background: calculateBackground(ambient), + '--thumb-inner-color': '#5acce6', + '--thumb-outer-color': '#090c29', + }} /> )} @@ -84,7 +88,11 @@ export function VolumeLighting({ min={0} type="range" step={0.1} - style={{ background: calculateBackground(diffuse) }} + style={{ + background: calculateBackground(diffuse), + '--thumb-inner-color': '#5acce6', + '--thumb-outer-color': '#090c29', + }} /> )} @@ -109,7 +117,11 @@ export function VolumeLighting({ min={0} type="range" step={0.1} - style={{ background: calculateBackground(specular) }} + style={{ + background: calculateBackground(specular), + '--thumb-inner-color': '#5acce6', + '--thumb-outer-color': '#090c29', + }} /> )} diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx index a75f0bd52..d666e354b 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx @@ -25,10 +25,10 @@ export function VolumeRenderingOptions({ commandsManager={commandsManager} serviceManager={serviceManager} /> -
+
LIGHTING
- +
onChange(parseInt(e.target.value, 10))} - style={{ background: calculateBackground((quality - min) / (max - min)) }} + style={{ + background: calculateBackground((quality - min) / (max - min)), + '--thumb-inner-color': '#5acce6', + '--thumb-outer-color': '#090c29', + }} /> )}
diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShift.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShift.tsx index 6ad9eb9d3..274f71599 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShift.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShift.tsx @@ -82,6 +82,8 @@ export function VolumeShift({ step={step} style={{ background: calculateBackground((shift - minShift) / (maxShift - minShift)), + '--thumb-inner-color': '#5acce6', + '--thumb-outer-color': '#090c29', }} /> )} diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx index 44b75bf93..eda52d5bb 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; export type WindowLevelProps = { viewportId: string; - presets: Record>; + presets: Array>>; commandsManager: CommandsManager; }; @@ -23,25 +23,34 @@ export function WindowLevel({ commandName: 'setViewportWindowLevel', commandOptions: { ...props, + viewportId, }, context: 'CORNERSTONE', }); }, - [commandsManager] + [commandsManager, viewportId] ); return ( - - {t('Modality Presets', { modality: Object.keys(presets)[0] })} - - {Object.values(presets)[0].map((preset, index) => ( - onSetWindowLevel({ ...preset, viewportId })} - > + {presets.map((modalityPresets, modalityIndex) => ( + + {Object.entries(modalityPresets).map(([modality, presetsArray]) => ( + + + {t('Modality Presets', { modality })} + + {presetsArray.map((preset, index) => ( + onSetWindowLevel(preset)} + /> + ))} + + ))} + ))} ); diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx index b4db9fd21..6905c9988 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx @@ -19,7 +19,7 @@ import { utilities } from '@cornerstonejs/core'; export type WindowLevelActionMenuProps = { viewportId: string; element: HTMLElement; - presets: Record>; + presets: Array>>; verticalDirection: AllInOneMenu.VerticalDirection; horizontalDirection: AllInOneMenu.HorizontalDirection; commandsManager: CommandsManager; @@ -128,10 +128,8 @@ export function WindowLevelActionMenu({ iconClassName={classNames( // Visible on hover and for the active viewport activeViewportId === viewportId ? 'visible' : 'invisible group-hover:visible', - 'flex shrink-0 cursor-pointer rounded active:text-white', - isLight - ? 'text-aqua-pale hover:bg-secondary-dark' - : 'text-primary-light hover:bg-secondary-light/60' + 'flex shrink-0 cursor-pointer rounded active:text-white text-primary-light', + isLight ? ' hover:bg-secondary-dark' : 'hover:bg-secondary-light/60' )} menuStyle={{ maxHeight: vpHeight - 32, minWidth: 218 }} onVisibilityChange={() => { @@ -166,10 +164,10 @@ export function WindowLevelActionMenu({ )} - {presets && !is3DVolume && ( + {presets && presets.length > 0 && !is3DVolume && ( 0; - - return hasMenu ? ( + return ( - ) : null; + ); } diff --git a/extensions/cornerstone/src/getToolbarModule.tsx b/extensions/cornerstone/src/getToolbarModule.tsx index e58643da4..74a116269 100644 --- a/extensions/cornerstone/src/getToolbarModule.tsx +++ b/extensions/cornerstone/src/getToolbarModule.tsx @@ -43,8 +43,8 @@ export default function getToolbarModule({ commandsManager, servicesManager }) { return { disabled: false, className: isPrimaryActive - ? '!text-black bg-primary-light' - : '!text-common-bright hover:!bg-primary-dark hover:!text-primary-light', + ? '!text-black bg-primary-light rounded' + : '!text-common-bright hover:!bg-primary-dark hover:!text-primary-light rounded', // Todo: isActive right now is used for nested buttons where the primary // button needs to be fully rounded (vs partial rounded) when active // otherwise it does not have any other use diff --git a/extensions/cornerstone/src/hps/fourUp.ts b/extensions/cornerstone/src/hps/fourUp.ts index 69305e6c1..df4056854 100644 --- a/extensions/cornerstone/src/hps/fourUp.ts +++ b/extensions/cornerstone/src/hps/fourUp.ts @@ -77,8 +77,11 @@ export const fourUp = { { id: 'mprDisplaySet', options: { - // ToDo: choose appropriate preset - displayPreset: 'CT-Bone', + displayPreset: { + CT: 'CT-Bone', + MR: 'MR-Default', + default: 'CT-Bone', + }, }, }, ], diff --git a/extensions/cornerstone/src/hps/main3D.ts b/extensions/cornerstone/src/hps/main3D.ts index a00ccc4a7..5976dae2c 100644 --- a/extensions/cornerstone/src/hps/main3D.ts +++ b/extensions/cornerstone/src/hps/main3D.ts @@ -77,8 +77,11 @@ export const main3D = { { id: 'mprDisplaySet', options: { - // ToDo: choose appropriate preset - displayPreset: 'CT-Bone', + displayPreset: { + CT: 'CT-Bone', + MR: 'MR-Default', + default: 'CT-Bone', + }, }, }, ], diff --git a/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts b/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts index 759ce1849..53289f829 100644 --- a/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts +++ b/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts @@ -84,7 +84,11 @@ export const mprAnd3DVolumeViewport = { { id: 'mprDisplaySet', options: { - displayPreset: 'CT-Bone', + displayPreset: { + CT: 'CT-Bone', + MR: 'MR-Default', + default: 'CT-Bone', + }, }, }, ], diff --git a/extensions/cornerstone/src/hps/only3D.ts b/extensions/cornerstone/src/hps/only3D.ts index 53c32cf61..17d6ceecd 100644 --- a/extensions/cornerstone/src/hps/only3D.ts +++ b/extensions/cornerstone/src/hps/only3D.ts @@ -51,8 +51,11 @@ export const only3D = { { id: 'mprDisplaySet', options: { - // ToDo: choose appropriate preset - displayPreset: 'CT-Bone', + displayPreset: { + CT: 'CT-Bone', + MR: 'MR-Default', + default: 'CT-Bone', + }, }, }, ], diff --git a/extensions/cornerstone/src/hps/primary3D.ts b/extensions/cornerstone/src/hps/primary3D.ts index 11054d106..ebb8a59d9 100644 --- a/extensions/cornerstone/src/hps/primary3D.ts +++ b/extensions/cornerstone/src/hps/primary3D.ts @@ -77,8 +77,11 @@ export const primary3D = { { id: 'mprDisplaySet', options: { - // ToDo: choose appropriate preset - displayPreset: 'CT-Bone', + displayPreset: { + CT: 'CT-Bone', + MR: 'MR-Default', + default: 'CT-Bone', + }, }, }, ], diff --git a/extensions/cornerstone/src/init.tsx b/extensions/cornerstone/src/init.tsx index c06bff08a..2282abb5b 100644 --- a/extensions/cornerstone/src/init.tsx +++ b/extensions/cornerstone/src/init.tsx @@ -160,7 +160,7 @@ export default async function init({ const labelmapRepresentation = cornerstoneTools.Enums.SegmentationRepresentations.Labelmap; cornerstoneTools.segmentation.config.setGlobalRepresentationConfig(labelmapRepresentation, { - fillAlpha: 0.3, + fillAlpha: 0.5, fillAlphaInactive: 0.2, outlineOpacity: 1, outlineOpacityInactive: 0.65, diff --git a/extensions/cornerstone/src/initMeasurementService.js b/extensions/cornerstone/src/initMeasurementService.js index 61145aa18..79d94f78f 100644 --- a/extensions/cornerstone/src/initMeasurementService.js +++ b/extensions/cornerstone/src/initMeasurementService.js @@ -33,6 +33,8 @@ const initMeasurementService = ( PlanarFreehandROI, SplineROI, LivewireContour, + Probe, + UltrasoundDirectional, } = measurementServiceMappingsFactory( measurementService, displaySetService, @@ -157,6 +159,22 @@ const initMeasurementService = ( LivewireContour.toMeasurement ); + measurementService.addMapping( + csTools3DVer1MeasurementSource, + 'Probe', + Probe.matchingCriteria, + Probe.toAnnotation, + Probe.toMeasurement + ); + + measurementService.addMapping( + csTools3DVer1MeasurementSource, + 'UltrasoundDirectionalTool', + UltrasoundDirectional.matchingCriteria, + UltrasoundDirectional.toAnnotation, + UltrasoundDirectional.toMeasurement + ); + return csTools3DVer1MeasurementSource; }; diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts index fa33ef58b..319884d62 100644 --- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts @@ -721,10 +721,14 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi } public async setVolumesForViewport(viewport, volumeInputArray, presentations) { - const { displaySetService, toolGroupService } = this.servicesManager.services; + const { displaySetService, toolGroupService, viewportGridService } = + this.servicesManager.services; const viewportInfo = this.getViewportInfo(viewport.id); const displaySetOptions = viewportInfo.getDisplaySetOptions(); + const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewport.id); + const displaySet = displaySetService.getDisplaySetByUID(displaySetUIDs[0]); + const displaySetModality = displaySet?.Modality; // Todo: use presentations states const volumesProperties = volumeInputArray.map((volumeInput, index) => { @@ -750,7 +754,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi } if (displayPreset !== undefined) { - properties.preset = displayPreset; + properties.preset = displayPreset[displaySetModality] || displayPreset.default; } return { properties, volumeId }; diff --git a/extensions/cornerstone/src/utils/colormaps.js b/extensions/cornerstone/src/utils/colormaps.js index 85946725c..a21df96ac 100644 --- a/extensions/cornerstone/src/utils/colormaps.js +++ b/extensions/cornerstone/src/utils/colormaps.js @@ -1,4 +1,20 @@ const colormaps = [ + { + ColorSpace: 'RGB', + Name: 'Grayscale', + name: 'Grayscale', + NanColor: [1, 0, 0], + RGBPoints: [0, 0, 0, 0, 1, 1, 1, 1], + description: 'Grayscale', + }, + { + ColorSpace: 'RGB', + Name: 'X Ray', + name: 'X Ray', + NanColor: [1, 0, 0], + RGBPoints: [0, 1, 1, 1, 1, 0, 0, 0], + description: 'X Ray', + }, { ColorSpace: 'RGB', Name: 'hsv', @@ -1579,22 +1595,6 @@ const colormaps = [ ], description: 'Siemens', }, - { - ColorSpace: 'RGB', - Name: 'X Ray', - name: 'X Ray', - NanColor: [1, 0, 0], - RGBPoints: [0, 1, 1, 1, 1, 0, 0, 0], - description: 'X Ray', - }, - { - ColorSpace: 'RGB', - Name: 'Grayscale', - name: 'Grayscale', - NanColor: [1, 0, 0], - RGBPoints: [0, 0, 0, 0, 1, 1, 1, 1], - description: 'Grayscale', - }, ]; export { colormaps }; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts new file mode 100644 index 000000000..554df1842 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts @@ -0,0 +1,187 @@ +import SUPPORTED_TOOLS from './constants/supportedTools'; +import { getDisplayUnit } from './utils'; +import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; +import { utils } from '@ohif/core'; + +const Probe = { + toAnnotation: measurement => {}, + + /** + * Maps cornerstone annotation event data to measurement service format. + * + * @param {Object} cornerstone Cornerstone event data + * @return {Measurement} Measurement instance + */ + toMeasurement: ( + csToolsEventDetail, + displaySetService, + CornerstoneViewportService, + getValueTypeFromToolType, + customizationService + ) => { + const { annotation, viewportId } = csToolsEventDetail; + const { metadata, data, annotationUID } = annotation; + + if (!metadata || !data) { + console.warn('Length tool: Missing metadata or data'); + return null; + } + + const { toolName, referencedImageId, FrameOfReferenceUID } = metadata; + const validToolType = SUPPORTED_TOOLS.includes(toolName); + + if (!validToolType) { + throw new Error('Tool not supported'); + } + + const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = + getSOPInstanceAttributes(referencedImageId); + + let displaySet; + + if (SOPInstanceUID) { + displaySet = displaySetService.getDisplaySetForSOPInstanceUID( + SOPInstanceUID, + SeriesInstanceUID + ); + } else { + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + } + + const { points } = data.handles; + + const mappedAnnotations = getMappedAnnotations(annotation, displaySetService); + + const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService); + const getReport = () => + _getReport(mappedAnnotations, points, FrameOfReferenceUID, customizationService); + + return { + uid: annotationUID, + SOPInstanceUID, + FrameOfReferenceUID, + points, + metadata, + referenceSeriesUID: SeriesInstanceUID, + referenceStudyUID: StudyInstanceUID, + frameNumber: mappedAnnotations?.[0]?.frameNumber || 1, + toolName: metadata.toolName, + displaySetInstanceUID: displaySet.displaySetInstanceUID, + label: data.label, + displayText: displayText, + data: data.cachedStats, + type: getValueTypeFromToolType(toolName), + getReport, + }; + }, +}; + +function getMappedAnnotations(annotation, DisplaySetService) { + const { metadata, data } = annotation; + const { cachedStats } = data; + const { referencedImageId } = metadata; + const targets = Object.keys(cachedStats); + + if (!targets.length) { + return; + } + + const annotations = []; + Object.keys(cachedStats).forEach(targetId => { + const targetStats = cachedStats[targetId]; + + if (!referencedImageId) { + throw new Error('Non-acquisition plane measurement mapping not supported'); + } + + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = + getSOPInstanceAttributes(referencedImageId); + + const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( + SOPInstanceUID, + SeriesInstanceUID, + frameNumber + ); + + const { SeriesNumber } = displaySet; + const { value } = targetStats; + const unit = 'HU'; + + annotations.push({ + SeriesInstanceUID, + SOPInstanceUID, + SeriesNumber, + frameNumber, + unit, + value, + }); + }); + + return annotations; +} + +/* +This function is used to convert the measurement data to a format that is +suitable for the report generation (e.g. for the csv report). The report +returns a list of columns and corresponding values. +*/ +function _getReport(mappedAnnotations, points, FrameOfReferenceUID, customizationService) { + const columns = []; + const values = []; + + // Add Type + columns.push('AnnotationType'); + values.push('Cornerstone:Probe'); + + mappedAnnotations.forEach(annotation => { + const { value, unit } = annotation; + columns.push(`Probe (${unit})`); + values.push(value); + }); + + if (FrameOfReferenceUID) { + columns.push('FrameOfReferenceUID'); + values.push(FrameOfReferenceUID); + } + + if (points) { + columns.push('points'); + values.push(points.map(p => p.join(' ')).join(';')); + } + + return { + columns, + values, + }; +} + +function getDisplayText(mappedAnnotations, displaySet, customizationService) { + if (!mappedAnnotations || !mappedAnnotations.length) { + return ''; + } + + const displayText = []; + + const { value, unit, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0]; + + const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID); + + let InstanceNumber; + if (instance) { + InstanceNumber = instance.InstanceNumber; + } + + const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : ''; + const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : ''; + if (value === undefined) { + return displayText; + } + const roundedValue = utils.roundNumber(value, 2); + displayText.push( + `${roundedValue} ${getDisplayUnit(unit)} (S: ${SeriesNumber}${instanceText}${frameText})` + ); + + return displayText; +} + +export default Probe; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/UltrasoundDirectional.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/UltrasoundDirectional.ts new file mode 100644 index 000000000..f88973ae9 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/UltrasoundDirectional.ts @@ -0,0 +1,206 @@ +import SUPPORTED_TOOLS from './constants/supportedTools'; +import { getDisplayUnit } from './utils'; +import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; +import { utils } from '@ohif/core'; + +const UltrasoundDirectional = { + toAnnotation: measurement => {}, + + /** + * Maps cornerstone annotation event data to measurement service format. + * + * @param {Object} cornerstone Cornerstone event data + * @return {Measurement} Measurement instance + */ + toMeasurement: ( + csToolsEventDetail, + displaySetService, + CornerstoneViewportService, + getValueTypeFromToolType, + customizationService + ) => { + const { annotation, viewportId } = csToolsEventDetail; + const { metadata, data, annotationUID } = annotation; + + if (!metadata || !data) { + console.warn('Length tool: Missing metadata or data'); + return null; + } + + const { toolName, referencedImageId, FrameOfReferenceUID } = metadata; + const validToolType = SUPPORTED_TOOLS.includes(toolName); + + if (!validToolType) { + throw new Error('Tool not supported'); + } + + const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = + getSOPInstanceAttributes(referencedImageId); + + let displaySet; + + if (SOPInstanceUID) { + displaySet = displaySetService.getDisplaySetForSOPInstanceUID( + SOPInstanceUID, + SeriesInstanceUID + ); + } else { + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + } + + const { points } = data.handles; + + const mappedAnnotations = getMappedAnnotations(annotation, displaySetService); + + const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService); + const getReport = () => + _getReport(mappedAnnotations, points, FrameOfReferenceUID, customizationService); + + return { + uid: annotationUID, + SOPInstanceUID, + FrameOfReferenceUID, + points, + metadata, + referenceSeriesUID: SeriesInstanceUID, + referenceStudyUID: StudyInstanceUID, + frameNumber: mappedAnnotations?.[0]?.frameNumber || 1, + toolName: metadata.toolName, + displaySetInstanceUID: displaySet.displaySetInstanceUID, + label: data.label, + displayText: displayText, + data: data.cachedStats, + type: getValueTypeFromToolType(toolName), + getReport, + }; + }, +}; + +function getMappedAnnotations(annotation, DisplaySetService) { + const { metadata, data } = annotation; + const { cachedStats } = data; + const { referencedImageId } = metadata; + const targets = Object.keys(cachedStats); + + if (!targets.length) { + return; + } + + const annotations = []; + Object.keys(cachedStats).forEach(targetId => { + const targetStats = cachedStats[targetId]; + + if (!referencedImageId) { + throw new Error('Non-acquisition plane measurement mapping not supported'); + } + + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = + getSOPInstanceAttributes(referencedImageId); + + const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( + SOPInstanceUID, + SeriesInstanceUID, + frameNumber + ); + + const { SeriesNumber } = displaySet; + const { xValues, yValues, units, isUnitless, isHorizontal } = targetStats; + + annotations.push({ + SeriesInstanceUID, + SOPInstanceUID, + SeriesNumber, + frameNumber, + xValues, + yValues, + units, + isUnitless, + isHorizontal, + }); + }); + + return annotations; +} + +/* +This function is used to convert the measurement data to a format that is +suitable for the report generation (e.g. for the csv report). The report +returns a list of columns and corresponding values. +*/ +function _getReport(mappedAnnotations, points, FrameOfReferenceUID, customizationService) { + const columns = []; + const values = []; + + // Add Type + columns.push('AnnotationType'); + values.push('Cornerstone:UltrasoundDirectional'); + + mappedAnnotations.forEach(annotation => { + const { xValues, yValues, units, isUnitless } = annotation; + if (isUnitless) { + columns.push('Length' + units[0]); + values.push(utils.roundNumber(xValues[0], 2)); + } else { + const dist1 = Math.abs(xValues[1] - xValues[0]); + const dist2 = Math.abs(yValues[1] - yValues[0]); + columns.push('Time' + units[0]); + values.push(utils.roundNumber(dist1, 2)); + columns.push('Length' + units[1]); + values.push(utils.roundNumber(dist2, 2)); + } + }); + + if (FrameOfReferenceUID) { + columns.push('FrameOfReferenceUID'); + values.push(FrameOfReferenceUID); + } + + if (points) { + columns.push('points'); + values.push(points.map(p => p.join(' ')).join(';')); + } + + return { + columns, + values, + }; +} + +function getDisplayText(mappedAnnotations, displaySet, customizationService) { + if (!mappedAnnotations || !mappedAnnotations.length) { + return ''; + } + + const displayText = []; + + const { xValues, yValues, units, isUnitless, SeriesNumber, SOPInstanceUID, frameNumber } = + mappedAnnotations[0]; + + const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID); + + let InstanceNumber; + if (instance) { + InstanceNumber = instance.InstanceNumber; + } + + const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : ''; + const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : ''; + const seriesText = `(S: ${SeriesNumber}${instanceText}${frameText})`; + + if (xValues === undefined || yValues === undefined) { + return displayText; + } + + if (isUnitless) { + displayText.push(`${utils.roundNumber(xValues[0], 2)} ${units[0]} ${seriesText}`); + } else { + const dist1 = Math.abs(xValues[1] - xValues[0]); + const dist2 = Math.abs(yValues[1] - yValues[0]); + displayText.push(`${utils.roundNumber(dist1)} ${units[0]} ${seriesText}`); + displayText.push(`${utils.roundNumber(dist2)} ${units[1]} ${seriesText}`); + } + + return displayText; +} + +export default UltrasoundDirectional; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js b/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js index 8f69319da..9f7bbb04a 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js @@ -11,4 +11,6 @@ export default [ 'PlanarFreehandROI', 'SplineROI', 'LivewireContour', + 'Probe', + 'UltrasoundDirectionalTool', ]; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.ts index 19de0308f..0cc991fe7 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.ts @@ -10,6 +10,8 @@ import PlanarFreehandROI from './PlanarFreehandROI'; import RectangleROI from './RectangleROI'; import SplineROI from './SplineROI'; import LivewireContour from './LivewireContour'; +import Probe from './Probe'; +import UltrasoundDirectional from './UltrasoundDirectional'; const measurementServiceMappingsFactory = ( measurementService: MeasurementService, @@ -44,6 +46,8 @@ const measurementServiceMappingsFactory = ( Angle: ANGLE, SplineROI: POLYLINE, LivewireContour: POLYLINE, + Probe: POINT, + UltrasoundDirectional: POLYLINE, }; return TOOL_TYPE_TO_VALUE_TYPE[toolType]; @@ -211,6 +215,24 @@ const measurementServiceMappingsFactory = ( ], }, + Probe: { + toAnnotation: Probe.toAnnotation, + toMeasurement: csToolsAnnotation => + Probe.toMeasurement( + csToolsAnnotation, + displaySetService, + cornerstoneViewportService, + _getValueTypeFromToolType, + customizationService + ), + matchingCriteria: [ + { + valueType: MeasurementService.VALUE_TYPES.POINT, + points: 1, + }, + ], + }, + CobbAngle: { toAnnotation: CobbAngle.toAnnotation, toMeasurement: csToolsAnnotation => @@ -244,6 +266,23 @@ const measurementServiceMappingsFactory = ( }, ], }, + UltrasoundDirectional: { + toAnnotation: UltrasoundDirectional.toAnnotation, + toMeasurement: csToolsAnnotation => + UltrasoundDirectional.toMeasurement( + csToolsAnnotation, + displaySetService, + cornerstoneViewportService, + _getValueTypeFromToolType, + customizationService + ), + matchingCriteria: [ + { + valueType: MeasurementService.VALUE_TYPES.POLYLINE, + points: 2, + }, + ], + }, }; return factories; diff --git a/extensions/default/src/ViewerLayout/ViewerHeader.tsx b/extensions/default/src/ViewerLayout/ViewerHeader.tsx index 9d676bcbe..6d5ab60f7 100644 --- a/extensions/default/src/ViewerLayout/ViewerHeader.tsx +++ b/extensions/default/src/ViewerLayout/ViewerHeader.tsx @@ -53,6 +53,7 @@ function ViewerHeader({ hotkeysManager, extensionManager, servicesManager, appCo content: AboutModal, title: t('AboutModal:About OHIF Viewer'), contentProps: { versionNumber, commitHash }, + containerDimensions: 'max-w-4xl max-h-4xl', }), }, { @@ -114,7 +115,7 @@ function ViewerHeader({ hotkeysManager, extensionManager, servicesManager, appCo appConfig={appConfig} > -
+
diff --git a/extensions/default/src/commandsModule.ts b/extensions/default/src/commandsModule.ts index c86663239..04fd54029 100644 --- a/extensions/default/src/commandsModule.ts +++ b/extensions/default/src/commandsModule.ts @@ -445,6 +445,7 @@ const commandsModule = ({ displaySetInstanceUID, onClose: UIModalService.hide, }, + containerDimensions: 'max-w-4xl max-h-4xl', title: 'DICOM Tag Browser', }); }, diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js index 1881337ce..579ff9c7b 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js @@ -74,6 +74,12 @@ function _askTrackMeasurements(uiViewportDialogService, viewportId) { uiViewportDialogService.hide(); resolve(RESPONSE.CANCEL); }, + onKeyPress: event => { + if (event.key === 'Enter') { + const action = actions.find(action => action.value === RESPONSE.HYDRATE_REPORT); + onSubmit(action.value); + } + }, }); }); } diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx index c085b15fb..4d9dc3a4d 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx @@ -329,6 +329,65 @@ function PanelStudyBrowserTracking({ } }, [expandedStudyInstanceUIDs, jumpToDisplaySet, tabs]); + const onClickUntrack = displaySetInstanceUID => { + const onConfirm = () => { + const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); + sendTrackedMeasurementsEvent('UNTRACK_SERIES', { + SeriesInstanceUID: displaySet.SeriesInstanceUID, + }); + const measurements = measurementService.getMeasurements(); + measurements.forEach(m => { + if (m.referenceSeriesUID === displaySet.SeriesInstanceUID) { + measurementService.remove(m.uid); + } + }); + }; + + uiDialogService.create({ + id: 'untrack-series', + centralize: true, + isDraggable: false, + showOverlay: true, + content: Dialog, + contentProps: { + title: 'Untrack Series', + body: () => ( +
+

Are you sure you want to untrack this series?

+

+ This action cannot be undone and will delete all your existing measurements. +

+
+ ), + actions: [ + { + id: 'cancel', + text: 'Cancel', + type: ButtonEnums.type.secondary, + }, + { + id: 'yes', + text: 'Yes', + type: ButtonEnums.type.primary, + classes: ['untrack-yes-button'], + }, + ], + onClose: () => uiDialogService.dismiss({ id: 'untrack-series' }), + onSubmit: async ({ action }) => { + switch (action.id) { + case 'yes': + onConfirm(); + uiDialogService.dismiss({ id: 'untrack-series' }); + break; + case 'cancel': + uiDialogService.dismiss({ id: 'untrack-series' }); + break; + } + }, + }, + }); + }; + return ( { - const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); - // TODO: shift this somewhere else where we're centralizing this logic? - // Potentially a helper from displaySetInstanceUID to this - sendTrackedMeasurementsEvent('UNTRACK_SERIES', { - SeriesInstanceUID: displaySet.SeriesInstanceUID, - }); - const measurements = measurementService.getMeasurements(); - measurements.forEach(m => { - if (m.referenceSeriesUID === displaySet.SeriesInstanceUID) { - measurementService.remove(m.uid); - } - }); + onClickUntrack(displaySetInstanceUID); }} onClickThumbnail={() => {}} onDoubleClickThumbnail={onDoubleClickThumbnailHandler} diff --git a/extensions/tmtv/src/Panels/PanelPetSUV.tsx b/extensions/tmtv/src/Panels/PanelPetSUV.tsx index 3252a08ac..eccd76afb 100644 --- a/extensions/tmtv/src/Panels/PanelPetSUV.tsx +++ b/extensions/tmtv/src/Panels/PanelPetSUV.tsx @@ -127,7 +127,7 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) { } return (
-
+
diff --git a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdExport.tsx b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdExport.tsx index d8da1f504..181240e6c 100644 --- a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdExport.tsx +++ b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdExport.tsx @@ -77,7 +77,7 @@ export default function PanelRoiThresholdSegmentation({ servicesManager, command return ( <> -
+
{tmtvValue !== null ? (
@@ -87,7 +87,7 @@ export default function PanelRoiThresholdSegmentation({ servicesManager, command
{`${tmtvValue} mL`}
) : null} -
+
), title: (instances || 0).toString(), - gridCol: 4, + gridCol: 2, }, ], // Todo: This is actually running for all rows, even if they are @@ -454,6 +454,7 @@ function WorkList({ content: AboutModal, title: t('AboutModal:About OHIF Viewer'), contentProps: { versionNumber, commitHash }, + containerDimensions: 'max-w-4xl max-h-4xl', }), }, { diff --git a/platform/docs/docs/configuration/configurationFiles.md b/platform/docs/docs/configuration/configurationFiles.md index be62938f3..1c945f4cb 100644 --- a/platform/docs/docs/configuration/configurationFiles.md +++ b/platform/docs/docs/configuration/configurationFiles.md @@ -123,7 +123,8 @@ Here are a list of some options available: what is specified by `maxNumberOfWebWorkers`. Some windows machines require smaller values. - `acceptHeader` : accept header to request specific dicom transfer syntax ex : [ 'multipart/related; type=image/jls; q=1', 'multipart/related; type=application/octet-stream; q=0.1' ] - `investigationalUseDialog`: This should contain an object with `option` value, it can be either `always` which always shows the dialog once per session, `never` which never shows the dialog, or `configure` which shows the dialog once and won't show it again until a set number of days defined by the user, if it's set to configure, you are required to add an additional property `days` which is the number of days to wait before showing the dialog again. -- `groupEnabled`: boolean, if set to true, all valid modes for the study get grouped together first, then the rest of the modes. If false, all modes are shown in the order they are defined in the configuration. +- `groupEnabledModesFirst`: boolean, if set to true, all valid modes for the study get grouped together first, then the rest of the modes. If false, all modes are shown in the order they are defined in the configuration. +- `showPatientInfo`: string, if set to 'visible', the patient info header will be shown and its initial state is expanded. If set to 'visibleCollapsed', the patient info header will be shown but it's initial state is collapsed. If set to 'disabled', the patient info header will never be shown, and if set to 'visibleReadOnly', the patient info header will be shown and always expanded. - `requestTransferSyntaxUID` : Request a specific Transfer syntax from dicom web server ex: 1.2.840.10008.1.2.4.80 (applied only if acceptHeader is not set) - `omitQuotationForMultipartRequest`: Some servers (e.g., .NET) require the `multipart/related` request to be sent without quotation marks. Defaults to `false`. If your server doesn't require this, then setting this flag to `true` might improve performance (by removing the need for preflight requests). Also note that if auth headers are used, a preflight request is required. diff --git a/platform/ui/src/assets/icons/tool-layout.svg b/platform/ui/src/assets/icons/tool-layout.svg index 7e8bf1494..2ebbcabc6 100644 --- a/platform/ui/src/assets/icons/tool-layout.svg +++ b/platform/ui/src/assets/icons/tool-layout.svg @@ -3,19 +3,20 @@ tool-layout - - - - - + + + + + + + - + - \ No newline at end of file diff --git a/platform/ui/src/assets/icons/tool-seg-brush.svg b/platform/ui/src/assets/icons/tool-seg-brush.svg new file mode 100644 index 000000000..2258260d9 --- /dev/null +++ b/platform/ui/src/assets/icons/tool-seg-brush.svg @@ -0,0 +1,9 @@ + + + tool-seg-brush + + + + + + \ No newline at end of file diff --git a/platform/ui/src/assets/icons/tool-seg-eraser.svg b/platform/ui/src/assets/icons/tool-seg-eraser.svg new file mode 100644 index 000000000..a1f78fed4 --- /dev/null +++ b/platform/ui/src/assets/icons/tool-seg-eraser.svg @@ -0,0 +1,10 @@ + + + tool-seg-eraser + + + + + + + \ No newline at end of file diff --git a/platform/ui/src/assets/icons/tool-seg-shape.svg b/platform/ui/src/assets/icons/tool-seg-shape.svg new file mode 100644 index 000000000..290d156e6 --- /dev/null +++ b/platform/ui/src/assets/icons/tool-seg-shape.svg @@ -0,0 +1,9 @@ + + + tool-seg-shape + + + + + + \ No newline at end of file diff --git a/platform/ui/src/assets/icons/tool-seg-threshold.svg b/platform/ui/src/assets/icons/tool-seg-threshold.svg new file mode 100644 index 000000000..4870fa5a4 --- /dev/null +++ b/platform/ui/src/assets/icons/tool-seg-threshold.svg @@ -0,0 +1,17 @@ + + + tool-seg-threshold + + + + + + + + + + + + + + \ No newline at end of file diff --git a/platform/ui/src/components/ButtonGroup/ButtonGroup.tsx b/platform/ui/src/components/ButtonGroup/ButtonGroup.tsx index a8f89d249..32b294082 100644 --- a/platform/ui/src/components/ButtonGroup/ButtonGroup.tsx +++ b/platform/ui/src/components/ButtonGroup/ButtonGroup.tsx @@ -40,27 +40,30 @@ const ButtonGroup = ({ 'border-secondary-light rounded-[5px] border bg-black': !separated, })} > - {!separated && - Children.map(children, (child, index) => { - if (React.isValidElement(child)) { - return cloneElement(child, { - key: index, - className: classnames( - 'rounded-[4px] px-2 py-1', - index === activeIndex - ? 'bg-customblue-40 text-white' - : 'text-primary-active bg-black', - child.props.className, - disabled ? 'ohif-disabled' : '' - ), - onClick: e => { - child.props.onClick && child.props.onClick(e); - handleButtonClick(index); - }, - }); - } - return child; - })} + {!separated && ( +
+ {Children.map(children, (child, index) => { + if (React.isValidElement(child)) { + return cloneElement(child, { + key: index, + className: classnames( + 'rounded-[4px] px-2 py-1', + index === activeIndex + ? 'bg-customblue-40 text-white' + : 'text-primary-active bg-black', + child.props.className, + disabled ? 'ohif-disabled' : '' + ), + onClick: e => { + child.props.onClick && child.props.onClick(e); + handleButtonClick(index); + }, + }); + } + return child; + })} +
+ )} {separated && (
{Children.map(children, (child, index) => { diff --git a/platform/ui/src/components/Header/Header.tsx b/platform/ui/src/components/Header/Header.tsx index 2ba958b08..c75dfa923 100644 --- a/platform/ui/src/components/Header/Header.tsx +++ b/platform/ui/src/components/Header/Header.tsx @@ -65,8 +65,7 @@ function Header({
{children}
- {(showPatientInfo === PatientInfoVisibility.VISIBLE || - showPatientInfo === PatientInfoVisibility.VISIBLE_COLLAPSED) && ( + {showPatientInfo !== PatientInfoVisibility.DISABLED && ( { - if (!isMixedPatients) { + if (!isMixedPatients && appConfig.showPatientInfo !== PatientInfoVisibility.VISIBLE_READONLY) { setExpanded(!expanded); } }; diff --git a/platform/ui/src/components/Icon/getIcon.js b/platform/ui/src/components/Icon/getIcon.js index c2edee38d..494b8ce3b 100644 --- a/platform/ui/src/components/Icon/getIcon.js +++ b/platform/ui/src/components/Icon/getIcon.js @@ -145,11 +145,11 @@ import iconAdd from './../../assets/icons/icon-add.svg'; import iconRename from './../../assets/icons/icon-rename.svg'; import iconDelete from './../../assets/icons/icon-delete.svg'; import iconMoreMenu from './../../assets/icons/icon-more-menu.svg'; -import iconToolBrush from './../../assets/icons/icon-tool-brush.svg'; -import iconToolEraser from './../../assets/icons/icon-tool-eraser.svg'; +import iconToolBrush from './../../assets/icons/tool-seg-brush.svg'; +import iconToolEraser from './../../assets/icons/tool-seg-eraser.svg'; import iconToolScissor from './../../assets/icons/icon-tool-scissor.svg'; -import iconToolShape from './../../assets/icons/icon-tool-shape.svg'; -import iconToolThreshold from './../../assets/icons/icon-tool-threshold.svg'; +import iconToolShape from './../../assets/icons/tool-seg-shape.svg'; +import iconToolThreshold from './../../assets/icons/tool-seg-threshold.svg'; import viewportWindowLevel from './../../assets/icons/viewport-window-level.svg'; import dicomTagBrowser from './../../assets/icons/tool-dicom-tag-browser.svg'; import iconToolFreehandRoi from './../../assets/icons/tool-freehand-roi.svg'; diff --git a/platform/ui/src/components/IconButton/IconButton.tsx b/platform/ui/src/components/IconButton/IconButton.tsx index ea6922455..fff9d168f 100644 --- a/platform/ui/src/components/IconButton/IconButton.tsx +++ b/platform/ui/src/components/IconButton/IconButton.tsx @@ -64,6 +64,7 @@ const iconSizeClasses = { medium: 'w-5 h-5', large: 'w-6 h-6', toolbar: 'w-[28px] h-[28px]', + toolbox: 'w-[24px] h-[24px]', }; const fullWidthClasses = { @@ -92,7 +93,7 @@ const IconButton = ({ onClick(e); }; - const padding = size === 'toolbar' ? '6px' : size === 'toolbox' ? '6px' : null; + const padding = size === 'toolbar' ? '6px' : size === 'toolbox' ? '4px' : null; return (