feat(segmentation mode): Add create, and export SEG with Brushes (#3632)
This commit is contained in:
1 parent
896866e9d0
commit
48bbd6281a
186 files changed
+6801
-2883
No files matched your search
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import Icon from '../Icon';
|
||||
|
||||
function AddSegmentRow({ onClick }) {
|
||||
return (
|
||||
<div
|
||||
className="flex hover:cursor-pointer"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="h-[28px] w-[28px]"></div>
|
||||
<div className="group ml-2.5 mt-1">
|
||||
<div className="text-primary-active group-hover:bg-secondary-dark flex items-center rounded-[4px] pr-2">
|
||||
<div className="grid h-[28px] w-[28px] place-items-center">
|
||||
<Icon name="icon-add" />
|
||||
</div>
|
||||
<span className="text-[13px]">Add Segment</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddSegmentRow;
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import Icon from '../Icon';
|
||||
|
||||
function NoSegmentationRow({ onSegmentationAdd }) {
|
||||
return (
|
||||
<div
|
||||
className="group"
|
||||
onClick={onSegmentationAdd}
|
||||
>
|
||||
<div className="text-primary-active group-hover:bg-secondary-dark flex items-center rounded-[4px] group-hover:cursor-pointer">
|
||||
<div className="grid h-[28px] w-[28px] place-items-center">
|
||||
<Icon name="icon-add" />
|
||||
</div>
|
||||
<span className="text-[13px]">Add Segmentation</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default NoSegmentationRow;
|
||||
@@ -1,10 +1,13 @@
|
||||
import React, { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Icon from '../Icon';
|
||||
import InputRange from '../InputRange';
|
||||
import CheckBox from '../CheckBox';
|
||||
import InputNumber from '../InputNumber';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const getRoundedValue = value => {
|
||||
return Math.round(value * 100) / 100;
|
||||
};
|
||||
|
||||
const ActiveSegmentationConfig = ({
|
||||
config,
|
||||
@@ -15,7 +18,7 @@ const ActiveSegmentationConfig = ({
|
||||
setFillAlpha,
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex justify-between px-2 pt-[13px] text-[12px]">
|
||||
<div className="flex justify-between px-3 pt-[13px] text-[12px]">
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="mb-[12px] text-white">Active</div>
|
||||
<CheckBox
|
||||
@@ -39,7 +42,7 @@ const ActiveSegmentationConfig = ({
|
||||
<InputRange
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
value={config.outlineOpacity * 100}
|
||||
value={getRoundedValue(config.outlineOpacity * 100)}
|
||||
onChange={setOutlineOpacityActive}
|
||||
step={1}
|
||||
containerClassName="mt-[4px] mb-[9px]"
|
||||
@@ -48,9 +51,9 @@ const ActiveSegmentationConfig = ({
|
||||
unit="%"
|
||||
/>
|
||||
<InputRange
|
||||
minValue={0}
|
||||
minValue={4}
|
||||
maxValue={100}
|
||||
value={config.fillAlpha * 100}
|
||||
value={getRoundedValue(config.fillAlpha * 100)}
|
||||
onChange={setFillAlpha}
|
||||
step={1}
|
||||
containerClassName="mt-[4px] mb-[9px]"
|
||||
@@ -80,11 +83,11 @@ const InactiveSegmentationConfig = ({
|
||||
setFillAlphaInactive,
|
||||
}) => {
|
||||
return (
|
||||
<div className="px-2">
|
||||
<div className="px-3">
|
||||
<CheckBox
|
||||
label="Display Inactive Segmentations"
|
||||
checked={config.renderInactiveSegmentations}
|
||||
labelClassName="text-[12px] pt-1"
|
||||
labelClassName="text-[12px]"
|
||||
className="mb-[9px]"
|
||||
onChange={setRenderInactiveSegmentations}
|
||||
/>
|
||||
@@ -94,7 +97,7 @@ const InactiveSegmentationConfig = ({
|
||||
<InputRange
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
value={config.fillAlphaInactive * 100}
|
||||
value={getRoundedValue(config.fillAlphaInactive * 100)}
|
||||
onChange={setFillAlphaInactive}
|
||||
step={1}
|
||||
containerClassName="mt-[4px]"
|
||||
@@ -120,51 +123,37 @@ const SegmentationConfig = ({
|
||||
const { initialConfig } = segmentationConfig;
|
||||
const [isMinimized, setIsMinimized] = useState(true);
|
||||
return (
|
||||
<div className="bg-primary-dark">
|
||||
<div
|
||||
className="flex cursor-pointer items-center"
|
||||
onClick={evt => {
|
||||
evt.stopPropagation();
|
||||
setIsMinimized(!isMinimized);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="panel-group-open-close"
|
||||
className={classNames('h-5 w-5 cursor-pointer text-white transition duration-300', {
|
||||
'rotate-90 transform': !isMinimized,
|
||||
})}
|
||||
<div className="bg-primary-dark select-none">
|
||||
<div>
|
||||
<ActiveSegmentationConfig
|
||||
config={initialConfig}
|
||||
setFillAlpha={setFillAlpha}
|
||||
setOutlineWidthActive={setOutlineWidthActive}
|
||||
setOutlineOpacityActive={setOutlineOpacityActive}
|
||||
setRenderFill={setRenderFill}
|
||||
setRenderOutline={setRenderOutline}
|
||||
/>
|
||||
<span className="text-[12px] font-[300] text-[#d8d8d8]">{'Segmentation Appearance'}</span>
|
||||
</div>
|
||||
{/* active segmentation */}
|
||||
{!isMinimized && (
|
||||
<div>
|
||||
<ActiveSegmentationConfig
|
||||
config={initialConfig}
|
||||
setFillAlpha={setFillAlpha}
|
||||
setOutlineWidthActive={setOutlineWidthActive}
|
||||
setOutlineOpacityActive={setOutlineOpacityActive}
|
||||
setRenderFill={setRenderFill}
|
||||
setRenderOutline={setRenderOutline}
|
||||
<div className="mx-1 mb-[8px] h-[1px] bg-[#212456]"></div>
|
||||
<div
|
||||
onClick={() => setIsMinimized(!isMinimized)}
|
||||
className="flex cursor-pointer items-center pl-2 pb-[9px]"
|
||||
>
|
||||
<Icon
|
||||
name="panel-group-open-close"
|
||||
className={classNames('h-5 w-5 cursor-pointer text-white transition duration-300', {
|
||||
'rotate-90 transform': !isMinimized,
|
||||
})}
|
||||
/>
|
||||
{/* A small line */}
|
||||
<div className="mx-1 mb-[8px] h-[1px] bg-[#212456]"></div>
|
||||
{/* inactive segmentation */}
|
||||
<div
|
||||
onClick={() => setIsMinimized(!isMinimized)}
|
||||
className="flex cursor-pointer items-center pl-2 pb-[9px]"
|
||||
>
|
||||
<span className="text-[12px] font-[300] text-[#d8d8d8]">
|
||||
{'Inactive Segmentations'}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-[12px] font-[300] text-[#d8d8d8]">{'Inactive Segmentations'}</span>
|
||||
</div>
|
||||
{!isMinimized && (
|
||||
<InactiveSegmentationConfig
|
||||
config={initialConfig}
|
||||
setRenderInactiveSegmentations={setRenderInactiveSegmentations}
|
||||
setFillAlphaInactive={setFillAlphaInactive}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
<div className="h-[6px] bg-black "></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import React from 'react';
|
||||
import { Select, Icon, Dropdown } from '../../components';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function SegmentationDropDownRow({
|
||||
segmentations = [],
|
||||
activeSegmentation,
|
||||
onActiveSegmentationChange,
|
||||
disableEditing,
|
||||
onToggleSegmentationVisibility,
|
||||
onSegmentationEdit,
|
||||
onSegmentationDownload,
|
||||
storeSegmentation,
|
||||
onSegmentationDelete,
|
||||
onSegmentationAdd,
|
||||
}) {
|
||||
const handleChange = option => {
|
||||
onActiveSegmentationChange(option.value); // Notify the parent
|
||||
};
|
||||
|
||||
const selectOptions = segmentations.map(s => ({
|
||||
value: s.id,
|
||||
label: s.label,
|
||||
}));
|
||||
|
||||
if (!activeSegmentation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="group mx-0.5 mt-[8px] flex items-center">
|
||||
<div
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Dropdown
|
||||
id="segmentation-dropdown"
|
||||
showDropdownIcon={false}
|
||||
alignment="left"
|
||||
itemsClassName="text-primary-active"
|
||||
showBorders={false}
|
||||
list={[
|
||||
...(!disableEditing
|
||||
? [
|
||||
{
|
||||
title: 'Add New Segmentation',
|
||||
onClick: () => {
|
||||
onSegmentationAdd();
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(!disableEditing
|
||||
? [
|
||||
{
|
||||
title: 'Rename',
|
||||
onClick: () => {
|
||||
onSegmentationEdit(activeSegmentation.id);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: 'Delete',
|
||||
onClick: () => {
|
||||
onSegmentationDelete(activeSegmentation.id);
|
||||
},
|
||||
},
|
||||
...(!disableEditing
|
||||
? [
|
||||
{
|
||||
title: 'Export DICOM SEG',
|
||||
onClick: () => {
|
||||
storeSegmentation(activeSegmentation.id);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Download',
|
||||
onClick: () => {
|
||||
onSegmentationDownload(activeSegmentation.id);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
>
|
||||
<div className="hover:bg-secondary-dark mx-1 grid h-[28px] w-[28px] cursor-pointer place-items-center rounded-[4px]">
|
||||
<Icon name="icon-more-menu"></Icon>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
{selectOptions?.length && (
|
||||
<Select
|
||||
id="segmentation-select"
|
||||
isClearable={false}
|
||||
onChange={handleChange}
|
||||
components={{
|
||||
DropdownIndicator: () => (
|
||||
<Icon
|
||||
name={'chevron-down-new'}
|
||||
className="mr-2"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
isSearchable={false}
|
||||
options={selectOptions}
|
||||
value={selectOptions?.find(o => o.value === activeSegmentation.id)}
|
||||
className="text-aqua-pale h-[26px] w-1/2 text-[13px]"
|
||||
/>
|
||||
)}
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="hover:bg-secondary-dark ml-3 mr-1 grid h-[28px] w-[28px] cursor-pointer place-items-center rounded-[4px]"
|
||||
onClick={() => onToggleSegmentationVisibility(activeSegmentation.id)}
|
||||
>
|
||||
{activeSegmentation.isVisible ? (
|
||||
<Icon
|
||||
name="row-shown"
|
||||
className="text-primary-active"
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
name="row-hidden"
|
||||
className="text-primary-active"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SegmentationDropDownRow.propTypes = {
|
||||
segmentations: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
activeSegmentation: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
isVisible: PropTypes.bool.isRequired,
|
||||
}),
|
||||
onActiveSegmentationChange: PropTypes.func.isRequired,
|
||||
disableEditing: PropTypes.bool,
|
||||
onToggleSegmentationVisibility: PropTypes.func,
|
||||
onSegmentationEdit: PropTypes.func,
|
||||
onSegmentationDownload: PropTypes.func,
|
||||
storeSegmentation: PropTypes.func,
|
||||
onSegmentationDelete: PropTypes.func,
|
||||
onSegmentationAdd: PropTypes.func,
|
||||
};
|
||||
|
||||
SegmentationDropDownRow.defaultProps = {
|
||||
segmentations: [],
|
||||
disableEditing: false,
|
||||
};
|
||||
|
||||
export default SegmentationDropDownRow;
|
||||
@@ -1,287 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import SegmentationGroupSegment from './SegmentationGroupSegment';
|
||||
import Dropdown from '../Dropdown';
|
||||
import classnames from 'classnames';
|
||||
import Icon from '../Icon';
|
||||
import IconButton from '../IconButton';
|
||||
|
||||
const AddNewSegmentRow = ({
|
||||
id,
|
||||
onConfigChange,
|
||||
onToggleSegmentationVisibility,
|
||||
onSegmentAdd,
|
||||
isVisible,
|
||||
showAddSegment,
|
||||
disableEditing,
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="text-primary-active flex cursor-pointer items-center bg-black py-1 pl-[29px] text-[12px] hover:opacity-80">
|
||||
{showAddSegment && !disableEditing && (
|
||||
<div
|
||||
className="flex items-center"
|
||||
onClick={() => onSegmentAdd()}
|
||||
>
|
||||
<Icon
|
||||
name="row-add"
|
||||
className="h-5 w-5"
|
||||
/>
|
||||
<div className="">Add Segment</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-grow" />
|
||||
<div
|
||||
className="flex items-center pr-2"
|
||||
onClick={() => {
|
||||
onToggleSegmentationVisibility(id);
|
||||
}}
|
||||
>
|
||||
{isVisible ? (
|
||||
<Icon
|
||||
name="row-show-all"
|
||||
className="h-5 w-5"
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
name="row-hide-all"
|
||||
className="h-5 w-5"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* {isSegmentationConfigOpen && (
|
||||
<SegmentationConfig onConfigChange={onConfigChange} />
|
||||
)} */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SegmentGroupHeader = ({
|
||||
isMinimized,
|
||||
onToggleMinimizeSegmentation,
|
||||
onSegmentationClick,
|
||||
id,
|
||||
label,
|
||||
isActive,
|
||||
segmentCount,
|
||||
onSegmentationEdit,
|
||||
disableEditing,
|
||||
onSegmentationDelete,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'flex-static border-secondary-light flex h-[27px] cursor-pointer items-center gap-2 rounded-t-md border-b pr-2 pl-[3px] text-[12px]',
|
||||
{
|
||||
'bg-secondary-main': isActive,
|
||||
'bg-secondary-dark': !isActive,
|
||||
}
|
||||
)}
|
||||
onClick={evt => {
|
||||
evt.stopPropagation();
|
||||
onSegmentationClick(id);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="panel-group-open-close"
|
||||
onClick={evt => {
|
||||
evt.stopPropagation();
|
||||
onToggleMinimizeSegmentation(id);
|
||||
}}
|
||||
className={classnames('h-5 w-5 cursor-pointer text-white transition duration-300', {
|
||||
'rotate-90 transform': !isMinimized,
|
||||
})}
|
||||
/>
|
||||
<span className="w-full overflow-hidden text-ellipsis whitespace-nowrap text-white">
|
||||
{label.toUpperCase()}
|
||||
</span>
|
||||
<div className="flex-grow" />
|
||||
<span className="text-white ">{segmentCount}</span>
|
||||
<div
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{!disableEditing && (
|
||||
<Dropdown
|
||||
id="segmentation-dropdown"
|
||||
showDropdownIcon={false}
|
||||
list={[
|
||||
{
|
||||
title: 'Rename',
|
||||
onClick: () => {
|
||||
onSegmentationEdit(id);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Delete',
|
||||
onClick: () => {
|
||||
onSegmentationDelete(id);
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<IconButton
|
||||
id={''}
|
||||
variant="text"
|
||||
color="inherit"
|
||||
size="initial"
|
||||
className="text-primary-active"
|
||||
>
|
||||
<Icon name="panel-group-more" />
|
||||
</IconButton>
|
||||
</Dropdown>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SegmentationGroup = ({
|
||||
label,
|
||||
id,
|
||||
segmentCount,
|
||||
isVisible,
|
||||
isActive,
|
||||
onSegmentClick,
|
||||
isMinimized,
|
||||
onSegmentColorClick,
|
||||
disableEditing,
|
||||
showAddSegment,
|
||||
segments,
|
||||
activeSegmentIndex,
|
||||
onSegmentAdd,
|
||||
onSegmentationClick,
|
||||
onClickSegmentColor,
|
||||
onSegmentationEdit,
|
||||
onToggleSegmentVisibility,
|
||||
onToggleSegmentationVisibility,
|
||||
onSegmentDelete,
|
||||
showSegmentDelete,
|
||||
onToggleMinimizeSegmentation,
|
||||
onSegmentationConfigChange,
|
||||
onSegmentationDelete,
|
||||
onSegmentEdit,
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex min-h-0 flex-auto flex-col">
|
||||
<SegmentGroupHeader
|
||||
id={id}
|
||||
label={label}
|
||||
isActive={isActive}
|
||||
isMinimized={isMinimized}
|
||||
onToggleMinimizeSegmentation={onToggleMinimizeSegmentation}
|
||||
onSegmentationClick={onSegmentationClick}
|
||||
segmentCount={segmentCount}
|
||||
onSegmentationEdit={onSegmentationEdit}
|
||||
onSegmentationDelete={onSegmentationDelete}
|
||||
disableEditing={disableEditing}
|
||||
/>
|
||||
{!isMinimized && (
|
||||
<div className="flex min-h-0 flex-auto flex-col">
|
||||
<AddNewSegmentRow
|
||||
onConfigChange={onSegmentationConfigChange}
|
||||
onSegmentAdd={onSegmentAdd}
|
||||
isVisible={isVisible}
|
||||
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
|
||||
id={id}
|
||||
disableEditing={disableEditing}
|
||||
showAddSegment={showAddSegment}
|
||||
/>
|
||||
<div className="ohif-scrollbar flex min-h-0 flex-col overflow-y-hidden">
|
||||
{!!segments.length &&
|
||||
segments.map(segment => {
|
||||
if (segment === undefined || segment === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { segmentIndex, color, label, isVisible, isLocked } = segment;
|
||||
return (
|
||||
<div
|
||||
className="mb-[1px]"
|
||||
key={segmentIndex}
|
||||
>
|
||||
<SegmentationGroupSegment
|
||||
segmentationId={id}
|
||||
segmentIndex={segmentIndex}
|
||||
label={label}
|
||||
color={color}
|
||||
isActive={activeSegmentIndex === segmentIndex}
|
||||
isLocked={isLocked}
|
||||
isVisible={isVisible}
|
||||
disableEditing={disableEditing}
|
||||
onClick={onSegmentClick}
|
||||
onEdit={onSegmentEdit}
|
||||
onDelete={onSegmentDelete}
|
||||
showSegmentDelete={showSegmentDelete}
|
||||
onColor={onSegmentColorClick}
|
||||
onToggleVisibility={onToggleSegmentVisibility}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
SegmentationGroup.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
segments: PropTypes.array.isRequired,
|
||||
segmentCount: PropTypes.number.isRequired,
|
||||
isVisible: PropTypes.bool.isRequired,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
isMinimized: PropTypes.bool.isRequired,
|
||||
activeSegmentIndex: PropTypes.number,
|
||||
onClickNewSegment: PropTypes.func.isRequired,
|
||||
onClickSegment: PropTypes.func.isRequired,
|
||||
onClickSegmentEdit: PropTypes.func.isRequired,
|
||||
onClickSegmentDelete: PropTypes.func.isRequired,
|
||||
onToggleSegmentLocked: PropTypes.func,
|
||||
onToggleSegmentVisibility: PropTypes.func.isRequired,
|
||||
onToggleSegmentationVisibility: PropTypes.func.isRequired,
|
||||
onSegmentClick: PropTypes.func.isRequired,
|
||||
showAddSegment: PropTypes.bool.isRequired,
|
||||
onSegmentAdd: PropTypes.func.isRequired,
|
||||
onSegmentationClick: PropTypes.func.isRequired,
|
||||
onClickSegmentColor: PropTypes.func.isRequired,
|
||||
onSegmentationEdit: PropTypes.func.isRequired,
|
||||
onSegmentDelete: PropTypes.func.isRequired,
|
||||
onToggleMinimizeSegmentation: PropTypes.func.isRequired,
|
||||
onSegmentationConfigChange: PropTypes.func.isRequired,
|
||||
onSegmentationDelete: PropTypes.func.isRequired,
|
||||
onSegmentEdit: PropTypes.func.isRequired,
|
||||
disableEditing: PropTypes.bool,
|
||||
};
|
||||
|
||||
SegmentationGroup.defaultProps = {
|
||||
label: '',
|
||||
segmentCount: 0,
|
||||
segments: [],
|
||||
isVisible: true,
|
||||
isMinimized: false,
|
||||
onClickNewSegment: () => {},
|
||||
onClickSegment: () => {},
|
||||
onClickSegmentEdit: () => {},
|
||||
onClickSegmentDelete: () => {},
|
||||
onToggleSegmentLocked: () => {},
|
||||
onToggleSegmentVisibility: () => {},
|
||||
onToggleSegmentationVisibility: () => {},
|
||||
onSegmentClick: () => {},
|
||||
showAddSegment: false,
|
||||
onSegmentAdd: () => {},
|
||||
onSegmentationClick: () => {},
|
||||
onClickSegmentColor: () => {},
|
||||
onSegmentationEdit: () => {},
|
||||
onSegmentDelete: () => {},
|
||||
onToggleMinimizeSegmentation: () => {},
|
||||
onSegmentationConfigChange: () => {},
|
||||
onSegmentationDelete: () => {},
|
||||
onSegmentEdit: () => {},
|
||||
};
|
||||
|
||||
export default SegmentationGroup;
|
||||
@@ -11,7 +11,7 @@ const SegmentItem = ({
|
||||
isActive,
|
||||
isVisible,
|
||||
color,
|
||||
showSegmentDelete,
|
||||
showDelete,
|
||||
disableEditing,
|
||||
isLocked = false,
|
||||
onClick,
|
||||
@@ -21,200 +21,168 @@ const SegmentItem = ({
|
||||
onToggleVisibility,
|
||||
onToggleLocked,
|
||||
}) => {
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
const [isSegmentIndexHovering, setIsSegmentIndexHovering] = useState(false);
|
||||
|
||||
const onMouseEnter = () => setIsHovering(true);
|
||||
const onMouseLeave = () => setIsHovering(false);
|
||||
const [isNumberBoxHovering, setIsNumberBoxHovering] = useState(false);
|
||||
|
||||
const cssColor = `rgb(${color[0]},${color[1]},${color[2]})`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'bg-primary-dark group relative flex cursor-pointer overflow-hidden border text-[12px] transition duration-300',
|
||||
{
|
||||
'border-primary-light rounded-[3px]': isActive,
|
||||
},
|
||||
{
|
||||
'border-transparent': !isActive,
|
||||
}
|
||||
)}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
className={classnames('text-aqua-pale group/row flex min-h-[28px] bg-black')}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onClick(segmentationId, segmentIndex);
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
data-cy={'segment-item'}
|
||||
>
|
||||
<div
|
||||
className={classnames(
|
||||
'flex w-[27px] items-center justify-center border-r border-r-black text-[12px]',
|
||||
{
|
||||
'bg-primary-light border-l-primary-light rounded-l-sm border-l text-black': isActive,
|
||||
'rounded-l-sm border-l border-l-[#1d3c58] bg-[#1d3c58]': !isActive && isHovering,
|
||||
'bg-primary-dark text-aqua-pale': !isActive && !isHovering && isVisible,
|
||||
'bg-[#140e2e] text-[#537594] opacity-60': !isActive && !isVisible,
|
||||
}
|
||||
)}
|
||||
onMouseEnter={() => setIsSegmentIndexHovering(true)}
|
||||
onMouseLeave={() => setIsSegmentIndexHovering(false)}
|
||||
className={classnames('bg-primary-dark group/number grid w-[32px] place-items-center', {
|
||||
'bg-primary-light border-primary-light rounded-l-[4px] border text-black': isActive,
|
||||
'border-primary-dark border': !isActive,
|
||||
})}
|
||||
onMouseEnter={() => setIsNumberBoxHovering(true)}
|
||||
onMouseLeave={() => setIsNumberBoxHovering(false)}
|
||||
>
|
||||
{isSegmentIndexHovering && showSegmentDelete ? (
|
||||
{isNumberBoxHovering && showDelete ? (
|
||||
<Icon
|
||||
name="close"
|
||||
className={classnames('pr-0.5')}
|
||||
className={classnames('h-[8px] w-[8px]', {
|
||||
'hover:cursor-pointer hover:opacity-60': !disableEditing,
|
||||
})}
|
||||
onClick={e => {
|
||||
if (disableEditing) {
|
||||
return;
|
||||
}
|
||||
e.stopPropagation();
|
||||
onDelete(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={classnames('flex items-center pr-2 ')}>{segmentIndex}</div>
|
||||
<div>{segmentIndex}</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
'relative flex w-full items-center justify-between border-r border-r-black py-1 pl-2 text-white ',
|
||||
{
|
||||
'bg-primary-dark text-primary-light': isActive,
|
||||
'bg-primary-dark text-aqua-pale': !isActive && isVisible,
|
||||
'bg-[#140e2e] text-[#3d5871] opacity-60': !isVisible,
|
||||
}
|
||||
)}
|
||||
className={classnames('relative flex w-full', {
|
||||
'border-primary-light bg-primary-dark rounded-r-[4px] border border-l-0': isActive,
|
||||
'border border-l-0 border-transparent': !isActive,
|
||||
})}
|
||||
>
|
||||
<div className={classnames('flex items-baseline gap-2')}>
|
||||
<div
|
||||
className={classnames('h-[8px] w-[8px] shrink-0 rounded-full')}
|
||||
style={{ backgroundColor: cssColor }}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onColor(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
<div className="">{label}</div>
|
||||
</div>
|
||||
{/* with faded background */}
|
||||
<div className="bg-black/15 absolute right-0 rounded-lg pr-[7px]">
|
||||
{!isVisible && !isHovering && (
|
||||
<div>
|
||||
<Icon
|
||||
name="row-hidden"
|
||||
className={classnames('h-5 w-5 text-[#3d5871] ')}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onToggleVisibility(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isHovering && (
|
||||
<div className={classnames('flex items-center')}>
|
||||
{!disableEditing && (
|
||||
<Icon
|
||||
name="row-edit"
|
||||
className={classnames('h-5 w-5', {
|
||||
'text-white': isLocked,
|
||||
'text-primary-light': !isLocked,
|
||||
})}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onEdit(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isVisible ? (
|
||||
<Icon
|
||||
name="row-hide"
|
||||
className={classnames('h-5 w-5', {
|
||||
'text-white': isLocked,
|
||||
'text-primary-light': !isLocked,
|
||||
})}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onToggleVisibility(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
name="row-unhide"
|
||||
className={classnames('h-5 w-5', {
|
||||
'text-white': isLocked,
|
||||
'text-primary-light': !isLocked,
|
||||
})}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onToggleVisibility(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="relative flex flex-col w-full border-t border-t-black">
|
||||
<div className="fl`ex items-center mb-1 ml-2">
|
||||
<div className="flex items-center flex-1 text-base text-primary-light">
|
||||
<div className="group-hover/row:bg-primary-dark flex h-full w-full flex-grow items-center">
|
||||
<div className="pl-2 pr-1.5">
|
||||
<div
|
||||
className={classnames(
|
||||
'w-3 h-3 mt-1 mr-2 rounded-full cursor-pointer transition duration-300 hover:opacity-80'
|
||||
)}
|
||||
className={classnames('h-[8px] w-[8px] grow-0 rounded-full', {
|
||||
'hover:cursor-pointer hover:opacity-60': !disableEditing,
|
||||
})}
|
||||
style={{ backgroundColor: cssColor }}
|
||||
onClick={e => {
|
||||
if (disableEditing) {
|
||||
return;
|
||||
}
|
||||
e.stopPropagation();
|
||||
onColor(segmentationId, segmentIndex);
|
||||
}}
|
||||
style={{ backgroundColor: cssColor }}
|
||||
></div>
|
||||
{label}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center w-1/3">
|
||||
<div className="px-1">
|
||||
<div className="flex items-center py-1 hover:cursor-pointer">{label}</div>
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
'absolute right-3 top-0 flex flex-row-reverse rounded-lg pt-[3px]',
|
||||
{}
|
||||
)}
|
||||
>
|
||||
<div className="group-hover/row:hidden">
|
||||
{!isVisible && (
|
||||
<Icon
|
||||
className={classnames(
|
||||
'text-white w-4 cursor-pointer transition duration-300 hover:opacity-80'
|
||||
)}
|
||||
name={isVisible ? 'eye-visible' : 'eye-hidden'}
|
||||
name="row-hidden"
|
||||
className="h-5 w-5 text-[#3d5871]"
|
||||
onClick={e => {
|
||||
// stopPropagation needed to avoid disable the current active item
|
||||
e.stopPropagation();
|
||||
onToggleVisibility(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{onToggleLocked !== undefined ? (
|
||||
<div className="px-1">
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Icon for 'row-lock' that shows when NOT hovering and 'isLocked' is true */}
|
||||
<div className="group-hover/row:hidden">
|
||||
{isLocked && (
|
||||
<div className="flex">
|
||||
<Icon
|
||||
className={classnames(
|
||||
'text-white w-4 cursor-pointer transition duration-300 hover:opacity-80'
|
||||
)}
|
||||
name={isLocked ? 'lock' : 'dotted-circle'}
|
||||
name="row-lock"
|
||||
className="h-5 w-5 text-[#3d5871]"
|
||||
onClick={e => {
|
||||
// stopPropagation needed to avoid disable the current active item
|
||||
e.stopPropagation();
|
||||
onToggleLocked(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="px-1">
|
||||
<Icon
|
||||
className={classnames(
|
||||
'text-white w-4 cursor-pointer transition duration-300 hover:opacity-80'
|
||||
|
||||
{/* This icon is visible when 'isVisible' is true */}
|
||||
{isVisible && (
|
||||
<Icon
|
||||
name="row-hidden"
|
||||
className="h-5 w-5 opacity-0"
|
||||
/>
|
||||
)}
|
||||
name={'pencil'}
|
||||
onClick={e => {
|
||||
// stopPropagation needed to avoid disable the current active item
|
||||
e.stopPropagation();
|
||||
onEdit(segmentationId, segmentIndex);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Icons that show only when hovering */}
|
||||
<div className="hidden group-hover/row:flex">
|
||||
<HoveringIcons
|
||||
onEdit={onEdit}
|
||||
isLocked={isLocked}
|
||||
isVisible={isVisible}
|
||||
onToggleLocked={onToggleLocked}
|
||||
onToggleVisibility={onToggleVisibility}
|
||||
segmentationId={segmentationId}
|
||||
segmentIndex={segmentIndex}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const HoveringIcons = ({
|
||||
onEdit,
|
||||
isLocked,
|
||||
isVisible,
|
||||
onToggleLocked,
|
||||
onToggleVisibility,
|
||||
segmentationId,
|
||||
segmentIndex,
|
||||
}) => {
|
||||
const iconClass = 'w-5 h-5 hover:cursor-pointer hover:opacity-60';
|
||||
|
||||
const handleIconClick = (e, action) => {
|
||||
e.stopPropagation();
|
||||
action(segmentationId, segmentIndex);
|
||||
};
|
||||
|
||||
const createIcon = (name, action, color = null) => (
|
||||
<Icon
|
||||
name={name}
|
||||
className={classnames(iconClass, color ?? 'text-white')}
|
||||
onClick={e => handleIconClick(e, action)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
{createIcon('row-edit', onEdit)}
|
||||
{createIcon(
|
||||
isLocked ? 'row-lock' : 'row-unlock',
|
||||
onToggleLocked,
|
||||
isLocked ? 'text-[#3d5871]' : null
|
||||
)}
|
||||
{createIcon(
|
||||
isVisible ? 'row-shown' : 'row-hidden',
|
||||
onToggleVisibility,
|
||||
!isVisible ? 'text-[#3d5871]' : null
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from '../Icon';
|
||||
import SegmentationGroup from './SegmentationGroup';
|
||||
import { PanelSection } from '../../components';
|
||||
import SegmentationConfig from './SegmentationConfig';
|
||||
import SegmentationDropDownRow from './SegmentationDropDownRow';
|
||||
import NoSegmentationRow from './NoSegmentationRow';
|
||||
import AddSegmentRow from './AddSegmentRow';
|
||||
import SegmentationGroupSegment from './SegmentationGroupSegment';
|
||||
|
||||
const SegmentationGroupTable = ({
|
||||
segmentations,
|
||||
// segmentation initial config
|
||||
segmentationConfig,
|
||||
// UI show/hide
|
||||
disableEditing,
|
||||
showAddSegmentation,
|
||||
showAddSegment,
|
||||
showDeleteSegment,
|
||||
// segmentation/segment handlers
|
||||
onSegmentationAdd,
|
||||
onSegmentationEdit,
|
||||
onSegmentationClick,
|
||||
onSegmentationDelete,
|
||||
showAddSegmentation,
|
||||
showAddSegment,
|
||||
onSegmentationDownload,
|
||||
storeSegmentation,
|
||||
// segment handlers
|
||||
onSegmentClick,
|
||||
onSegmentAdd,
|
||||
segmentationConfig,
|
||||
disableEditing,
|
||||
onSegmentDelete,
|
||||
onSegmentEdit,
|
||||
onToggleSegmentationVisibility,
|
||||
onToggleSegmentVisibility,
|
||||
onToggleSegmentLock,
|
||||
onSegmentColorClick,
|
||||
isMinimized,
|
||||
onToggleMinimizeSegmentation,
|
||||
// segmentation config handlers
|
||||
setFillAlpha,
|
||||
setFillAlphaInactive,
|
||||
setOutlineWidthActive,
|
||||
@@ -31,111 +41,191 @@ const SegmentationGroupTable = ({
|
||||
setRenderInactiveSegmentations,
|
||||
setRenderOutline,
|
||||
}) => {
|
||||
const [isConfigOpen, setIsConfigOpen] = useState(false);
|
||||
const [activeSegmentationId, setActiveSegmentationId] = useState(null);
|
||||
|
||||
const onActiveSegmentationChange = segmentationId => {
|
||||
onSegmentationClick(segmentationId);
|
||||
setActiveSegmentationId(segmentationId);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// find the first active segmentation to set
|
||||
let activeSegmentationIdToSet = segmentations?.find(segmentation => segmentation.isActive)?.id;
|
||||
|
||||
// If there is no active segmentation, set the first one to be active
|
||||
if (!activeSegmentationIdToSet && segmentations?.length > 0) {
|
||||
activeSegmentationIdToSet = segmentations[0].id;
|
||||
}
|
||||
|
||||
// If there is no segmentation, set the active segmentation to null
|
||||
if (segmentations?.length === 0) {
|
||||
activeSegmentationIdToSet = null;
|
||||
}
|
||||
|
||||
setActiveSegmentationId(activeSegmentationIdToSet);
|
||||
}, [segmentations]);
|
||||
|
||||
const activeSegmentation = segmentations?.find(
|
||||
segmentation => segmentation.id === activeSegmentationId
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="font-inter flex min-h-0 flex-col font-[300]">
|
||||
<SegmentationConfig
|
||||
setFillAlpha={setFillAlpha}
|
||||
setFillAlphaInactive={setFillAlphaInactive}
|
||||
setOutlineWidthActive={setOutlineWidthActive}
|
||||
setOutlineOpacityActive={setOutlineOpacityActive}
|
||||
setRenderFill={setRenderFill}
|
||||
setRenderInactiveSegmentations={setRenderInactiveSegmentations}
|
||||
setRenderOutline={setRenderOutline}
|
||||
segmentationConfig={segmentationConfig}
|
||||
/>
|
||||
<div className="mt-1 flex min-h-0 flex-col pr-[1px]">
|
||||
{!!segmentations.length &&
|
||||
segmentations.map((segmentation, index) => {
|
||||
const {
|
||||
id,
|
||||
label,
|
||||
displayText = [],
|
||||
segmentCount,
|
||||
segments,
|
||||
isVisible,
|
||||
isActive,
|
||||
activeSegmentIndex,
|
||||
} = segmentation;
|
||||
return (
|
||||
<SegmentationGroup
|
||||
id={id}
|
||||
key={id}
|
||||
label={label}
|
||||
disableEditing={disableEditing}
|
||||
isMinimized={isMinimized[id]}
|
||||
segments={segments}
|
||||
showAddSegment={showAddSegment}
|
||||
segmentCount={segmentCount}
|
||||
isActive={isActive}
|
||||
isVisible={isVisible}
|
||||
onSegmentColorClick={onSegmentColorClick}
|
||||
onSegmentationClick={() => onSegmentationClick(id)}
|
||||
activeSegmentIndex={activeSegmentIndex}
|
||||
onToggleMinimizeSegmentation={onToggleMinimizeSegmentation}
|
||||
onSegmentationEdit={onSegmentationEdit}
|
||||
onSegmentationDelete={onSegmentationDelete}
|
||||
onSegmentClick={onSegmentClick}
|
||||
onSegmentEdit={onSegmentEdit}
|
||||
onToggleSegmentVisibility={onToggleSegmentVisibility}
|
||||
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
|
||||
onSegmentAdd={onSegmentAdd}
|
||||
showSegmentDelete={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{showAddSegmentation && !disableEditing && (
|
||||
<div
|
||||
className="text-primary-active flex h-[45px] cursor-pointer items-center bg-black pl-1 text-[12px] hover:opacity-80"
|
||||
onClick={() => onSegmentationAdd()}
|
||||
>
|
||||
<Icon
|
||||
name="row-add"
|
||||
className="h-5 w-5"
|
||||
<div className="flex min-h-0 flex-col bg-black text-[13px] font-[300]">
|
||||
<PanelSection
|
||||
title="Segmentation"
|
||||
actionIcons={
|
||||
activeSegmentation && [
|
||||
{
|
||||
name: 'settings-bars',
|
||||
onClick: () => setIsConfigOpen(isOpen => !isOpen),
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
{isConfigOpen && (
|
||||
<SegmentationConfig
|
||||
setFillAlpha={setFillAlpha}
|
||||
setFillAlphaInactive={setFillAlphaInactive}
|
||||
setOutlineWidthActive={setOutlineWidthActive}
|
||||
setOutlineOpacityActive={setOutlineOpacityActive}
|
||||
setRenderFill={setRenderFill}
|
||||
setRenderInactiveSegmentations={setRenderInactiveSegmentations}
|
||||
setRenderOutline={setRenderOutline}
|
||||
segmentationConfig={segmentationConfig}
|
||||
/>
|
||||
<div className="pl-1">Add New Segmentation</div>
|
||||
)}
|
||||
<div className="bg-primary-dark">
|
||||
{segmentations?.length === 0 ? (
|
||||
<div className="select-none rounded-[4px]">
|
||||
{showAddSegmentation && !disableEditing && (
|
||||
<NoSegmentationRow onSegmentationAdd={onSegmentationAdd} />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-1 select-none">
|
||||
<SegmentationDropDownRow
|
||||
segmentations={segmentations}
|
||||
disableEditing={disableEditing}
|
||||
activeSegmentation={activeSegmentation}
|
||||
onActiveSegmentationChange={onActiveSegmentationChange}
|
||||
onSegmentationDelete={onSegmentationDelete}
|
||||
onSegmentationEdit={onSegmentationEdit}
|
||||
onSegmentationDownload={onSegmentationDownload}
|
||||
storeSegmentation={storeSegmentation}
|
||||
onSegmentationAdd={onSegmentationAdd}
|
||||
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
|
||||
/>
|
||||
{!disableEditing && showAddSegment && (
|
||||
<AddSegmentRow onClick={() => onSegmentAdd(activeSegmentationId)} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{activeSegmentation && (
|
||||
<div className="ohif-scrollbar mt-1.5 flex min-h-0 flex-col overflow-y-hidden">
|
||||
{activeSegmentation?.segments?.map(segment => {
|
||||
if (!segment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { segmentIndex, color, label, isVisible, isLocked } = segment;
|
||||
return (
|
||||
<div
|
||||
className="mb-[1px]"
|
||||
key={segmentIndex}
|
||||
>
|
||||
<SegmentationGroupSegment
|
||||
segmentationId={activeSegmentationId}
|
||||
segmentIndex={segmentIndex}
|
||||
label={label}
|
||||
color={color}
|
||||
isActive={activeSegmentation.activeSegmentIndex === segmentIndex}
|
||||
disableEditing={disableEditing}
|
||||
isLocked={isLocked}
|
||||
isVisible={isVisible}
|
||||
onClick={onSegmentClick}
|
||||
onEdit={onSegmentEdit}
|
||||
onDelete={onSegmentDelete}
|
||||
showDelete={showDeleteSegment}
|
||||
onColor={onSegmentColorClick}
|
||||
onToggleVisibility={onToggleSegmentVisibility}
|
||||
onToggleLocked={onToggleSegmentLock}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</PanelSection>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
SegmentationGroupTable.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
segmentations: PropTypes.array.isRequired,
|
||||
activeSegmentationId: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
onEdit: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onToggleLocked: PropTypes.func,
|
||||
onToggleVisibility: PropTypes.func.isRequired,
|
||||
onToggleVisibilityAll: PropTypes.func.isRequired,
|
||||
segmentationConfig: PropTypes.object,
|
||||
segmentations: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
segments: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
segmentIndex: PropTypes.number.isRequired,
|
||||
color: PropTypes.array.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
isVisible: PropTypes.bool.isRequired,
|
||||
isLocked: PropTypes.bool.isRequired,
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
segmentationConfig: PropTypes.object.isRequired,
|
||||
disableEditing: PropTypes.bool,
|
||||
showAddSegmentation: PropTypes.bool,
|
||||
showAddSegment: PropTypes.bool,
|
||||
showDeleteSegment: PropTypes.bool,
|
||||
onSegmentationAdd: PropTypes.func.isRequired,
|
||||
onSegmentationEdit: PropTypes.func.isRequired,
|
||||
onSegmentationClick: PropTypes.func.isRequired,
|
||||
onSegmentationDelete: PropTypes.func.isRequired,
|
||||
onSegmentationDownload: PropTypes.func.isRequired,
|
||||
storeSegmentation: PropTypes.func.isRequired,
|
||||
onSegmentClick: PropTypes.func.isRequired,
|
||||
onSegmentAdd: PropTypes.func.isRequired,
|
||||
onSegmentDelete: PropTypes.func.isRequired,
|
||||
onSegmentEdit: PropTypes.func.isRequired,
|
||||
onToggleSegmentationVisibility: PropTypes.func.isRequired,
|
||||
onToggleSegmentVisibility: PropTypes.func.isRequired,
|
||||
onToggleSegmentLock: PropTypes.func.isRequired,
|
||||
onSegmentColorClick: PropTypes.func.isRequired,
|
||||
setFillAlpha: PropTypes.func.isRequired,
|
||||
setFillAlphaInactive: PropTypes.func.isRequired,
|
||||
setOutlineWidthActive: PropTypes.func.isRequired,
|
||||
setOutlineOpacityActive: PropTypes.func.isRequired,
|
||||
setRenderFill: PropTypes.func.isRequired,
|
||||
setRenderInactiveSegmentations: PropTypes.func.isRequired,
|
||||
setRenderOutline: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
SegmentationGroupTable.defaultProps = {
|
||||
title: '',
|
||||
segmentations: [],
|
||||
activeSegmentationId: '',
|
||||
onClick: () => {},
|
||||
onEdit: () => {},
|
||||
onDelete: () => {},
|
||||
onToggleLocked: () => {},
|
||||
onToggleVisibility: () => {},
|
||||
onToggleVisibilityAll: () => {},
|
||||
segmentationConfig: {
|
||||
initialConfig: {
|
||||
fillAlpha: 0.5,
|
||||
fillAlphaInactive: 0.5,
|
||||
outlineWidthActive: 1,
|
||||
outlineOpacity: 1,
|
||||
outlineOpacityInactive: 0.5,
|
||||
renderFill: true,
|
||||
renderInactiveSegmentations: true,
|
||||
renderOutline: true,
|
||||
},
|
||||
},
|
||||
disableEditing: false,
|
||||
showAddSegmentation: true,
|
||||
showAddSegment: true,
|
||||
showDeleteSegment: true,
|
||||
onSegmentationAdd: () => {},
|
||||
onSegmentationEdit: () => {},
|
||||
onSegmentationClick: () => {},
|
||||
onSegmentationDelete: () => {},
|
||||
onSegmentationDownload: () => {},
|
||||
storeSegmentation: () => {},
|
||||
onSegmentClick: () => {},
|
||||
onSegmentAdd: () => {},
|
||||
onSegmentDelete: () => {},
|
||||
onSegmentEdit: () => {},
|
||||
onToggleSegmentationVisibility: () => {},
|
||||
onToggleSegmentVisibility: () => {},
|
||||
onToggleSegmentLock: () => {},
|
||||
onSegmentColorClick: () => {},
|
||||
setFillAlpha: () => {},
|
||||
setFillAlphaInactive: () => {},
|
||||
setOutlineWidthActive: () => {},
|
||||
@@ -144,5 +234,4 @@ SegmentationGroupTable.defaultProps = {
|
||||
setRenderInactiveSegmentations: () => {},
|
||||
setRenderOutline: () => {},
|
||||
};
|
||||
|
||||
export default SegmentationGroupTable;
|
||||
+6
-7
@@ -1,7 +1,5 @@
|
||||
import SegmentationGroupTable from '../SegmentationGroupTable';
|
||||
|
||||
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
|
||||
import { createComponentTemplate } from '../../../storybook/functions/create-component-story';
|
||||
import { SegmentationGroupTable } from '../../../components';
|
||||
|
||||
export const argTypes = {
|
||||
component: SegmentationGroupTable,
|
||||
@@ -40,13 +38,14 @@ SegmentationGroupTable is a component that renders the SegmentationGroupTables.
|
||||
showAddSegmentation: true,
|
||||
isMinimized: { '4be5-b9b2-ed33-fa86970568e7': false },
|
||||
showAddSegment: true,
|
||||
showDeleteSegment: true,
|
||||
segmentations: [
|
||||
{
|
||||
id: '4be5-b9b2-ed33-fa86970568e7',
|
||||
volumeId: 'a3b0f15f-4be5-b9b2-ed33-fa86970568e7',
|
||||
activeSegmentIndex: 1,
|
||||
activeSegmentIndex: 2,
|
||||
cachedStats: {},
|
||||
label: 'Segmentation',
|
||||
label: 'Segmentation 1',
|
||||
segmentsLocked: {},
|
||||
type: 'LABELMAP',
|
||||
displayText: [],
|
||||
@@ -55,7 +54,7 @@ SegmentationGroupTable is a component that renders the SegmentationGroupTables.
|
||||
segments: [
|
||||
null,
|
||||
{
|
||||
label: 'Segment 1',
|
||||
label: 'This is a very long segment label',
|
||||
segmentIndex: 1,
|
||||
color: [128, 174, 128],
|
||||
opacity: 255,
|
||||
@@ -88,7 +87,7 @@ SegmentationGroupTable is a component that renders the SegmentationGroupTables.
|
||||
volumeId: 'a3b0f15f-4be5-b9b2-ed33-fa86970568e7',
|
||||
activeSegmentIndex: 1,
|
||||
cachedStats: {},
|
||||
label: 'Segmentation2',
|
||||
label: '2 Segmentation',
|
||||
segmentsLocked: {},
|
||||
type: 'LABELMAP',
|
||||
displayText: [],
|
||||
|
||||
Reference in new issue
Block a user