feat: separate checks for addWindowLevelActionMenu and addSegmentationOverlay (#4813)

This commit is contained in:
Sofien-Sellami 2025-02-28 18:25:52 +01:00 committed by GitHub
parent 7d67834159
commit 7f0cc0f60e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 185 additions and 40 deletions

View File

@ -15,7 +15,6 @@ import type { Types } from '@ohif/core';
import OHIFViewportActionCorners from '../components/OHIFViewportActionCorners';
import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu';
import { useAppConfig } from '@state';
import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu';
import { getViewportPresentations } from '../utils/presentations/getViewportPresentations';
import { useSynchronizersStore } from '../stores/useSynchronizersStore';
@ -89,7 +88,6 @@ const OHIFCornerstoneViewport = React.memo(
const [scrollbarHeight, setScrollbarHeight] = useState('100px');
const [enabledVPElement, setEnabledVPElement] = useState(null);
const elementRef = useRef() as React.MutableRefObject<HTMLDivElement>;
const [appConfig] = useAppConfig();
const {
displaySetService,
@ -324,51 +322,52 @@ const OHIFCornerstoneViewport = React.memo(
// Set up the window level action menu in the viewport action corners.
useEffect(() => {
// Doing an === check here because the default config value when not set is true
if (appConfig.addWindowLevelActionMenu === false) {
return;
const windowLevelActionMenu = customizationService.getCustomization(
'viewportActionMenu.windowLevelActionMenu'
);
const segmentationOverlay = customizationService.getCustomization(
'viewportActionMenu.segmentationOverlay'
);
if (windowLevelActionMenu?.enabled) {
viewportActionCornersService.addComponent({
viewportId,
id: 'windowLevelActionMenu',
component: getWindowLevelActionMenu({
viewportId,
element: elementRef.current,
displaySets,
servicesManager,
commandsManager,
location: windowLevelActionMenu.location,
verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom,
horizontalDirection: AllInOneMenu.HorizontalDirection.RightToLeft,
}),
location: windowLevelActionMenu.location,
});
}
const location = viewportActionCornersService.LOCATIONS.topRight;
// TODO: In the future we should consider using the customization service
// to determine if and in which corner various action components should go.
viewportActionCornersService.addComponent({
viewportId,
id: 'windowLevelActionMenu',
component: getWindowLevelActionMenu({
if (segmentationOverlay?.enabled) {
viewportActionCornersService.addComponent({
viewportId,
element: elementRef.current,
displaySets,
servicesManager,
commandsManager,
location,
verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom,
horizontalDirection: AllInOneMenu.HorizontalDirection.RightToLeft,
}),
location,
});
viewportActionCornersService.addComponent({
viewportId,
id: 'segmentation',
component: getViewportDataOverlaySettingsMenu({
viewportId,
element: elementRef.current,
displaySets,
servicesManager,
commandsManager,
location,
}),
location,
});
id: 'segmentation',
component: getViewportDataOverlaySettingsMenu({
viewportId,
element: elementRef.current,
displaySets,
servicesManager,
commandsManager,
location: segmentationOverlay.location,
}),
location: segmentationOverlay.location,
});
}
}, [
displaySets,
viewportId,
viewportActionCornersService,
servicesManager,
commandsManager,
appConfig,
]);
const { ref: resizeRef } = useResizeDetector({

View File

@ -0,0 +1,12 @@
import viewportActionCornersService from '../services/ViewportActionCornersService/ViewportActionCornersService';
export default {
'viewportActionMenu.windowLevelActionMenu': {
enabled: true,
location: viewportActionCornersService.LOCATIONS.topRight,
},
'viewportActionMenu.segmentationOverlay': {
enabled: true,
location: viewportActionCornersService.LOCATIONS.topRight,
},
};

View File

@ -9,6 +9,7 @@ import colorbarCustomization from './customizations/colorbarCustomization';
import windowLevelPresetsCustomization from './customizations/windowLevelPresetsCustomization';
import miscCustomization from './customizations/miscCustomization';
import windowLevelActionMenuCustomization from './customizations/windowLevelActionMenuCustomization';
import viewportActionMenuCustomizations from './customizations/viewportActionMenuCustomizations';
function getCustomizationModule({ commandsManager, servicesManager }) {
return [
@ -26,6 +27,7 @@ function getCustomizationModule({ commandsManager, servicesManager }) {
...windowLevelPresetsCustomization,
...miscCustomization,
...windowLevelActionMenuCustomization,
...viewportActionMenuCustomizations,
},
},
];

View File

@ -129,7 +129,6 @@ declare global {
showStudyList?: boolean;
whiteLabeling?: Record<string, unknown>;
httpErrorHandler?: (error: Error) => void;
addWindowLevelActionMenu?: boolean;
dangerouslyUseDynamicConfig?: {
enabled: boolean;
regex: RegExp;

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

View File

@ -207,6 +207,86 @@ customizationService.setCustomizations({
},
});
```
```
**Example: Customizing viewport action Menu**
**Before (v3.9):**
```javascript
// In your configuration for global customizations
window.config = {
// rest of config
addWindowLevelActionMenu: true,
};
```
**After (v3.10):**
```javascript
// you can now handle each action menu item (windowLevelActionMenu and segmentationOverlay) separately
// cornerstone extension getCustomizationModule
function getCustomizationModule() {
return [
{
name: 'default',
value: {
'viewportActionMenu.windowLevelActionMenu': {
enabled: true,
location: viewportActionCornersService.LOCATIONS.topRight,
},
'viewportActionMenu.segmentationOverlay': {
enabled: true,
location: viewportActionCornersService.LOCATIONS.topRight,
},
},
},
];
}
// Accessing customizations within your component (e.g., OHIFCornerstoneViewport.tsx)
const windowLevelActionMenu = customizationService.getCustomization('viewportActionMenu.windowLevelActionMenu');
const segmentationOverlay = customizationService.getCustomization('viewportActionMenu.segmentationOverlay');
// Modifying customizations at runtime, for example, in your mode's onModeEnter
customizationService.setCustomizations({
'viewportActionMenu.windowLevelActionMenu': {
$set: {
enabled: false,
location: viewportActionCornersService.LOCATIONS.bottomLeft,
},
},
'viewportActionMenu.segmentationOverlay': {
$set: {
enabled: true,
location: viewportActionCornersService.LOCATIONS.topLeft,
},
},
});
// Alternatively, setting global customizations via configuration
window.config = {
// rest of config
customizationService: [
{
'viewportActionMenu.windowLevelActionMenu': {
$set: {
enabled: false,
location: 1,
},
},
'viewportActionMenu.segmentationOverlay': {
$set: {
enabled: true,
location: 1,
},
},
},
],
// rest of config
};
```
**Note:**
@ -237,3 +317,5 @@ To keep our customization system consistent, you should be aware of a few key re
| `cornerstoneOverlayTopRight` | `viewportOverlay.topRight` | Custom overlay items for the top-right corner of the viewport. |
| `cornerstoneOverlayBottomLeft` | `viewportOverlay.bottomLeft` | Custom overlay items for the bottom-left corner of the viewport. |
| `cornerstoneOverlayBottomRight` | `viewportOverlay.bottomRight` | Custom overlay items for the bottom-right corner of the viewport. |
| (New) | `viewportActionMenu.windowLevelActionMenu` | Controls the display and the location of the window level action menu in the viewport. |
| (New) | `viewportActionMenu.segmentationOverlay` | Controls the display and the location of segmentation overlays in the viewport. |

View File

@ -16,6 +16,7 @@ import loadingIndicatorProgress from '../../../assets/img/loading-indicator-icon
import loadingIndicatorPercent from '../../../assets/img/loading-indicator-percent.png';
import viewportActionCorners from '../../../assets/img/viewport-action-corners.png';
import contextMenu from '../../../assets/img/context-menu.jpg';
import segmentationOverlay from '../../../assets/img/segmentation-overlay.png';
import segDisplayEditingTrue from '../../../assets/img/segDisplayEditingTrue.png';
import segDisplayEditingFalse from '../../../assets/img/segDisplayEditingFalse.png';
@ -784,6 +785,57 @@ window.config = {
};
`,
},
{
id: 'viewportActionMenu.windowLevelActionMenu',
description:
'Configures the display and location of the window level action menu in the viewport.',
image: windowLevelActionMenu,
default: null,
configuration: `
window.config = {
// rest of window config
customizationService: [
{
'viewportActionMenu.windowLevelActionMenu': {
$set: {
enabled: true,
location: 1, // Set the location of the menu in the viewport.
// 0: topLeft
// 1: topRight
// 2: bottomLeft
// 3: bottomRight
}
},
},
],
};
`,
},
{
id: 'viewportActionMenu.segmentationOverlay',
description: 'Configures the display and location of the segmentation overlay in the viewport.',
image: segmentationOverlay,
default: null,
configuration: `
window.config = {
// rest of window config
customizationService: [
{
'viewportActionMenu.segmentationOverlay': {
$set: {
enabled: true,
location: 1, // Set the location of the overlay in the viewport.
// 0: topLeft
// 1: topRight
// 2: bottomLeft
// 3: bottomRight
}
},
},
],
};
`,
},
];
export const segmentationCustomizations = [

View File

@ -192,7 +192,6 @@ if auth headers are used, a preflight request is required.
- `allowMultiSelectExport`: (default to false), if set to true, the user will be able to select the datasource to export the report to.
- `activateViewportBeforeInteraction`: (default to true), if set to false, tools can be used directly without the need to click and activate the viewport.
- `autoPlayCine`: (default to false), if set to true, data sets with the DICOM frame time tag (i.e. (0018,1063)) will auto play when displayed
- `addWindowLevelActionMenu`: (default to true), if set to false, the window level action menu item is NOT added to the viewport action corners
- `dangerouslyUseDynamicConfig`: Dynamic config allows user to pass `configUrl` query string. This allows to load config without recompiling application. If the `configUrl` query string is passed, the worklist and modes will load from the referenced json rather than the default .env config. If there is no `configUrl` path provided, the default behaviour is used and there should not be any deviation from current user experience.<br/>
Points to consider while using `dangerouslyUseDynamicConfig`:<br/>
- User have to enable this feature by setting `dangerouslyUseDynamicConfig.enabled:true`. By default it is `false`.