Skip to content

ui(components): Adds InputNumber component and 4d panel updates #4874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
000018a
InputNumber prototype component
dan-rukas Mar 19, 2025
7e249a9
Update compound component
dan-rukas Mar 19, 2025
15d4720
Updated legacy colors and button sizing
dan-rukas Mar 19, 2025
fc2b976
Design updates to align with other components
dan-rukas Mar 19, 2025
340b90b
Removed option for backward compatibility
dan-rukas Mar 19, 2025
c637fac
Restored cornerstone dynamic operator types
dan-rukas Mar 19, 2025
034bb46
Merge branch 'OHIF:master' into feat/panel-layout-4d
dan-rukas Mar 19, 2025
57acb3b
Prototype panel
dan-rukas Mar 19, 2025
7b792cc
Removed legacy colors and styles
dan-rukas Mar 19, 2025
5a9ddf1
Added WindowLevel ui-next component
dan-rukas Mar 19, 2025
0ed8c0a
WindowLevel design updates
dan-rukas Mar 20, 2025
a812538
Streamlined component based on context
dan-rukas Mar 20, 2025
c045790
Updated vertical controls
dan-rukas Mar 20, 2025
22dfd16
Panel background fix
dan-rukas Mar 20, 2025
f463563
Updated vertical controls subcomponent
dan-rukas Mar 20, 2025
755821b
Vertical button sizing
dan-rukas Mar 20, 2025
24229ff
Updated vertical button positioning
dan-rukas Mar 20, 2025
e45f5bd
Centered labels
dan-rukas Mar 20, 2025
f101a2d
Update DynamicVolumeControls.tsx
dan-rukas Mar 20, 2025
073475c
Add stepper mode to Numeric component and implement controls
sedghi Mar 24, 2025
3380d3b
move ot numeric
sedghi Mar 25, 2025
c240067
add back components
sedghi Mar 25, 2025
ab688bb
Enhance NumericMetaShowcase with improved layout and styling for Numb…
sedghi Mar 25, 2025
b3fc6f3
Added default height to NumberStepper
dan-rukas Mar 31, 2025
6a4b130
Merge branch 'master' into feat/panel-layout-4d
sedghi Apr 1, 2025
fe94a91
fix
sedghi Apr 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 140 additions & 137 deletions bun.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import React, { useState } from 'react';
import { Button, PanelSection, ButtonGroup, IconButton, InputNumber } from '@ohif/ui';
import { Icons, Tooltip, TooltipTrigger, TooltipContent, Numeric } from '@ohif/ui-next';
import {
Tabs,
TabsList,
TabsTrigger,
Button,
PanelSection,
Icons,
Tooltip,
TooltipTrigger,
TooltipContent,
Numeric,
} from '@ohif/ui-next';
import { Enums } from '@cornerstonejs/core';

