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

@@ -87,7 +87,10 @@ interface DataRowProps {
description: string;
details?: { primary: string[]; secondary: string[] };
//
/** Primary selection: selected and in the active segmentation */
isSelected?: boolean;
/** Secondary selection: selected but in an inactive segmentation */
isSecondarySelected?: boolean;
onSelect?: (e) => void;
//
isVisible: boolean;
@@ -103,6 +106,7 @@ interface DataRowProps {
//
colorHex?: string;
onColor: (e) => void;
onCopy?: (e) => void;
className?: string;
children?: React.ReactNode;
}
@@ -121,7 +125,9 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
onRename,
onDelete,
onColor,
onCopy,
isSelected = false,
isSecondarySelected = false,
isVisible = true,
disableEditing = false,
className,
@@ -146,6 +152,9 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
case 'Rename':
onRename(e);
break;
case 'Copy':
onCopy?.(e);
break;
case 'Lock':
onToggleLocked(e);
break;
@@ -234,7 +243,11 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
onClick={onSelect}
data-cy="data-row"
>
{/* Hover Overlay */}
{/* Secondary Selection Tint (below hover, always visible when secondary-selected) */}
{isSecondarySelected && (
<div className="bg-primary/20 pointer-events-none absolute inset-0"></div>
)}
<div className="bg-primary/20 pointer-events-none absolute inset-0 opacity-0 transition-opacity group-hover:opacity-100"></div>
{/* Number Box */}
@@ -351,6 +364,17 @@ const DataRowComponent = React.forwardRef<HTMLDivElement, DataRowProps>(
Rename
</span>
</DropdownMenuItem>
{onCopy && (
<DropdownMenuItem onClick={e => handleAction('Copy', e)}>
<Icons.Copy className="text-foreground" />
<span
className="pl-2"
data-cy="Duplicate"
>
Duplicate
</span>
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={e => handleAction('Delete', e)}>
<Icons.Delete className="text-foreground" />
<span
@@ -146,6 +146,8 @@ import {
ToolExpand,
ToolClickSegment,
ToolSegmentLabel,
ToolSculptor,
ToolLabelmapEditWithContour,
} from './Sources/Tools';
import ActionNewDialog from './Sources/ActionNewDialog';
import NotificationInfo from './Sources/NotificationInfo';
@@ -228,6 +230,7 @@ import ArrowRight from './Sources/ArrowRight';
import ChevronLeft from './Sources/ChevronLeft';
import StatusAlert from './Sources/StatusAlert';
import Undo from './Sources/Undo';
import TabContours from './Sources/TabContours';
//
//
type IconProps = React.HTMLAttributes<SVGElement>;
@@ -674,6 +677,8 @@ export const Icons = {
'status-tracked': (props: IconProps) => StatusTracking(props),
'status-untracked': (props: IconProps) => StatusUntracked(props),
'status-locked': (props: IconProps) => StatusLocked(props),
'tab-contours': (props: IconProps) => TabContours(props),
TabContours: (props: IconProps) => TabContours(props),
'tab-segmentation': (props: IconProps) => TabSegmentation(props),
'tab-studies': (props: IconProps) => TabStudies(props),
'tab-linear': (props: IconProps) => TabLinear(props),
@@ -720,6 +725,7 @@ export const Icons = {
'tool-referenceLines': (props: IconProps) => ToolReferenceLines(props),
'tool-reset': (props: IconProps) => ToolReset(props),
'tool-rotate-right': (props: IconProps) => ToolRotateRight(props),
'icon-tool-sculptor': (props: IconProps) => ToolSculptor(props),
'tool-seg-brush': (props: IconProps) => ToolSegBrush(props),
'tool-seg-eraser': (props: IconProps) => ToolSegEraser(props),
'tool-seg-shape': (props: IconProps) => ToolSegShape(props),
@@ -775,6 +781,7 @@ export const Icons = {
'old-trash': (props: IconProps) => Trash(props),
'tool-point': (props: IconProps) => ToolCircle(props),
'tool-freehand-line': (props: IconProps) => ToolFreehand(props),
'tool-labelmap-edit-with-contour': (props: IconProps) => ToolLabelmapEditWithContour(props),
'actions-smooth': (props: IconProps) => ActionsSmooth(props),
'actions-simplify': (props: IconProps) => ActionsSimplify(props),
'actions-combine': (props: IconProps) => ActionsCombine(props),
@@ -0,0 +1,63 @@
import React from 'react';
import type { IconProps } from '../types';
export const TabContours = (props: IconProps) => (
<svg
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
{...props}
>
<path
d="M11.9168 1.5H10.2502C10.02 1.5 9.8335 1.68655 9.8335 1.91667V3.58333C9.8335 3.81345 10.02 4 10.2502 4H11.9168C12.1469 4 12.3335 3.81345 12.3335 3.58333V1.91667C12.3335 1.68655 12.1469 1.5 11.9168 1.5Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11.9168 18.1667H10.2502C10.02 18.1667 9.8335 18.3532 9.8335 18.5833V20.25C9.8335 20.4801 10.02 20.6667 10.2502 20.6667H11.9168C12.1469 20.6667 12.3335 20.4801 12.3335 20.25V18.5833C12.3335 18.3532 12.1469 18.1667 11.9168 18.1667Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M20.2503 9.83333H18.5837C18.3535 9.83333 18.167 10.0199 18.167 10.25V11.9167C18.167 12.1468 18.3535 12.3333 18.5837 12.3333H20.2503C20.4804 12.3333 20.667 12.1468 20.667 11.9167V10.25C20.667 10.0199 20.4804 9.83333 20.2503 9.83333Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.58333 9.83333H1.91667C1.68655 9.83333 1.5 10.0199 1.5 10.25V11.9167C1.5 12.1468 1.68655 12.3333 1.91667 12.3333H3.58333C3.81345 12.3333 4 12.1468 4 11.9167V10.25C4 10.0199 3.81345 9.83333 3.58333 9.83333Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M2.84277 9.83332C3.11056 8.0751 3.93334 6.44851 5.19101 5.191C6.44867 3.93349 8.07536 3.1109 9.83361 2.84332"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M9.83361 19.3242C8.07524 19.0565 6.44846 18.2338 5.19078 16.9762C3.93311 15.7185 3.1104 14.0917 2.84277 12.3333"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M19.3243 12.3333C19.0568 14.0916 18.2341 15.7184 16.9764 16.9759C15.7187 18.2335 14.0918 19.056 12.3335 19.3233"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M12.3335 2.84332C14.0917 3.1109 15.7184 3.93349 16.9761 5.191C18.2338 6.44851 19.0565 8.0751 19.3243 9.83332"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
export default TabContours;
@@ -2995,16 +2995,16 @@ export const ToolSegmentLabel = (props: IconProps) => (
<path
d="M2.6636 14.3578C2.58841 14.4105 2.53094 14.4847 2.49878 14.5707C2.46661 14.6567 2.46127 14.7504 2.48345 14.8395C2.50564 14.9286 2.5543 15.0088 2.62302 15.0697C2.69175 15.1305 2.77731 15.1691 2.86842 15.1804L7.42867 16.5722L8.82125 21.1316C8.83209 21.2229 8.87047 21.3088 8.93131 21.3779C8.99214 21.4469 9.07254 21.4958 9.16181 21.518C9.25109 21.5402 9.34502 21.5348 9.43111 21.5023C9.5172 21.4699 9.59139 21.412 9.6438 21.3364L20.4127 10.5683C20.7607 10.1801 20.9661 9.68484 20.9951 9.16423L21 3.82256C21 3.6044 20.9133 3.39518 20.7591 3.24092C20.6048 3.08666 20.3956 3 20.1774 3H14.8382C14.3176 3.02823 13.8223 3.2334 13.4341 3.58155L2.6636 14.3578Z"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="1.2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16.8882 8.75787C15.9796 8.75787 15.2431 8.02133 15.2431 7.11276C15.2431 6.20419 15.9796 5.46765 16.8882 5.46765C17.7968 5.46765 18.5333 6.20419 18.5333 7.11276C18.5333 8.02133 17.7968 8.75787 16.8882 8.75787Z"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="1.2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
@@ -3544,3 +3544,57 @@ export const ToolContract = (props: IconProps) => (
/>
</svg>
);
export const ToolSculptor = (props: IconProps) => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<path
d="M16.5 1.5C19.2614 1.5 21.5 3.73858 21.5 6.5C21.5 8.59844 20.2065 10.3931 18.374 11.1348C18.4555 11.5776 18.5 12.0336 18.5 12.5C18.5 16.6421 15.1421 20 11 20C10.6311 20 10.4428 19.5684 10.6035 19.2363C10.8575 18.7113 11 18.1224 11 17.5C11 15.2909 9.20914 13.5 7 13.5C6.07751 13.5 5.22759 13.8124 4.55078 14.3369C4.24235 14.5757 3.75506 14.4797 3.67188 14.0986C3.55945 13.5836 3.5 13.0488 3.5 12.5C3.5 8.35786 6.85786 5 11 5C11.2421 5 11.4815 5.01272 11.7178 5.03516C12.3439 2.98844 14.2482 1.5 16.5 1.5ZM16.4707 3.53125C16.1133 3.53125 15.8906 3.7832 15.8906 4.14648V6.07422H14.0684C13.7109 6.07422 13.4648 6.29688 13.4648 6.64844C13.4648 7 13.7109 7.2168 14.0684 7.2168H15.8906V9.13867C15.8906 9.50195 16.1133 9.75391 16.4707 9.75391C16.8223 9.75391 17.0449 9.50195 17.0449 9.13867V7.2168H18.8613C19.2188 7.2168 19.4707 7 19.4707 6.64844C19.4707 6.29688 19.2188 6.07422 18.8613 6.07422H17.0449V4.14648C17.0449 3.7832 16.8223 3.53125 16.4707 3.53125Z"
fill="currentColor"
/>
<path
d="M5.69531 18.1406C5.34375 18.1406 5.08594 17.9238 5.08594 17.5664C5.08594 17.2148 5.34375 16.998 5.69531 16.998H8.50195C8.85352 16.998 9.11719 17.2148 9.11719 17.5664C9.11719 17.9238 8.85352 18.1406 8.50195 18.1406H5.69531Z"
fill="currentColor"
/>
<circle
cx="7"
cy="17.5"
r="4.5"
stroke="currentColor"
strokeLinecap="round"
/>
</svg>
);
export const ToolLabelmapEditWithContour = (props: IconProps) => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<path
d="M10.3945 12.6113C10.043 12.6113 9.78516 12.3945 9.78516 12.0371C9.78516 11.6855 10.043 11.4688 10.3945 11.4688H13.2012C13.5527 11.4688 13.8164 11.6855 13.8164 12.0371C13.8164 12.3945 13.5527 12.6113 13.2012 12.6113H10.3945Z"
fill="currentColor"
/>
<path
opacity="0.5"
d="M18.312 6.84473C21.8473 8.88583 22.9225 13.6424 20.7134 17.4687C18.5042 21.2951 13.8473 22.7422 10.312 20.7011C9.51661 20.2419 8.84737 19.6439 8.31042 18.9512C11.3012 19.3598 14.4827 17.8251 16.1946 14.8598C17.9067 11.8945 17.6457 8.3712 15.7963 5.98538C16.6645 6.10409 17.5167 6.38556 18.312 6.84473Z"
fill="currentColor"
/>
<circle
cx="13.7852"
cy="13.4688"
r="8"
stroke="currentColor"
/>
<path
d="M4.50586 8.22266C4.14844 8.22266 3.92578 7.9707 3.92578 7.60742V5.68555H2.10352C1.74609 5.68555 1.5 5.46875 1.5 5.11719C1.5 4.76562 1.74609 4.54297 2.10352 4.54297H3.92578V2.61523C3.92578 2.25195 4.14844 2 4.50586 2C4.85742 2 5.08008 2.25195 5.08008 2.61523V4.54297H6.89648C7.25391 4.54297 7.50586 4.76562 7.50586 5.11719C7.50586 5.46875 7.25391 5.68555 6.89648 5.68555H5.08008V7.60742C5.08008 7.9707 4.85742 8.22266 4.50586 8.22266Z"
fill="currentColor"
/>
</svg>
);
@@ -1,8 +1,12 @@
import React from 'react';
import React, { useState } from 'react';
import RowInputRange from './RowInputRange';
import RowSegmentedControl from './RowSegmentedControl';
import RowDoubleRange from './RowDoubleRange';
import { Button } from '../Button';
import { Checkbox } from '../Checkbox/Checkbox';
import { Label } from '../Label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../Select';
import { Switch } from '../Switch';
const SETTING_TYPES = {
RANGE: 'range',
@@ -10,6 +14,9 @@ const SETTING_TYPES = {
CUSTOM: 'custom',
DOUBLE_RANGE: 'double-range',
BUTTON: 'button',
CHECKBOX: 'checkbox',
SWITCH: 'switch',
SELECT: 'select',
};
function ToolSettings({ options }) {
@@ -18,7 +25,8 @@ function ToolSettings({ options }) {
}
if (typeof options === 'function') {
return options();
const OptionsComponent = options;
return <OptionsComponent />;
}
return (
@@ -39,6 +47,12 @@ function ToolSettings({ options }) {
return renderCustomSetting(option);
case SETTING_TYPES.BUTTON:
return renderButtonSetting(option);
case SETTING_TYPES.CHECKBOX:
return renderCheckboxSetting(option);
case SETTING_TYPES.SWITCH:
return renderSwitchSetting(option);
case SETTING_TYPES.SELECT:
return renderSelectSetting(option);
default:
return null;
}
@@ -116,4 +130,74 @@ const renderButtonSetting = option => {
);
};
const renderCheckboxSetting = option => {
return (
<div
key={option.id}
className="flex items-center gap-2"
>
<Checkbox
id={option.id}
checked={option.value}
onCheckedChange={checked => {
option.onChange?.(checked);
}}
/>
<Label
htmlFor={option.id}
className="cursor-pointer"
>
{option.name}
</Label>
</div>
);
};
const renderSwitchSetting = option => {
return (
<div
key={option.id}
className="flex items-center gap-2"
>
<Switch
id={option.id}
checked={option.value}
onCheckedChange={checked => {
option.onChange?.(checked);
}}
/>
<Label
htmlFor={option.id}
className="cursor-pointer"
>
{option.name}
</Label>
</div>
);
};
const renderSelectSetting = option => {
return (
<Select
key={option.id}
onValueChange={value => option.onChange?.(value)}
value={option.value}
>
<SelectTrigger className="w-full overflow-hidden">
<SelectValue />
</SelectTrigger>
<SelectContent>
{option.values.map(value => (
<SelectItem
key={value.id}
value={value.id}
>
{value.label}
</SelectItem>
))}
</SelectContent>
</Select>
);
};
export default ToolSettings;
@@ -8,12 +8,21 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
}) => {
const { t } = useTranslation('SegmentationTable');
const { onSegmentationAdd, data, disableEditing, mode, disabled } =
useSegmentationTableContext('AddSegmentationRow');
const {
onSegmentationAdd,
data,
disableEditing,
mode,
disabled,
segmentationRepresentationType,
} = useSegmentationTableContext('AddSegmentationRow');
const isEmpty = data.length === 0;
// Check if we have at least one segmentation of the representation type for the panel this component is contained in.
const hasRepresentationType =
(!segmentationRepresentationType && data.length > 0) ||
data.some(info => segmentationRepresentationType === info.representation?.type);
if (!isEmpty && mode === 'collapsed') {
if (hasRepresentationType && mode === 'collapsed') {
return null;
}
@@ -25,7 +34,9 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
<div
data-cy="addSegmentation"
className={`group ${disabled ? 'pointer-events-none cursor-not-allowed opacity-70' : ''}`}
onClick={() => !disabled && onSegmentationAdd('')}
onClick={() =>
!disabled && onSegmentationAdd({ segmentationId: '', segmentationRepresentationType })
}
>
{children}
<div className="text-primary group-hover:bg-secondary-dark flex items-center rounded-[4px] pl-1 group-hover:cursor-pointer">
@@ -33,7 +44,7 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
{disabled ? <Icons.Info /> : <Icons.Add />}
</div>
<span className="text-[13px]">
{t(`${disabled ? 'Segmentation Not Supported' : 'Add Segmentation'}`)}
{t(disabled ? 'Segmentation not supported' : 'Add segmentation')}
</span>
</div>
</div>
@@ -1,6 +1,10 @@
import React from 'react';
import { PanelSection } from '../PanelSection';
import { useSegmentationTableContext, SegmentationExpandedProvider } from './contexts';
import {
useSegmentationTableContext,
SegmentationExpandedProvider,
useSegmentationExpanded,
} from './contexts';
import { useTranslation } from 'react-i18next';
import {
Button,
@@ -46,23 +50,31 @@ const SegmentationCollapsedDropdownMenu = ({ children }: { children: React.React
// Selector component - for the segmentation selection dropdown
const SegmentationCollapsedSelector = () => {
const { t } = useTranslation('SegmentationTable.HeaderCollapsed');
const { data, activeSegmentationId, onSegmentationClick } = useSegmentationTableContext(
const { data, onSegmentationClick, segmentationRepresentationType } = useSegmentationTableContext(
'SegmentationCollapsedSelector'
);
const { segmentation } = useSegmentationExpanded('SegmentationCollapsedSelector');
if (!data?.length) {
return null;
}
const segmentations = data.map(seg => ({
id: seg.segmentation.segmentationId,
label: seg.segmentation.label,
}));
const segmentations = data
// Only show segmentations of the representation type for this panel. Show all segmentations if no type is specified.
.filter(
seg =>
!segmentationRepresentationType ||
segmentationRepresentationType === seg.representation.type
)
.map(seg => ({
id: seg.segmentation.segmentationId,
label: seg.segmentation.label,
}));
return (
<Select
onValueChange={value => onSegmentationClick(value)}
value={activeSegmentationId}
value={segmentation?.segmentationId}
>
<SelectTrigger className="w-full overflow-hidden">
<SelectValue placeholder={t('Select a segmentation')} />
@@ -120,28 +132,33 @@ const SegmentationCollapsedContent = ({ children }: { children: React.ReactNode
const SegmentationCollapsedRoot: React.FC<{ children?: React.ReactNode }> = ({
children = null,
}) => {
const { mode, data, activeSegmentationId } = useSegmentationTableContext('SegmentationCollapsed');
const { mode, data, segmentationRepresentationType, selectedSegmentationIdForType } =
useSegmentationTableContext('SegmentationCollapsed');
// Check if we should render based on mode
if (mode !== 'collapsed' || !data || data.length === 0) {
return null;
}
// Find active segmentation
const activeSegmentationInfo = data.find(
info => info.segmentation.segmentationId === activeSegmentationId
// Find the segmentations for the representation type for this collapsed view.
const segmentations = data.filter(
segmentation =>
!segmentationRepresentationType ||
segmentationRepresentationType === segmentation.representation?.type
);
if (!activeSegmentationInfo) {
// Check if we should render.
if (mode !== 'collapsed' || !data || data.length === 0 || segmentations.length === 0) {
return null;
}
// Find the selected segmentation info for the representation type, or default to the first one.
const selectedSegmentationInfo =
segmentations.find(
segmentation => segmentation.segmentation.segmentationId === selectedSegmentationIdForType
) ?? segmentations[0];
return (
<div className="space-y-0">
<PanelSection className="mb-0">
<SegmentationExpandedProvider
segmentation={activeSegmentationInfo.segmentation}
representation={activeSegmentationInfo.representation}
segmentation={selectedSegmentationInfo.segmentation}
representation={selectedSegmentationInfo.representation}
isActive={true}
onSegmentationClick={() => {}} // No-op since it's already the active one
>
@@ -93,7 +93,7 @@ const SegmentationExpandedContent = ({ children }: { children: React.ReactNode }
// Main compound component
const SegmentationExpandedRoot = ({ children }) => {
const { data, activeSegmentationId, onSegmentationClick, mode } =
const { data, activeSegmentationId, onSegmentationClick, mode, segmentationRepresentationType } =
useSegmentationTableContext('SegmentationExpanded');
const { ref: scrollableContainerRef, maxHeight } = useDynamicMaxHeight(data);
@@ -113,25 +113,31 @@ const SegmentationExpandedRoot = ({ children }) => {
style={{ maxHeight: maxHeight }}
className={`space-y-0 pl-0.5`}
>
{data.map(segmentationInfo => {
const isActive = segmentationInfo.segmentation.segmentationId === activeSegmentationId;
{data
.filter(
segmentationInfo =>
!segmentationRepresentationType ||
segmentationInfo.representation.type === segmentationRepresentationType
)
.map(segmentationInfo => {
const isActive = segmentationInfo.segmentation.segmentationId === activeSegmentationId;
return (
<PanelSection
key={segmentationInfo.segmentation.segmentationId}
className=""
>
<SegmentationExpandedProvider
segmentation={segmentationInfo.segmentation}
representation={segmentationInfo.representation}
isActive={isActive}
onSegmentationClick={onSegmentationClick}
return (
<PanelSection
key={segmentationInfo.segmentation.segmentationId}
className=""
>
{children}
</SegmentationExpandedProvider>
</PanelSection>
);
})}
<SegmentationExpandedProvider
segmentation={segmentationInfo.segmentation}
representation={segmentationInfo.representation}
isActive={isActive}
onSegmentationClick={onSegmentationClick}
>
{children}
</SegmentationExpandedProvider>
</PanelSection>
);
})}
</div>
</ScrollArea>
);
@@ -15,6 +15,7 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
onSegmentClick,
onSegmentEdit,
onSegmentDelete,
onSegmentCopy,
data,
showSegmentIndex = true,
} = useSegmentationTableContext('SegmentationSegments');
@@ -116,6 +117,9 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
const { locked, active, label, displayText } = segmentFromSegmentation;
const cssColor = `rgb(${color[0]},${color[1]},${color[2]})`;
// Secondary selection: segment is active, but its parent segmentation is inactive
const isSecondarySelected = active && !isActiveSegmentation;
const hasStats = segmentFromSegmentation.cachedStats?.namedStats;
const segmentRowRef = (element: HTMLElement) => {
@@ -139,7 +143,10 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
// details={displayText}
description={displayText}
colorHex={cssColor}
isSelected={active}
// Primary selection only when part of the active segmentation
isSelected={active && isActiveSegmentation}
// Secondary selection tint when selected in an inactive segmentation
isSecondarySelected={isSecondarySelected}
isVisible={visible}
isLocked={locked}
disableEditing={disableEditing}
@@ -158,6 +165,11 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
onSelect={() => onSegmentClick(segmentation.segmentationId, segmentIndex)}
onRename={() => onSegmentEdit(segmentation.segmentationId, segmentIndex)}
onDelete={() => onSegmentDelete(segmentation.segmentationId, segmentIndex)}
onCopy={
onSegmentCopy
? () => onSegmentCopy(segmentation.segmentationId, segmentIndex)
: undefined
}
/>
);
@@ -65,6 +65,13 @@ export interface SegmentationTableContextType {
showSegmentIndex?: boolean;
renderInactiveSegmentations?: boolean;
// The type segmentations displayed/filtered in this table. If undefined, show all types.
segmentationRepresentationType?: string;
// The (last) selected segmentation ID for the representation type above.
// If the type above is undefined, then it will store the last active segmentation ID.
selectedSegmentationIdForType?: string;
// Function handlers
setShowConfig?: (show: boolean) => void;
setRenderFill?: ({ type }: { type: string }, value: boolean) => void;
@@ -73,12 +80,19 @@ export interface SegmentationTableContextType {
setFillAlpha?: ({ type }: { type: string }, value: number) => void;
setFillAlphaInactive?: ({ type }: { type: string }, value: number) => void;
toggleRenderInactiveSegmentations?: () => void;
onSegmentationAdd?: (segmentationId: string) => void;
onSegmentationAdd?: ({
segmentationId,
segmentationRepresentationType,
}: {
segmentationId: string;
segmentationRepresentationType: string;
}) => void;
onSegmentationClick?: (segmentationId: string) => void;
onSegmentationDelete?: (segmentationId: string) => void;
onSegmentAdd?: (segmentationId: string) => void;
onSegmentClick?: (segmentationId: string, segmentIndex: number) => void;
onSegmentEdit?: (segmentationId: string, segmentIndex: number) => void;
onSegmentCopy?: (segmentationId: string, segmentIndex: number) => void;
onSegmentationEdit?: (segmentationId: string) => void;
onSegmentColorClick?: (segmentationId: string, segmentIndex: number) => void;
onSegmentDelete?: (segmentationId: string, segmentIndex: number) => void;