feat(actions): simplify action corner api through the toolbarService (#5033)

This commit is contained in:
Alireza authored and GitHub committed 2025-05-15 12:08:12 -04:00
1 parent f71b88e639
commit 7c15bb8901
246 files changed
+7930 -4270

No files matched your search

@@ -16,14 +16,16 @@ import {
import { useSystem } from '@ohif/core';
import { useViewportDisplaySets } from '../../hooks/useViewportDisplaySets';
import SelectItemWithModality from '../SelectItemWithModality';
import { useViewportRendering } from '../../hooks';
function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: string }>) {
const { commandsManager, servicesManager } = useSystem();
const [pendingForegrounds, setPendingForegrounds] = useState<string[]>([]);
const [pendingSegmentations, setPendingSegmentations] = useState<string[]>([]);
const [thresholdOpacityEnabled, setThresholdOpacityEnabled] = useState(false);
const { toggleColorbar } = useViewportRendering(viewportId);
const { hangingProtocolService } = servicesManager.services;
const { hangingProtocolService, toolbarService } = servicesManager.services;
const {
backgroundDisplaySet,
@@ -34,6 +36,8 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
foregroundDisplaySets,
} = useViewportDisplaySets(viewportId);
const [thresholdOpacityEnabled, setThresholdOpacityEnabled] = useState(false);
/**
* Change the background display set
*/
@@ -48,21 +52,6 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
});
};
/**
* Handle threshold and opacity toggle
*/
const handleThresholdOpacityToggle = (checked: boolean) => {
setThresholdOpacityEnabled(checked);
if (foregroundDisplaySets.length > 0) {
// Example implementation of threshold/opacity adjustment
commandsManager.runCommand('setForegroundThresholdOpacity', {
viewportId,
enabled: checked,
});
}
};
/**
* Replace a display set layer with a new one
*/
@@ -182,6 +171,18 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
}
};
// Check if the advanced window level components exist in toolbar
const hasAdvancedRenderingControls = !!toolbarService.getButton('advancedRenderingControls');
const hasOpacityMenu = !!toolbarService.getButton('opacityMenu');
const handleThresholdOpacityToggle = () => {
const newValue = !thresholdOpacityEnabled;
if (hasAdvancedRenderingControls) {
toggleColorbar();
}
setThresholdOpacityEnabled(newValue);
};
return (
<div className="bg-popover flex h-full w-[275px] flex-col rounded rounded-md p-1.5">
{/* Top buttons row */}
@@ -232,15 +233,17 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
<SelectItem
key={displaySet.displaySetInstanceUID}
value={displaySet.displaySetInstanceUID}
className="pr-2"
>
{displaySet.label}
<SelectItemWithModality displaySet={displaySet} />
</SelectItem>
{potentialOverlayDisplaySets.map(item => (
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
className="pr-2"
>
{item.label}
<SelectItemWithModality displaySet={item} />
</SelectItem>
))}
</SelectContent>
@@ -284,8 +287,9 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
className="pr-2"
>
{item.label}
<SelectItemWithModality displaySet={item} />
</SelectItem>
))}
</SelectContent>
@@ -334,15 +338,17 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
<SelectItem
key={displaySet.displaySetInstanceUID}
value={displaySet.displaySetInstanceUID}
className="pr-2"
>
{displaySet.label}
<SelectItemWithModality displaySet={displaySet} />
</SelectItem>
{potentialForegroundDisplaySets.map(item => (
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
className="pr-2"
>
{item.label}
<SelectItemWithModality displaySet={item} />
</SelectItem>
))}
</SelectContent>
@@ -386,8 +392,9 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
className="pr-2"
>
{item.label}
<SelectItemWithModality displaySet={item} />
</SelectItem>
))}
</SelectContent>
@@ -420,7 +427,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
<div className="mt-1 mb-1 flex items-center px-1">
<Icons.LayerBackground className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
<Select
value={backgroundDisplaySet.displaySetInstanceUID}
value={backgroundDisplaySet?.displaySetInstanceUID}
onValueChange={value => {
const selectedDisplaySet = potentialBackgroundDisplaySets.find(
ds => ds.displaySetInstanceUID === value
@@ -433,8 +440,8 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
<SelectTrigger className="flex-1">
<SelectValue>
{(
backgroundDisplaySet.SeriesDescription ||
backgroundDisplaySet.label ||
backgroundDisplaySet?.SeriesDescription ||
backgroundDisplaySet?.label ||
'background'
).toUpperCase()}
</SelectValue>
@@ -444,15 +451,16 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
<SelectItem
key={displaySet.displaySetInstanceUID}
value={displaySet.displaySetInstanceUID}
className="pr-2"
>
{displaySet.label}
<SelectItemWithModality displaySet={displaySet} />
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* {foregroundDisplaySets.length > 0 && (
{foregroundDisplaySets.length > 0 && (hasAdvancedRenderingControls || hasOpacityMenu) && (
<div className="mt-1 ml-7">
<div className="flex items-center">
<Switch
@@ -470,7 +478,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
</label>
</div>
</div>
)} */}
)}
</div>
);
}
@@ -1,73 +1,74 @@
import React, { ReactNode } from 'react';
import { useSystem } from '@ohif/core';
import {
Button,
cn,
Icons,
Popover,
PopoverContent,
PopoverTrigger,
useViewportGrid,
useViewportActionCorners,
useIconPresentation,
} from '@ohif/ui-next';
import ViewportDataOverlayMenu from './ViewportDataOverlayMenu';
import classNames from 'classnames';
import { MENU_IDS } from '../menus/menu-ids';
import { useViewportDisplaySets } from '../../hooks/useViewportDisplaySets';
export function ViewportDataOverlayMenuWrapper({
viewportId,
displaySets,
location,
}: withAppTypes<{
type DataOverlayMenuProps = {
viewportId: string;
element: HTMLElement;
location: string;
}>): ReactNode {
const [viewportGrid] = useViewportGrid();
const [actionCornerState, viewportActionCornersAPI] = useViewportActionCorners();
isOpen?: boolean;
onOpen?: () => void;
onClose?: () => void;
disabled?: boolean;
};
const isMenuOpen =
actionCornerState.viewports[viewportId]?.[location]?.find(
item => item.id === MENU_IDS.DATA_OVERLAY_MENU
)?.isOpen ?? false;
export function ViewportDataOverlayMenuWrapper(props: DataOverlayMenuProps): ReactNode {
const { viewportId, location, isOpen = false, onOpen, onClose, disabled, ...rest } = props;
const { viewportDisplaySets: displaySets } = useViewportDisplaySets(viewportId);
const { IconContainer, className: iconClassName, containerProps } = useIconPresentation();
const handleOpenChange = (openState: boolean) => {
if (openState) {
viewportActionCornersAPI.openItem?.(viewportId, MENU_IDS.DATA_OVERLAY_MENU);
onOpen?.();
} else {
viewportActionCornersAPI.closeItem?.(viewportId, MENU_IDS.DATA_OVERLAY_MENU);
onClose?.();
}
};
// Get proper alignment and side based on the location
let align = 'center';
let side = 'bottom';
const { servicesManager } = useSystem();
const { toolbarService } = servicesManager.services;
if (location !== undefined) {
const positioning = viewportActionCornersAPI.getAlignAndSide(location);
align = positioning.align;
side = positioning.side;
}
const { align, side } = toolbarService.getAlignAndSide(location);
const Icon = <Icons.ViewportViews className={iconClassName} />;
return (
<Popover
open={isMenuOpen}
open={isOpen}
onOpenChange={handleOpenChange}
>
<PopoverTrigger
asChild
className="flex items-center justify-center"
>
<Button
variant="ghost"
size="icon"
className={cn(
viewportGrid.activeViewportId === viewportId
? 'visible'
: 'invisible group-hover/pane:visible'
<div>
{IconContainer ? (
<IconContainer
disabled={disabled}
icon="ViewportViews"
{...rest}
{...containerProps}
>
{Icon}
</IconContainer>
) : (
<Button
variant="ghost"
size="icon"
disabled={disabled}
>
{Icon}
</Button>
)}
>
<Icons.ViewportViews className={classNames('text-highlight')} />
</Button>
</div>
</PopoverTrigger>
<PopoverContent
className="border-none bg-transparent p-0 shadow-none"
@@ -1,12 +0,0 @@
import React, { ReactNode } from 'react';
import { ViewportDataOverlayMenuWrapper } from './ViewportDataOverlayMenuWrapper';
export function getViewportDataOverlaySettingsMenu(
props: withAppTypes<{
viewportId: string;
element: HTMLElement;
location: string;
}>
): ReactNode {
return <ViewportDataOverlayMenuWrapper {...props} />;
}
@@ -42,6 +42,14 @@ export function getModalityOverlayColormap(customizationService, modality) {
export function getEnhancedDisplaySets({ viewportId, services }) {
const { displaySetService, viewportGridService } = services;
const displaySetsUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
if (!displaySetsUIDs?.length) {
return {
viewportDisplaySets: [],
enhancedDisplaySets: [],
};
}
const allDisplaySets = displaySetService.getActiveDisplaySets();
const otherDisplaySets = allDisplaySets.filter(
@@ -52,7 +60,7 @@ export function getEnhancedDisplaySets({ viewportId, services }) {
displaySetService.getDisplaySetByUID(displaySetUID)
);
const backgroundCanBeVolume = csUtils.isValidVolume(viewportDisplaySets[0].imageIds);
const backgroundCanBeVolume = csUtils.isValidVolume(viewportDisplaySets[0].imageIds || []);
const backgroundDisplaySet = viewportDisplaySets[0];
const enhancedDisplaySets = otherDisplaySets.map(displaySet => {