diff --git a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx index 2052dd874..7421f233b 100644 --- a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx +++ b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { useImageViewer } from '@ohif/ui'; import { useViewportGrid } from '@ohif/ui-next'; import { StudyBrowser } from '@ohif/ui-next'; @@ -8,6 +8,7 @@ import { Separator } from '@ohif/ui-next'; import { PanelStudyBrowserHeader } from './PanelStudyBrowserHeader'; import { defaultActionIcons } from './constants'; import MoreDropdownMenu from '../../Components/MoreDropdownMenu'; +import { CallbackCustomization } from 'platform/core/src/types'; const { sortStudyInstances, formatDate, createStudyBrowserTabs } = utils; @@ -22,16 +23,14 @@ function PanelStudyBrowser({ dataSource, }) { const { servicesManager, commandsManager } = useSystem(); - const { hangingProtocolService, displaySetService, uiNotificationService, customizationService } = - servicesManager.services; + const { displaySetService, customizationService } = servicesManager.services; const navigate = useNavigate(); // Normally you nest the components so the tree isn't so deep, and the data // doesn't have to have such an intense shape. This works well enough for now. // Tabs --> Studies --> DisplaySets --> Thumbnails const { StudyInstanceUIDs } = useImageViewer(); - const [{ activeViewportId, viewports, isHangingProtocolLayout }, viewportGridService] = - useViewportGrid(); + const [{ activeViewportId, viewports, isHangingProtocolLayout }] = useViewportGrid(); const [activeTabName, setActiveTabName] = useState('all'); const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([ ...StudyInstanceUIDs, @@ -66,27 +65,33 @@ function PanelStudyBrowser({ setViewPresets(newViewPresets); }; - const onDoubleClickThumbnailHandler = displaySetInstanceUID => { - let updatedViewports = []; - const viewportId = activeViewportId; - try { - updatedViewports = hangingProtocolService.getViewportsRequireUpdate( - viewportId, - displaySetInstanceUID, - isHangingProtocolLayout - ); - } catch (error) { - console.warn(error); - uiNotificationService.show({ - title: 'Thumbnail Double Click', - message: 'The selected display sets could not be added to the viewport.', - type: 'error', - duration: 3000, - }); - } + const onDoubleClickThumbnailHandler = useCallback( + async displaySetInstanceUID => { + const customHandler = customizationService.getCustomization( + 'studyBrowser.thumbnailDoubleClickCallback' + ) as CallbackCustomization; - viewportGridService.setDisplaySetsForViewports(updatedViewports); - }; + const setupArgs = { + activeViewportId, + commandsManager, + servicesManager, + isHangingProtocolLayout, + }; + + const handlers = customHandler?.callbacks.map(callback => callback(setupArgs)); + + for (const handler of handlers) { + await handler(displaySetInstanceUID); + } + }, + [ + activeViewportId, + commandsManager, + servicesManager, + isHangingProtocolLayout, + customizationService, + ] + ); // ~~ studyDisplayList useEffect(() => { @@ -163,7 +168,7 @@ function PanelStudyBrowser({ thumbnailSrc = await displaySet.getThumbnailSrc(); } if (!thumbnailSrc) { - let thumbnailSrc = await getImageSrc(imageId); + const thumbnailSrc = await getImageSrc(imageId); displaySet.thumbnailSrc = thumbnailSrc; } newImageSrcEntry[dSet.displaySetInstanceUID] = thumbnailSrc; diff --git a/extensions/default/src/customizations/studyBrowserCustomization.ts b/extensions/default/src/customizations/studyBrowserCustomization.ts index 238cc2892..d171b79ad 100644 --- a/extensions/default/src/customizations/studyBrowserCustomization.ts +++ b/extensions/default/src/customizations/studyBrowserCustomization.ts @@ -44,4 +44,33 @@ export default { }, ], 'studyBrowser.studyMode': 'all', + 'studyBrowser.thumbnailDoubleClickCallback': { + callbacks: [ + ({ activeViewportId, servicesManager, isHangingProtocolLayout }) => + async displaySetInstanceUID => { + const { hangingProtocolService, viewportGridService, uiNotificationService } = + servicesManager.services; + let updatedViewports = []; + const viewportId = activeViewportId; + + try { + updatedViewports = hangingProtocolService.getViewportsRequireUpdate( + viewportId, + displaySetInstanceUID, + isHangingProtocolLayout + ); + } catch (error) { + console.warn(error); + uiNotificationService.show({ + title: 'Thumbnail Double Click', + message: 'The selected display sets could not be added to the viewport.', + type: 'error', + duration: 3000, + }); + } + + viewportGridService.setDisplaySetsForViewports(updatedViewports); + }, + ], + }, }; diff --git a/platform/core/src/services/CustomizationService/types.ts b/platform/core/src/services/CustomizationService/types.ts index 0a183cce3..653e4c642 100644 --- a/platform/core/src/services/CustomizationService/types.ts +++ b/platform/core/src/services/CustomizationService/types.ts @@ -27,13 +27,18 @@ export interface ComponentCustomization extends BaseCustomization { content: (...props: any) => React.JSX.Element; } +export interface CallbackCustomization extends BaseCustomization { + callbacks: Array<(...props: any) => any>; +} + export type Customization = | React.ComponentType | BaseCustomization | LabelCustomization | CommandCustomization | CodeCustomization - | ComponentCustomization; + | ComponentCustomization + | CallbackCustomization; export default Customization; diff --git a/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx b/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx index c2400eb19..6a8cefe73 100644 --- a/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx +++ b/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx @@ -1476,6 +1476,94 @@ window.config = { }, }, ], +}; + `, + }, + { + id: 'studyBrowser.thumbnailDoubleClickCallback', + description: + 'Defines the callback function for when the user double clicks a series on the study browser.', + default: `{ + callback: ({ + activeViewportId, + servicesManager, + isHangingProtocolLayout + }) => + async displaySetInstanceUID => { + const { + hangingProtocolService, + viewportGridService, + uiNotificationService + } = + servicesManager.services; + let updatedViewports = []; + const viewportId = activeViewportId; + + try { + updatedViewports = hangingProtocolService.getViewportsRequireUpdate( + viewportId, + displaySetInstanceUID, + isHangingProtocolLayout + ); + } catch (error) { + console.warn(error); + uiNotificationService.show({ + title: 'Thumbnail Double Click', + message: 'The selected display sets could not be added to the viewport.', + type: 'error', + duration: 3000, + }); + } + + viewportGridService.setDisplaySetsForViewports(updatedViewports); + }, +}`, + configuration: ` +window.config = { + // rest of window config + customizationService: [{ + 'studyBrowser.thumbnailDoubleClickCallback': { + callback: ({ + activeViewportId, + commandsManager, + servicesManager, + isHangingProtocolLayout + }) => + async displaySetInstanceUID => { + const { + hangingProtocolService, + viewportGridService, + uiNotificationService + } = + servicesManager.services; + + let updatedViewports = []; + const viewportId = activeViewportId; + + // Changing original function here: + if (isBlacklistedModality(displaySetInstanceUID)) { + return; + } + + try { + updatedViewports = hangingProtocolService.getViewportsRequireUpdate( + viewportId, + displaySetInstanceUID, + isHangingProtocolLayout + ); + } catch (error) { + console.warn(error); + uiNotificationService.show({ + title: 'Thumbnail Double Click', + message: 'The selected display sets could not be added to the viewport.', + type: 'error', + duration: 3000, + }); + } + viewportGridService.setDisplaySetsForViewports(updatedViewports); + }; + }, + }], }; `, },