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,64 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { PanelSection, Icon, Tooltip } from '../../components';
|
||||
import ToolSettings from './ToolSettings';
|
||||
|
||||
const AdvancedToolbox = ({ title, items }) => {
|
||||
const [activeItemName, setActiveItemName] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
// see if any of the items are active from the outside
|
||||
const activeItem = items?.find(item => item.active);
|
||||
setActiveItemName(activeItem ? activeItem.name : null);
|
||||
}, [items]);
|
||||
|
||||
const activeItemOptions = items?.find(item => item.name === activeItemName)?.options;
|
||||
|
||||
return (
|
||||
<PanelSection title={title}>
|
||||
<div className="flex flex-col bg-black">
|
||||
<div className="bg-primary-dark mt-0.5 flex flex-wrap py-2">
|
||||
{items?.map(item => {
|
||||
return (
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
content={<span className="text-white">{item.name}</span>}
|
||||
key={item.name}
|
||||
>
|
||||
<div
|
||||
className="ml-2 mb-2"
|
||||
onClick={() => {
|
||||
if (item.disabled) {
|
||||
return;
|
||||
}
|
||||
setActiveItemName(item.name);
|
||||
item.onClick(item.name);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={classnames(
|
||||
'text-primary-active grid h-[40px] w-[40px] place-items-center rounded-md bg-black ',
|
||||
activeItemName === item.name && 'bg-primary-light text-black',
|
||||
item.disabled && 'opacity-50',
|
||||
!item.disabled &&
|
||||
'hover:bg-primary-light cursor-pointer hover:cursor-pointer hover:text-black'
|
||||
)}
|
||||
>
|
||||
<Icon name={item.icon} />
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="bg-primary-dark h-auto px-2">
|
||||
<ToolSettings options={activeItemOptions} />
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
);
|
||||
};
|
||||
|
||||
AdvancedToolbox.propTypes = {};
|
||||
|
||||
export default AdvancedToolbox;
|
||||
@@ -0,0 +1,84 @@
|
||||
import React from 'react';
|
||||
import { ButtonGroup, InputRange, ButtonEnums } from '../../components';
|
||||
|
||||
const SETTING_TYPES = {
|
||||
RANGE: 'range',
|
||||
RADIO: 'radio',
|
||||
CUSTOM: 'custom',
|
||||
};
|
||||
|
||||
function ToolSettings({ options }) {
|
||||
if (!options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const getButtons = option => {
|
||||
const buttons = [];
|
||||
|
||||
option.values?.map(({ label, value: optionValue }, index) => {
|
||||
buttons.push({
|
||||
children: label,
|
||||
onClick: () => option.onChange(optionValue),
|
||||
key: `button-${option.id}-${index}`, // A unique key
|
||||
});
|
||||
});
|
||||
|
||||
return buttons;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2 py-2 text-white">
|
||||
{options?.map(option => {
|
||||
if (option.type === SETTING_TYPES.RANGE) {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center"
|
||||
key={option.id}
|
||||
>
|
||||
<div className="w-1/3 text-xs text-[13px]">{option.name}</div>
|
||||
<div className="w-2/3">
|
||||
<InputRange
|
||||
minValue={option.min}
|
||||
maxValue={option.max}
|
||||
step={option.step}
|
||||
value={option.value}
|
||||
onChange={e => option.onChange(e)}
|
||||
allowNumberEdit={true}
|
||||
showAdjustmentArrows={false}
|
||||
inputClassName="ml-2 w-4/5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (option.type === SETTING_TYPES.RADIO) {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between text-[13px]"
|
||||
key={option.id}
|
||||
>
|
||||
<span>{option.name}</span>
|
||||
<div className="max-w-1/2">
|
||||
<ButtonGroup
|
||||
buttons={getButtons(option)}
|
||||
defaultActiveIndex={option.defaultActiveIndex}
|
||||
size={ButtonEnums.size.small}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (option.type === SETTING_TYPES.CUSTOM) {
|
||||
return (
|
||||
<div key={option.id}>
|
||||
{typeof option.children === 'function' ? option.children() : option.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ToolSettings;
|
||||
@@ -0,0 +1,144 @@
|
||||
import AdvancedToolbox from '../../AdvancedToolbox';
|
||||
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
|
||||
import {
|
||||
createComponentTemplate,
|
||||
createStoryMetaSettings,
|
||||
} from '../../../storybook/functions/create-component-story';
|
||||
|
||||
export const argTypes = {
|
||||
component: AdvancedToolbox,
|
||||
title: 'Components/AdvancedToolbox',
|
||||
};
|
||||
|
||||
<Meta
|
||||
title="Components/AdvancedToolbox"
|
||||
component={AdvancedToolbox}
|
||||
/>
|
||||
|
||||
export const advancedToolboxTemplate = args => (
|
||||
<div className="bg-primary-dark h-96 w-64">
|
||||
<AdvancedToolbox {...args} />
|
||||
</div>
|
||||
);
|
||||
|
||||
<Heading
|
||||
title="Advanced Toolbox"
|
||||
componentRelativePath="AdvancedToolbox/AdvancedToolbox.tsx"
|
||||
/>
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Props](#props)
|
||||
- [Usage](#usage)
|
||||
- [Contribute](#contribute)
|
||||
|
||||
## Overview
|
||||
|
||||
OHIF advanced toolbox which can host set of tools that require more space for customization.
|
||||
|
||||
<Canvas>
|
||||
<Story
|
||||
name="Overview"
|
||||
args={{
|
||||
title: 'Segmentation Tools',
|
||||
items: [
|
||||
{
|
||||
name: 'Brush',
|
||||
icon: 'icon-tool-brush',
|
||||
active: false,
|
||||
onClick: () => console.log('Brush clicked'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
type: 'range',
|
||||
min: 1,
|
||||
max: 10,
|
||||
value: 5,
|
||||
step: 1,
|
||||
onChange: value => console.log('Brush size changed', value),
|
||||
},
|
||||
{
|
||||
name: 'Mode',
|
||||
type: 'radio',
|
||||
value: 'Circle',
|
||||
values: [
|
||||
{ value: 'Circle', label: 'Circle' },
|
||||
{ value: 'Sphere', label: 'Sphere' },
|
||||
{ value: 'Rectangle', label: 'Rectangle' },
|
||||
],
|
||||
onChange: value => console.log('Brush mode changed', value),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Eraser',
|
||||
icon: 'icon-tool-eraser',
|
||||
onClick: () => console.log('eraser clicked'),
|
||||
options: [
|
||||
{
|
||||
name: 'Mode',
|
||||
type: 'radio',
|
||||
value: 'EraserSphere',
|
||||
values: [
|
||||
{ value: 'EraserCircle', label: 'Circle' },
|
||||
{ value: 'EraserSphere', label: 'Sphere' },
|
||||
],
|
||||
onChange: value => console.log('Brush mode changed', value),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Threshold',
|
||||
icon: 'icon-tool-threshold',
|
||||
active: true,
|
||||
onClick: () => console.log('eraser clicked'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
type: 'range',
|
||||
min: 1,
|
||||
max: 10,
|
||||
value: 5,
|
||||
step: 1,
|
||||
onChange: value => console.log('Brush size changed', value),
|
||||
},
|
||||
{
|
||||
name: 'Mode',
|
||||
type: 'radio',
|
||||
value: 'Circle',
|
||||
values: [
|
||||
{ value: 'Circle', label: 'Circle' },
|
||||
{ value: 'Sphere', label: 'Sphere' },
|
||||
{ value: 'Rectangle', label: 'Rectangle' },
|
||||
],
|
||||
onChange: value => console.log('Brush mode changed', value),
|
||||
},
|
||||
{
|
||||
name: 'custom',
|
||||
type: 'custom',
|
||||
children: () => {
|
||||
return (
|
||||
<div>
|
||||
<div>Custom</div>
|
||||
<input type="text" />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
{advancedToolboxTemplate.bind({})}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Props
|
||||
|
||||
<ArgsTable of={AdvancedToolbox} />
|
||||
|
||||
## Usage
|
||||
|
||||
## Contribute
|
||||
|
||||
<Footer componentRelativePath="AdvancedToolbox/__stories__/AdvancedToolbox.stories.mdx" />
|
||||
@@ -0,0 +1,2 @@
|
||||
import AdvancedToolbox from './AdvancedToolbox';
|
||||
export default AdvancedToolbox;
|
||||
Reference in new issue
Block a user