From 397fd856539cd3b949a9614a9ea32d0d04a90000 Mon Sep 17 00:00:00 2001 From: Alireza Date: Thu, 28 Nov 2024 12:41:08 -0500 Subject: [PATCH] feat(segmentation): Enhance dropdown menu functionality in SegmentationTable (#4553) --- .../src/panels/PanelSegmentation.tsx | 81 ++++++++++++- extensions/default/src/commandsModule.ts | 8 ++ modes/longitudinal/src/customizations.tsx | 38 ++++++ .../SegmentationExpanded.tsx | 6 +- .../SegmentationTable/SegmentationHeader.tsx | 108 +++--------------- .../SegmentationSelectorHeader.tsx | 79 +++---------- 6 files changed, 153 insertions(+), 167 deletions(-) diff --git a/extensions/cornerstone/src/panels/PanelSegmentation.tsx b/extensions/cornerstone/src/panels/PanelSegmentation.tsx index 03109fc5c..8298d4e44 100644 --- a/extensions/cornerstone/src/panels/PanelSegmentation.tsx +++ b/extensions/cornerstone/src/panels/PanelSegmentation.tsx @@ -1,5 +1,16 @@ import React from 'react'; -import { SegmentationTable } from '@ohif/ui-next'; +import { + DropdownMenuLabel, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuPortal, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + Icons, + SegmentationTable, + DropdownMenuSubTrigger, +} from '@ohif/ui-next'; import { useActiveViewportSegmentationRepresentations } from '../hooks/useActiveViewportSegmentationRepresentations'; import { metaData } from '@cornerstonejs/core'; @@ -147,6 +158,66 @@ export default function PanelSegmentation({ } ); + const CustomDropdownMenuContent = customizationService.getCustomComponent( + 'PanelSegmentation.CustomDropdownMenuContent', + ({ + activeSegmentation, + onSegmentationAdd, + onSegmentationRemoveFromViewport, + onSegmentationEdit, + onSegmentationDelete, + allowExport, + storeSegmentation, + onSegmentationDownload, + onSegmentationDownloadRTSS, + t, + }) => ( + + onSegmentationAdd(activeSegmentation.id)}> + + {t('Create New Segmentation')} + + + {t('Manage Current Segmentation')} + onSegmentationRemoveFromViewport(activeSegmentation.id)}> + + {t('Remove from Viewport')} + + onSegmentationEdit(activeSegmentation.id)}> + + {t('Rename')} + + + + + {t('Export & Download')} + + + + storeSegmentation(activeSegmentation.id)}> + {t('Export DICOM SEG')} + + onSegmentationDownload(activeSegmentation.id)}> + {t('Download DICOM SEG')} + + onSegmentationDownloadRTSS(activeSegmentation.id)}> + {t('Download DICOM RTSTRUCT')} + + + + + + onSegmentationDelete(activeSegmentation.id)}> + + {t('Delete')} + + + ) + ); + const exportOptions = segmentationsWithRepresentations.map(({ segmentation }) => { const { representationData, segmentationId } = segmentation; const { Labelmap } = representationData; @@ -219,13 +290,17 @@ export default function PanelSegmentation({ {SegmentationTableMode === 'collapsed' ? ( - + + + ) : ( - + + + {/* */} diff --git a/extensions/default/src/commandsModule.ts b/extensions/default/src/commandsModule.ts index 6e128e726..8916911af 100644 --- a/extensions/default/src/commandsModule.ts +++ b/extensions/default/src/commandsModule.ts @@ -124,6 +124,14 @@ const commandsModule = ({ * @param options.stageId - the stageId to apply * @param options.stageIndex - the index of the stage to go to. * @param options.reset - flag to indicate if the HP should be reset to its original and not restored to a previous state + * + * commandsManager.run('setHangingProtocol', { + * activeStudyUID: '1.2.3', + * protocolId: 'myProtocol', + * stageId: 'myStage', + * stageIndex: 0, + * reset: false, + * }); */ setHangingProtocol: ({ activeStudyUID = '', diff --git a/modes/longitudinal/src/customizations.tsx b/modes/longitudinal/src/customizations.tsx index 649a8bd46..09f59d9ac 100644 --- a/modes/longitudinal/src/customizations.tsx +++ b/modes/longitudinal/src/customizations.tsx @@ -1,3 +1,16 @@ +import React from 'react'; +import { + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuPortal, + DropdownMenuLabel, + Icons, +} from '@ohif/ui-next'; + export const performCustomizations = customizationService => { // Set the custom SegmentationTable customizationService.addModeCustomizations([ @@ -22,5 +35,30 @@ export const performCustomizations = customizationService => { // { value: 'Toe', label: 'Toe' }, // ], // }, + /** + * Custom Dropdown Menu Item + */ + // { + // id: 'PanelSegmentation.CustomDropdownMenuContent', + // content: ({ + // activeSegmentation, + // onSegmentationAdd, + // onSegmentationRemoveFromViewport, + // onSegmentationEdit, + // onSegmentationDelete, + // allowExport, + // storeSegmentation, + // onSegmentationDownload, + // onSegmentationDownloadRTSS, + // t, + // }) => ( + // + // onSegmentationDelete(activeSegmentation.id)}> + // + // {t('My Custom Dropdown Menu Item')} + // + // + // ), + // }, ]); }; diff --git a/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx b/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx index 02b0cc728..fa1499291 100644 --- a/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx +++ b/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx @@ -26,10 +26,7 @@ export const SegmentationExpanded: React.FC<{ children?: React.ReactNode }> = ({ representation: segmentationInfo.representation, }) ) : ( - + )} @@ -38,7 +35,6 @@ export const SegmentationExpanded: React.FC<{ children?: React.ReactNode }> = ({ React.isValidElement(child) ? React.cloneElement(child, { segmentation: segmentationInfo.segmentation, - representation: segmentationInfo.representation, }) : child )} diff --git a/platform/ui-next/src/components/SegmentationTable/SegmentationHeader.tsx b/platform/ui-next/src/components/SegmentationTable/SegmentationHeader.tsx index 8cb6d4932..97663d05b 100644 --- a/platform/ui-next/src/components/SegmentationTable/SegmentationHeader.tsx +++ b/platform/ui-next/src/components/SegmentationTable/SegmentationHeader.tsx @@ -1,39 +1,31 @@ import React from 'react'; import { Button } from '../Button'; import { Icons } from '../Icons/Icons'; -import { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuSub, - DropdownMenuSubTrigger, - DropdownMenuSubContent, - DropdownMenuPortal, -} from '../DropdownMenu'; +import { DropdownMenu, DropdownMenuTrigger } from '../DropdownMenu'; import { Tooltip, TooltipTrigger, TooltipContent } from '../Tooltip/Tooltip'; import { useSegmentationTableContext } from './SegmentationTableContext'; import { useTranslation } from 'react-i18next'; export const SegmentationHeader: React.FC<{ + children?: React.ReactNode; segmentation?: any; -}> = ({ segmentation }) => { +}> = ({ children, segmentation }) => { const { t } = useTranslation('SegmentationTable'); - const { - onSegmentAdd, - onSegmentationRemoveFromViewport, - onSegmentationEdit, - onSegmentationDelete, - onSegmentationDownload, - onSegmentationDownloadRTSS, - storeSegmentation, - } = useSegmentationTableContext('SegmentationHeader'); + const { ...contextProps } = useSegmentationTableContext('SegmentationHeader'); if (!segmentation) { return null; } + const childrenWithProps = React.Children.map(children, child => + React.isValidElement(child) + ? React.cloneElement(child, { + t, + ...contextProps, + }) + : child + ); + return (
@@ -48,79 +40,7 @@ export const SegmentationHeader: React.FC<{ - - { - e.stopPropagation(); - onSegmentAdd(segmentation.segmentationId); - }} - > - - {t('Add Segment')} - - - { - e.stopPropagation(); - onSegmentationRemoveFromViewport(segmentation.segmentationId); - }} - > - - {t('Remove from Viewport')} - - { - e.stopPropagation(); - onSegmentationEdit(segmentation.segmentationId); - }} - > - - {t('Rename')} - - e.stopPropagation()}> - - {t('Hide or Show all Segments')} - - - e.stopPropagation()}> - - {t('Export & Download')} - - - - { - e.stopPropagation(); - storeSegmentation(segmentation.segmentationId); - }} - > - {t('Export DICOM SEG')} - - { - e.stopPropagation(); - onSegmentationDownload(segmentation.segmentationId); - }} - > - {t('Download DICOM SEG')} - - { - e.stopPropagation(); - onSegmentationDownloadRTSS(segmentation.segmentationId); - }} - > - {t('Download DICOM RTSTRUCT')} - - - - - - onSegmentationDelete(segmentation.segmentationId)}> - - {t('Delete')} - - + {childrenWithProps}
{segmentation.label}
diff --git a/platform/ui-next/src/components/SegmentationTable/SegmentationSelectorHeader.tsx b/platform/ui-next/src/components/SegmentationTable/SegmentationSelectorHeader.tsx index 63967af36..1d8ca93e3 100644 --- a/platform/ui-next/src/components/SegmentationTable/SegmentationSelectorHeader.tsx +++ b/platform/ui-next/src/components/SegmentationTable/SegmentationSelectorHeader.tsx @@ -4,17 +4,9 @@ import { Button, DropdownMenu, DropdownMenuTrigger, - DropdownMenuItem, - DropdownMenuContent, - DropdownMenuSeparator, - DropdownMenuSub, - DropdownMenuPortal, - DropdownMenuSubContent, - DropdownMenuSubTrigger, Tooltip, TooltipTrigger, TooltipContent, - DropdownMenuLabel, } from '../../components'; import { useTranslation } from 'react-i18next'; @@ -26,20 +18,8 @@ export const SegmentationSelectorHeader: React.FC<{ children?: React.ReactNode } }) => { const { t } = useTranslation('SegmentationTable.HeaderCollapsed'); - const { - data, - activeSegmentationId, - mode, - onSegmentationClick, - onSegmentationAdd, - onSegmentationRemoveFromViewport, - onSegmentationEdit, - onSegmentationDelete, - onSegmentationDownload, - onSegmentationDownloadRTSS, - storeSegmentation, - exportOptions, - } = useSegmentationTableContext('SegmentationTable.HeaderCollapsed'); + const { data, activeSegmentationId, mode, onSegmentationClick, exportOptions, ...contextProps } = + useSegmentationTableContext('SegmentationTable.HeaderCollapsed'); if (mode !== 'collapsed' || !data?.length) { return null; @@ -65,6 +45,17 @@ export const SegmentationSelectorHeader: React.FC<{ children?: React.ReactNode } ({ segmentationId }) => segmentationId === activeSegmentation.id )?.isExportable; + const childrenWithProps = React.Children.map(children, child => + React.isValidElement(child) + ? React.cloneElement(child, { + activeSegmentation, + allowExport, + t, + ...contextProps, + }) + : child + ); + return (
@@ -76,49 +67,7 @@ export const SegmentationSelectorHeader: React.FC<{ children?: React.ReactNode } - - onSegmentationAdd(activeSegmentation.id)}> - - {t('Create New Segmentation')} - - - {t('Manage Current Segmentation')} - onSegmentationRemoveFromViewport(activeSegmentation.id)}> - - {t('Remove from Viewport')} - - onSegmentationEdit(activeSegmentation.id)}> - - {t('Rename')} - - - - - {t('Export & Download')} - - - - storeSegmentation(activeSegmentation.id)}> - {t('Export DICOM SEG')} - - onSegmentationDownload(activeSegmentation.id)}> - {t('Download DICOM SEG')} - - onSegmentationDownloadRTSS(activeSegmentation.id)}> - {t('Download DICOM RTSTRUCT')} - - - - - - onSegmentationDelete(activeSegmentation.id)}> - - {t('Delete')} - - + {childrenWithProps}