feature(segmentation): Add contour tools and utilities in a side panel (#5517)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-10-29 14:15:52 -04:00
1 parent ab440f086a
commit 35bfc9b0cc
73 files changed
+2770 -435

No files matched your search

+2
View File
@@ -3,3 +3,5 @@ export * from './types';
export * from './useViewportRef';
export * from './useViewportSize';
export * from './useViewportMousePosition';
export * from './useActiveToolOptions';
export * from './useRunCommand';
@@ -0,0 +1,53 @@
import { useSystem } from '../contextProviders/SystemProvider';
import { ButtonProps } from '../services/ToolBarService/types';
import { useToolbar } from './useToolbar';
type UseActiveToolOptionsProps = {
buttonSectionId: string;
};
export function useActiveToolOptions({ buttonSectionId }: UseActiveToolOptionsProps) {
const { servicesManager } = useSystem();
const { toolbarService } = servicesManager.services;
const { toolbarButtons } = useToolbar({
buttonSection: buttonSectionId,
});
// Helper to check a list of buttons for an active tool.
const findActiveOptions = (buttons: any[]): unknown => {
for (const tool of buttons) {
if (tool.componentProps.isActive) {
return {
activeToolOptions: tool.componentProps.options,
activeToolButtonId: tool.componentProps.id,
};
}
if (tool.componentProps.buttonSection) {
const nestedButtons = toolbarService.getButtonPropsInButtonSection(
tool.componentProps.buttonSection
) as ButtonProps[];
const activeNested = nestedButtons.find(nested => nested.isActive);
if (activeNested) {
return {
activeToolOptions: activeNested.options,
activeToolButtonId: activeNested.id,
};
}
}
}
return null;
};
// Look for active tool options across all sections.
const activeOptions = toolbarButtons.reduce((activeOptions, button) => {
if (activeOptions) {
return activeOptions;
}
const sectionId = button.componentProps.buttonSection;
const buttons = sectionId ? toolbarService.getButtonSection(sectionId) : [button];
return findActiveOptions(buttons);
}, null);
return activeOptions ?? {};
}
+19
View File
@@ -0,0 +1,19 @@
import { useCallback } from 'react';
import { useSystem } from '../contextProviders/SystemProvider';
/**
* Hook that provides a runCommand function for executing commands
* @returns A memoized runCommand function
*/
export function useRunCommand() {
const { commandsManager } = useSystem();
const runCommand = useCallback(
(commandName: string, commandOptions: Record<string, unknown> = {}) => {
return commandsManager.runCommand(commandName, commandOptions);
},
[commandsManager]
);
return runCommand;
}
@@ -38,7 +38,10 @@ export const TOOLBAR_SECTIONS = {
},
// mode specific
segmentationToolbox: 'segmentationToolbox',
labelMapSegmentationToolbox: 'labelMapSegmentationToolbox',
contourSegmentationToolbox: 'contourSegmentationToolbox',
labelMapSegmentationUtilities: 'labelMapSegmentationUtilities',
contourSegmentationUtilities: 'contourSegmentationUtilities',
dynamicToolbox: 'dynamic-toolbox',
roiThresholdToolbox: 'ROIThresholdToolbox',
};
@@ -314,12 +317,22 @@ export default class ToolbarService extends PubSubService {
: undefined;
// Check hideWhenDisabled at both evaluateProps level and props level
const hideWhenDisabled = evaluateProps?.hideWhenDisabled || props.hideWhenDisabled;
// Visibility is first determined by the evaluate function. If it is not returned from there,
// then we check hideWhenDisabled and disabled to determine visibility.
const visible =
evaluated?.visible === undefined
? hideWhenDisabled && evaluated?.disabled
? false
: true
: evaluated?.visible;
const updatedProps = {
...props,
...evaluated,
disabled: evaluated?.disabled || false,
visible: hideWhenDisabled && evaluated?.disabled ? false : true,
className: evaluated?.className || '',
visible,
className: evaluated?.className || props?.className || '',
isActive: evaluated?.isActive, // isActive will be undefined for buttons without this prop
};
evaluationResults.set(button.id, updatedProps);
@@ -570,7 +583,8 @@ export default class ToolbarService extends PubSubService {
btn.component = buttonType.defaultComponent;
}
if (!buttonType) {
if (!buttonType && !btn.component) {
console.warn(`Neither button type nor a component found for button: ${id}`);
return;
}
@@ -34,7 +34,7 @@ export type EvaluateObject = {
export type ButtonOptions = {
id: string;
type: 'range' | 'radio' | 'double-range' | 'custom';
type: 'range' | 'radio' | 'double-range' | 'custom' | 'checkbox' | 'select' | 'button';
name?: string;
min?: number;
max?: number;
@@ -59,6 +59,7 @@ export type ButtonProps = {
listeners?: Record<string, RunCommand>;
options?: ButtonOptions[];
buttonSection?: string | boolean;
isActive?: boolean;
};
export type Button = {