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 OHIFViewportActionCorners from '../components/OHIFViewportActionCorners';
import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu'; import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu';
import { useAppConfig } from '@state';
import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu'; import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu';
import { getViewportPresentations } from '../utils/presentations/getViewportPresentations'; import { getViewportPresentations } from '../utils/presentations/getViewportPresentations';
import { useSynchronizersStore } from '../stores/useSynchronizersStore'; import { useSynchronizersStore } from '../stores/useSynchronizersStore';
@ -89,7 +88,6 @@ const OHIFCornerstoneViewport = React.memo(
const [scrollbarHeight, setScrollbarHeight] = useState('100px'); const [scrollbarHeight, setScrollbarHeight] = useState('100px');
const [enabledVPElement, setEnabledVPElement] = useState(null); const [enabledVPElement, setEnabledVPElement] = useState(null);
const elementRef = useRef() as React.MutableRefObject<HTMLDivElement>; const elementRef = useRef() as React.MutableRefObject<HTMLDivElement>;
const [appConfig] = useAppConfig();
const { const {
displaySetService, displaySetService,
@ -324,51 +322,52 @@ const OHIFCornerstoneViewport = React.memo(
// Set up the window level action menu in the viewport action corners. // Set up the window level action menu in the viewport action corners.
useEffect(() => { useEffect(() => {
// Doing an === check here because the default config value when not set is true const windowLevelActionMenu = customizationService.getCustomization(
if (appConfig.addWindowLevelActionMenu === false) { 'viewportActionMenu.windowLevelActionMenu'
return; );
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; if (segmentationOverlay?.enabled) {
viewportActionCornersService.addComponent({
// 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({
viewportId, viewportId,
element: elementRef.current, id: 'segmentation',
displaySets, component: getViewportDataOverlaySettingsMenu({
servicesManager, viewportId,
commandsManager, element: elementRef.current,
location, displaySets,
verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom, servicesManager,
horizontalDirection: AllInOneMenu.HorizontalDirection.RightToLeft, commandsManager,
}), location: segmentationOverlay.location,
location, }),
}); location: segmentationOverlay.location,
});
viewportActionCornersService.addComponent({ }
viewportId,
id: 'segmentation',
component: getViewportDataOverlaySettingsMenu({
viewportId,
element: elementRef.current,
displaySets,
servicesManager,
commandsManager,
location,
}),
location,
});
}, [ }, [
displaySets, displaySets,
viewportId, viewportId,
viewportActionCornersService, viewportActionCornersService,
servicesManager, servicesManager,
commandsManager, commandsManager,
appConfig,
]); ]);
const { ref: resizeRef } = useResizeDetector({ 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 windowLevelPresetsCustomization from './customizations/windowLevelPresetsCustomization';
import miscCustomization from './customizations/miscCustomization'; import miscCustomization from './customizations/miscCustomization';
import windowLevelActionMenuCustomization from './customizations/windowLevelActionMenuCustomization'; import windowLevelActionMenuCustomization from './customizations/windowLevelActionMenuCustomization';
import viewportActionMenuCustomizations from './customizations/viewportActionMenuCustomizations';
function getCustomizationModule({ commandsManager, servicesManager }) { function getCustomizationModule({ commandsManager, servicesManager }) {
return [ return [
@ -26,6 +27,7 @@ function getCustomizationModule({ commandsManager, servicesManager }) {
...windowLevelPresetsCustomization, ...windowLevelPresetsCustomization,
...miscCustomization, ...miscCustomization,
...windowLevelActionMenuCustomization, ...windowLevelActionMenuCustomization,
...viewportActionMenuCustomizations,
}, },
}, },
]; ];

View File

@ -129,7 +129,6 @@ declare global {
showStudyList?: boolean; showStudyList?: boolean;
whiteLabeling?: Record<string, unknown>; whiteLabeling?: Record<string, unknown>;
httpErrorHandler?: (error: Error) => void; httpErrorHandler?: (error: Error) => void;
addWindowLevelActionMenu?: boolean;
dangerouslyUseDynamicConfig?: { dangerouslyUseDynamicConfig?: {
enabled: boolean; enabled: boolean;
regex: RegExp; 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:** **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. | | `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. | | `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. | | `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 loadingIndicatorPercent from '../../../assets/img/loading-indicator-percent.png';
import viewportActionCorners from '../../../assets/img/viewport-action-corners.png'; import viewportActionCorners from '../../../assets/img/viewport-action-corners.png';
import contextMenu from '../../../assets/img/context-menu.jpg'; import contextMenu from '../../../assets/img/context-menu.jpg';
import segmentationOverlay from '../../../assets/img/segmentation-overlay.png';
import segDisplayEditingTrue from '../../../assets/img/segDisplayEditingTrue.png'; import segDisplayEditingTrue from '../../../assets/img/segDisplayEditingTrue.png';
import segDisplayEditingFalse from '../../../assets/img/segDisplayEditingFalse.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 = [ 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. - `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. - `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 - `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/> - `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/> Points to consider while using `dangerouslyUseDynamicConfig`:<br/>
- User have to enable this feature by setting `dangerouslyUseDynamicConfig.enabled:true`. By default it is `false`. - User have to enable this feature by setting `dangerouslyUseDynamicConfig.enabled:true`. By default it is `false`.