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

+1 -1
View File
@@ -53,7 +53,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^3.12.2",
"@cornerstonejs/dicom-image-loader": "^3.15.1",
"@emotion/serialize": "^1.1.3",
"@ohif/core": "3.11.0-beta.32",
"@ohif/extension-cornerstone": "3.11.0-beta.32",
+2 -3
View File
@@ -13,6 +13,7 @@ import {
HotkeysManager,
ServiceProvidersManager,
SystemContextProvider,
ViewportRefsProvider,
} from '@ohif/core';
import {
ThemeWrapper as ThemeWrapperNext,
@@ -26,7 +27,6 @@ import {
ModalProvider,
ViewportDialogProvider,
UserAuthenticationProvider,
ViewportActionCornersProvider,
} from '@ohif/ui-next';
// Viewer Project
// TODO: Should this influence study list?
@@ -105,7 +105,6 @@ function App({
userAuthenticationService,
uiNotificationService,
customizationService,
viewportActionCornersService,
} = servicesManager.services;
const providers = [
@@ -114,6 +113,7 @@ function App({
[I18nextProvider, { i18n }],
[ThemeWrapperNext],
[SystemContextProvider, { commandsManager, extensionManager, hotkeysManager, servicesManager }],
[ViewportRefsProvider],
[ViewportGridProvider, { service: viewportGridService }],
[ViewportDialogProvider, { service: uiViewportDialogService }],
[CineProvider, { service: cineService }],
@@ -121,7 +121,6 @@ function App({
[TooltipProvider],
[DialogProvider, { service: uiDialogService, dialog: ManagedDialog }],
[ModalProvider, { service: uiModalService, modal: ModalNext }],
[ViewportActionCornersProvider, { service: viewportActionCornersService }],
[ShepherdJourneyProvider],
];
+1 -9
View File
@@ -4,13 +4,11 @@ import { ViewportGrid, ViewportPane } from '@ohif/ui-next';
import { useViewportGrid } from '@ohif/ui-next';
import EmptyViewport from './EmptyViewport';
import { useAppConfig } from '@state';
import { useViewportActionCornersWithGrid } from '../hooks';
function ViewerViewportGrid(props: withAppTypes) {
const { servicesManager, viewportComponents = [], dataSource, commandsManager } = props;
const [viewportGrid, viewportGridService] = useViewportGrid();
const [appConfig] = useAppConfig();
const { initializeViewportCorners } = useViewportActionCornersWithGrid();
const { layout, activeViewportId, viewports, isHangingProtocolLayout } = viewportGrid;
const { numCols, numRows } = layout;
@@ -419,12 +417,6 @@ function ViewerViewportGrid(props: withAppTypes) {
isHangingProtocolLayout={isHangingProtocolLayout}
onElementEnabled={evt => {
viewportGridService.setViewportIsReady(viewportId, true);
// Initialize the viewport action corners for this viewport
if (evt?.detail?.element) {
const elementRef = { current: evt.detail.element };
initializeViewportCorners(viewportId, elementRef, displaySets, commandsManager);
}
}}
/>
</div>
@@ -433,7 +425,7 @@ function ViewerViewportGrid(props: withAppTypes) {
}
return viewportPanes;
}, [viewports, activeViewportId, viewportComponents, dataSource, initializeViewportCorners]);
}, [viewports, activeViewportId, viewportComponents, dataSource]);
/**
* Loading indicator until numCols and numRows are gotten from the HangingProtocolService
+1 -2
View File
@@ -1,5 +1,4 @@
import useDebounce from './useDebounce';
import useSearchParams from './useSearchParams';
import useViewportActionCornersWithGrid from './useViewportActionCornersWithGrid';
export { useDebounce, useSearchParams, useViewportActionCornersWithGrid };
export { useDebounce, useSearchParams };
+1 -2
View File
@@ -1,5 +1,4 @@
import useViewportActionCornersWithGrid from './useViewportActionCornersWithGrid';
import useDebounce from './useDebounce';
import useSearchParams from './useSearchParams';
export { useViewportActionCornersWithGrid, useDebounce, useSearchParams };
export { useDebounce, useSearchParams };
@@ -1,112 +0,0 @@
import { useCallback, MutableRefObject, useRef } from 'react';
import { useViewportActionCorners, ViewportActionCornersLocations } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
/**
* Hook that manages viewport action corner components for all viewports in the grid
* @returns A function that can be called to initialize action corners for a viewport
*/
export default function useViewportActionCornersWithGrid() {
const [, api] = useViewportActionCorners();
const { servicesManager } = useSystem();
const { customizationService } = servicesManager.services;
// Keep a ref to track processed viewports to avoid duplicates
const processedViewports = useRef<Set<string>>(new Set());
// Map of customization keys to their corresponding enum values
const locationMap = {
'viewportActionMenu.topLeft': ViewportActionCornersLocations.topLeft,
'viewportActionMenu.topRight': ViewportActionCornersLocations.topRight,
'viewportActionMenu.bottomLeft': ViewportActionCornersLocations.bottomLeft,
'viewportActionMenu.bottomRight': ViewportActionCornersLocations.bottomRight,
};
// Function to process customizations for a viewport
const initializeViewportCorners = useCallback(
(
viewportId: string,
elementRef: MutableRefObject<HTMLDivElement>,
displaySets: any[],
commandsManager: any
) => {
if (!viewportId || !elementRef?.current) {
return;
}
// Prevent duplicate processing
if (processedViewports.current.has(viewportId)) {
return;
}
// Mark this viewport as processed
processedViewports.current.add(viewportId);
// Clear any existing components for this viewport
api.clear(viewportId);
// Process each location
Object.entries(locationMap).forEach(([locationKey, locationValue]) => {
const items = customizationService.getCustomization(locationKey);
if (!items || !items.length) {
return;
}
items.forEach(item => {
try {
if (typeof item.component === 'function') {
// Use the component renderer provided directly in the item
const component = item.component({
viewportId,
element: elementRef.current,
displaySets,
location: locationValue,
commandsManager,
});
if (component) {
api.addComponent({
viewportId,
id: item.id,
component,
location: locationValue,
indexPriority: item.indexPriority,
});
}
} else if (item.component) {
// Handle static components
api.addComponent({
viewportId,
id: item.id,
component: item.component,
location: locationValue,
indexPriority: item.indexPriority,
});
}
} catch (error) {
console.error(`Error adding component ${item.id} to viewport corner:`, error);
}
});
});
},
[api, customizationService]
);
// Cleanup function for unmounting viewports
const cleanupViewportCorners = useCallback(
(viewportId: string) => {
if (!viewportId) {
return;
}
// Remove from processed set
processedViewports.current.delete(viewportId);
// Clear from the store
api.clear(viewportId);
},
[api]
);
return { initializeViewportCorners, cleanupViewportCorners };
}
+2 -2
View File
@@ -83,10 +83,10 @@ export default function ModeRoute({
extensionManager.setActiveDataSource(dataSourceName);
}
const dataSource = extensionManager.getActiveDataSource()[0];
const dataSource = extensionManager.getActiveDataSourceOrNull();
// Only handling one route per mode for now
const route = mode.routes[0];
const route = mode.routes?.[0] ?? null;
useEffect(() => {
const loadExtensions = async () => {