const controlClassNames = {
sizeClassName: 'w-[58px] h-[28px]',
arrowsDirection: 'horizontal',
labelPosition: 'bottom',
// Helper function to safely convert any value to uppercase string
const toUpperCaseString = value => {
if (value === null || value === undefined) {
return '';
}
return String(value).toUpperCase();
};

const Header = ({ title, tooltip }) => (
Expand All @@ -16,18 +28,18 @@ const Header = ({ title, tooltip }) => (
<span>
<Icons.ByName
name="info-link"
className="text-primary-active h-[14px] w-[14px]"
className="text-primary h-3 w-3"
/>
</span>
</TooltipTrigger>
<TooltipContent
sideOffset={4}
className="bg-primary-dark max-w-xs p-2 text-white"
className="max-w-xs"
>
<div>{tooltip}</div>
</TooltipContent>
</Tooltip>
<span className="text-aqua-pale text-[11px] uppercase">{title}</span>
<span className="text-muted-foreground text-xs uppercase tracking-wide">{title}</span>
</div>
);

Expand All @@ -51,112 +63,138 @@ const DynamicVolumeControls = ({
const [computedView, setComputedView] = useState(false);
const [computeViewMode, setComputeViewMode] = useState(Enums.DynamicOperatorType.SUM);

// Wrapper for onGenerate to handle potential errors
const handleGenerate = () => {
try {
if (typeof onGenerate === 'function') {
onGenerate(computeViewMode);
} else {
console.error('onGenerate is not a function', onGenerate);
}
} catch (error) {
console.error('Error in onGenerate:', error);
}
};

return (
<div className="flex select-none flex-col">
<PanelSection
title="Controls"
childrenClassName="space-y-4 pb-5 px-5"
>
<div className="mt-2">
<Header
title="View"
tooltip={
'Select the view mode, 4D to view the dynamic volume or Computed to view the computed volume'
}
/>
<ButtonGroup className="mt-2 w-full">
<button
className="w-1/2"
onClick={() => {
setComputedView(false);
onDynamicClick?.();
<PanelSection defaultOpen={true}>
<PanelSection.Header>Controls</PanelSection.Header>
<PanelSection.Content className="bg-muted space-y-4 px-5 pt-2 pb-4">
<div className="mb-4">
<Header
title="View"
tooltip={
'Select the view mode, 4D to view the dynamic volume or Computed to view the computed volume'
}
/>
<Tabs
value={computedView ? 'computed' : '4d'}
onValueChange={value => {
const isComputed = value === 'computed';
setComputedView(isComputed);
if (!isComputed && typeof onDynamicClick === 'function') {
onDynamicClick();
}
}}
className="my-2 w-full"
>
4D
</button>
<button
className="w-1/2"
onClick={() => {
setComputedView(true);
<TabsList className="w-full">
<TabsTrigger
value="4d"
className="w-1/2"
>
4D
</TabsTrigger>
<TabsTrigger
value="computed"
className="w-1/2"
>
Computed
</TabsTrigger>
</TabsList>
</Tabs>
</div>
<div>
<DimensionGroupControls
onPlayPauseChange={onPlayPauseChange}
isPlaying={isPlaying}
computedView={computedView}
// fps
fps={fps}
onFpsChange={onFpsChange}
minFps={minFps}
maxFps={maxFps}
//
numDimensionGroups={numDimensionGroups}
onDimensionGroupChange={onDimensionGroupChange}
currentDimensionGroupNumber={currentDimensionGroupNumber}
/>
</div>
<div className={`mt-3 flex flex-col ${computedView ? '' : 'ohif-disabled'}`}>
<Header
title="Computed Operation"
tooltip={
<div>
Operation Buttons (SUM, AVERAGE, SUBTRACT): Select the mathematical operation to
be applied to the data set.
<br /> Range Slider: Choose the numeric range of dimension groups within which the
operation will be performed.
<br />
Generate Button: Execute the chosen operation on the specified range of data.
</div>
}
/>
<Tabs
value={String(computeViewMode)}
onValueChange={value => {
setComputeViewMode(value);
}}
className="mt-2 w-full"
>
Computed
</button>
</ButtonGroup>
</div>
<div>
<DimensionGroupControls
onPlayPauseChange={onPlayPauseChange}
isPlaying={isPlaying}
computedView={computedView}
// fps
fps={fps}
onFpsChange={onFpsChange}
minFps={minFps}
maxFps={maxFps}
//
numDimensionGroups={numDimensionGroups}
onDimensionGroupChange={onDimensionGroupChange}
currentDimensionGroupNumber={currentDimensionGroupNumber}
/>
</div>
<div className={`mt-6 flex flex-col ${computedView ? '' : 'ohif-disabled'}`}>
<Header
title="Computed Operation"
tooltip={
<div>
Operation Buttons (SUM, AVERAGE, SUBTRACT): Select the mathematical operation to be
applied to the data set.
<br /> Range Slider: Choose the numeric range of dimension groups within which the
operation will be performed.
<br />
Generate Button: Execute the chosen operation on the specified range of data.
</div>
}
/>
<ButtonGroup
className="mt-2 w-full"
separated={true}
>
<button
className="w-1/2"
onClick={() => setComputeViewMode(Enums.DynamicOperatorType.SUM)}
>
{Enums.DynamicOperatorType.SUM.toString().toUpperCase()}
</button>
<button
className="w-1/2"
onClick={() => setComputeViewMode(Enums.DynamicOperatorType.AVERAGE)}
>
{Enums.DynamicOperatorType.AVERAGE.toString().toUpperCase()}
</button>
<button
className="w-1/2"
onClick={() => setComputeViewMode(Enums.DynamicOperatorType.SUBTRACT)}
>
{Enums.DynamicOperatorType.SUBTRACT.toString().toUpperCase()}
</button>
</ButtonGroup>
<div className="mt-2 w-full">
<Numeric.Container
mode="doubleRange"
min={1}
max={numDimensionGroups}
values={rangeValues}
onChange={onDoubleRangeChange}
<TabsList className="w-full gap-1">
{' '}
<TabsTrigger
value={String(Enums.DynamicOperatorType.SUM)}
className="w-1/3"
>
{toUpperCaseString(Enums.DynamicOperatorType.SUM)}
</TabsTrigger>
<TabsTrigger
value={String(Enums.DynamicOperatorType.AVERAGE)}
className="w-1/3"
>
{toUpperCaseString(Enums.DynamicOperatorType.AVERAGE)}
</TabsTrigger>
<TabsTrigger
value={String(Enums.DynamicOperatorType.SUBTRACT)}
className="w-1/3"
>
{toUpperCaseString(Enums.DynamicOperatorType.SUBTRACT)}
</TabsTrigger>
</TabsList>
</Tabs>
<div className="mt-2 w-full">
<Numeric.Container
mode="doubleRange"
min={1}
max={numDimensionGroups || 1}
values={rangeValues || [1, numDimensionGroups || 1]}
onChange={onDoubleRangeChange || (() => {})}
>
<Numeric.DoubleRange showNumberInputs />
</Numeric.Container>
</div>
<Button
variant="default"
size="sm"
className="mt-2 h-[26px] w-[115px] self-start p-0"
onClick={handleGenerate}
>
<Numeric.DoubleRange showNumberInputs />
</Numeric.Container>
Generate
</Button>
</div>
<Button
className="mt-2 !h-[26px] !w-[115px] self-start !p-0"
onClick={() => {
onGenerate(computeViewMode);
}}
>
Generate
</Button>
</div>
</PanelSection.Content>
</PanelSection>
</div>
);
Expand All @@ -179,7 +217,7 @@ function DimensionGroupControls({
const getPlayPauseIconName = () => (isPlaying ? 'icon-pause' : 'icon-play');

return (
<div className={computedView && 'ohif-disabled'}>
<div className={computedView ? 'ohif-disabled' : ''}>
<Header
title="4D Controls"
tooltip={
Expand All @@ -192,32 +230,56 @@ function DimensionGroupControls({
}
/>
<div className="mt-3 flex justify-between">
<IconButton
<Button
id="play-pause-button"
className="bg-customblue-30 h-[26px] w-[58px] rounded-[4px]"
onClick={() => onPlayPauseChange(!isPlaying)}
variant="secondary"
size="default"
className="w-[58px]"
onClick={() => {
if (typeof onPlayPauseChange === 'function') {
onPlayPauseChange(!isPlaying);
}
}}
>
<Icons.ByName
name={getPlayPauseIconName()}
className="active:text-primary-light hover:bg-customblue-300 h-[24px] w-[24px] cursor-pointer text-white"
className="text-foreground h-[24px] w-[24px]"
/>
</IconButton>
<InputNumber
value={currentDimensionGroupNumber}
onChange={onDimensionGroupChange}
minValue={1}
maxValue={numDimensionGroups}
label="Group"
{...controlClassNames}
/>
<InputNumber
value={fps}
onChange={onFpsChange}
minValue={minFps}
maxValue={maxFps}
{...controlClassNames}
label="FPS"
/>
</Button>

<Numeric.Container
mode="stepper"
value={currentDimensionGroupNumber || 1}
onChange={onDimensionGroupChange || (() => {})}
min={1}
max={numDimensionGroups || 1}
step={1}
>
<div className="flex flex-col items-center">
<Numeric.NumberStepper
className="h-[28px] w-[58px]"
direction="horizontal"
/>
<Numeric.Label className="text-muted-foreground mt-1 text-sm">Frame</Numeric.Label>
</div>
</Numeric.Container>

<Numeric.Container
mode="stepper"
value={fps || 1}
onChange={onFpsChange || (() => {})}
min={minFps || 1}
max={maxFps || 30}
step={1}
>
<div className="flex flex-col items-center">
<Numeric.NumberStepper
className="h-[28px] w-[58px]"
direction="horizontal"
/>
<Numeric.Label className="text-muted-foreground mt-1 text-sm">FPS</Numeric.Label>
</div>
</Numeric.Container>
</div>
</div>
);
Expand Down
Loading