feat(viewport): Enhance Orientation Menu with Reformat Option and UI Improvements (#5184)

Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
Co-authored-by: Alireza <ar.sedghi@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-07-14 09:10:09 -04:00
1 parent 529806c0d9
commit c175837273
14 files changed
+416 -47

No files matched your search

@@ -1,5 +1,5 @@
import React from 'react';
import { cn, Icons, ToolButton, useIconPresentation } from '@ohif/ui-next';
import { cn, Icons, useIconPresentation } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { Enums } from '@cornerstonejs/core';
import { Popover, PopoverTrigger, PopoverContent, Button, useViewportGrid } from '@ohif/ui-next';
@@ -22,13 +22,20 @@ function ViewportOrientationMenu({
onClose?: () => void;
disabled?: boolean;
}>) {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService, toolbarService } = servicesManager.services;
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const viewportOrientation = viewportInfo.getOrientation();
const [gridState] = useViewportGrid();
const viewportIdToUse = viewportId || gridState.activeViewportId;
const { IconContainer, className: iconClassName, containerProps } = useIconPresentation();
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService, toolbarService } = servicesManager.services;
const [currentOrientation, setCurrentOrientation] = React.useState<string>(
typeof viewportOrientation === 'string' ? viewportOrientation : 'axial'
);
const handleOrientationChange = (orientation: string) => {
setCurrentOrientation(orientation);
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportIdToUse);
const currentViewportType = viewportInfo?.getViewportType();
@@ -56,6 +63,9 @@ function ViewportOrientationMenu({
case 'coronal':
orientationEnum = Enums.OrientationAxis.CORONAL;
break;
case 'reformat':
orientationEnum = Enums.OrientationAxis.REFORMAT;
break;
default:
orientationEnum = Enums.OrientationAxis.ACQUISITION;
}
@@ -106,7 +116,7 @@ function ViewportOrientationMenu({
// Get proper alignment and side based on the location using toolbar service
const { align, side } = toolbarService.getAlignAndSide(Number(location));
const Icon = <Icons.OrientationSwitch className={iconClassName} />;
const Icon = React.createElement(getIcon(currentOrientation), { className: iconClassName });
return (
<Popover
open={isOpen}
@@ -139,43 +149,93 @@ function ViewportOrientationMenu({
</div>
</PopoverTrigger>
<PopoverContent
className="w-[100px] p-1"
className="h-[170px] w-[130px] flex-shrink-0 flex-col items-start rounded p-1"
align={align}
side={side}
style={{ left: 0 }}
>
<div className="flex flex-col">
<Button
variant="ghost"
className="justify-start"
onClick={() => handleOrientationChange('axial')}
>
Axial
</Button>
<Button
variant="ghost"
className="justify-start"
onClick={() => handleOrientationChange('sagittal')}
>
Sagittal
</Button>
<Button
variant="ghost"
className="justify-start"
onClick={() => handleOrientationChange('coronal')}
>
Coronal
</Button>
<Button
variant="ghost"
className="justify-start"
onClick={() => handleOrientationChange('acquisition')}
>
Acquisition
</Button>
</div>
<Button
variant="ghost"
className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('axial')}
>
<div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'axial' ? (
<Icons.Checked className="text-primary h-6 w-6" />
) : null}
</div>
<div className="flex-1 text-left">Axial</div>
</Button>
<Button
variant="ghost"
className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('sagittal')}
>
<div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'sagittal' ? (
<Icons.Checked className="text-primary h-6 w-6" />
) : null}
</div>
<div className="flex-1 text-left">Sagittal</div>
</Button>
<Button
variant="ghost"
className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('coronal')}
>
<div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'coronal' ? (
<Icons.Checked className="text-primary h-6 w-6" />
) : null}
</div>
<div className="flex-1 text-left">Coronal</div>
</Button>
<Button
variant="ghost"
className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('acquisition')}
>
<div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'acquisition' ? (
<Icons.Checked className="text-primary h-6 w-6" />
) : null}
</div>
<div className="flex-1 text-left">Acquisition</div>
</Button>
{/* Divider */}
<div className="mx-1 my-2 border-t border-white/20" />
<Button
variant="ghost"
className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('reformat')}
>
<div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'reformat' ? (
<Icons.Checked className="text-primary h-6 w-6" />
) : null}
</div>
<div className="flex-1 text-left">Reformat</div>
</Button>
</PopoverContent>
</Popover>
);
}
const getIcon = (orientationName: string) => {
switch (orientationName.toLowerCase()) {
case 'axial':
return Icons.OrientationSwitchA;
case 'sagittal':
return Icons.OrientationSwitchS;
case 'coronal':
return Icons.OrientationSwitchC;
case 'reformat':
return Icons.OrientationSwitchR;
case 'acquisition':
return Icons.OrientationSwitch;
default:
return Icons.OrientationSwitch;
}
};
export default ViewportOrientationMenu;