Migration Guide: Input Components to @ohif/ui-next
This guide explains how to migrate from the existing Input, InputNumber, InputRange, InputDoubleRange, InputFilterText, InputGroup, InputLabelWrapper, and InputText components to their new equivalents or patterns using @ohif/ui-next, including the Numeric meta component for numeric inputs.
Why Migrate?
See the full list of components in the Numeric Component Showcase
The old components relied heavily on props, making them complex and difficult to maintain and apply custom styles. The new Numeric component provides a structured approach with a context-based API, reducing prop clutter and improving reusability.
The Numeric component offers several advantages:
- Versatile Modes: It supports basic number input (
Numeric.NumberInput), stepper controls (Numeric.NumberStepper), single range sliders (Numeric.SingleRange), and double range sliders (Numeric.DoubleRange). - Flexible Layout: You have full control over the layout using standard CSS classes (
className) on the container and its subcomponents likeNumeric.Label,Numeric.NumberInput, etc., allowing for various arrangements (e.g., flex, grid). - Enhanced Customization: Easily customize the appearance and behavior, such as showing/hiding associated number inputs for sliders, displaying the current value within the label (
showValue), and integrating icons. - State Management: Supports both controlled and uncontrolled component states.
Input type="number" > Numeric.NumberInput
Basic Usage
Old Usage:
<Input
id="example"
label="Enter a number"
value={value}
onChange={(e) => setValue(e.target.value)}
type="number"
/>
New Usage:
<Numeric.Container mode="number" value={value} onChange={setValue}>
<Numeric.Label>Enter a number</Numeric.Label>
<Numeric.NumberInput />
</Numeric.Container>
Input with Custom Classes
Old Usage (with containerClassName, labelClassName, and className)
In the old implementation, we manually applied containerClassName, labelClassName, and className to style the Input component:
<Input
id="example"
label="Enter a number"
value={value}
onChange={(e) => setValue(e.target.value)}
type="number"
containerClassName="flex flex-col space-y-2"
labelClassName="text-gray-500 text-sm"
className="border rounded p-2"
/>
New Usage (Migrating to Numeric.NumberInput)
With Numeric, you should wrap everything inside Numeric.Container, and you can directly apply class names to its subcomponents:
<Numeric.Container mode="number" value={value} onChange={setValue} className="flex flex-col space-y-2">
<Numeric.Label className="text-gray-500 text-sm">Enter a number</Numeric.Label>
<Numeric.NumberInput className="border rounded p-2" />
</Numeric.Container>
Input / InputText (General) > @ohif/ui-next Input + Label
Key Changes:
- The base
Inputcomponent from@ohif/uiis replaced by theInputcomponent from@ohif/ui-next. - Styling props like
labelClassName,containerClassNameare removed. Use standardclassNameon theInputcomponent and its container elements. - Labels provided via the
labelprop are removed. Use the separateLabelcomponent from@ohif/ui-nextalongside theInput. - Layout is handled by standard HTML/Tailwind (Flexbox, Grid).
Migration Steps:
- Update Import: Ensure you are importing
InputandLabelfrom@ohif/ui-next. - Replace Label Prop: If you used the
labelprop, add a separate<Label>component before the<Input>. - Handle Layout: Wrap the
<Label>and<Input>in a containerdivand use layout utilities (e.g.,flex,items-center,space-x-2,flex-col) to position them correctly. - Transfer Styling: Migrate styles from
className,labelClassName, andcontainerClassNameto theclassNameprop of the newInput,Label, and containerdivas appropriate.
Example Diff (Conceptual - derived from PanelPetSUV):
- <Input
- containerClassName={'flex flex-row justify-between items-center'}
- label={'Weight'}
- labelChildren={<span className="text-aqua-pale"> kg</span>}
- labelClassName="text-[13px] text-white"
- className="h-[26px] w-[117px]"
- value={metadata.PatientWeight || ''}
- onChange={handleWeightChange}
- />
+ <div className="flex flex-row items-center space-x-4"> {/* Replaced containerClassName */}
+ <Label className="min-w-32 flex-shrink-0 text-[13px] text-white"> {/* Replaced labelClassName */}
+ Weight
+ <span className="text-muted-foreground"> kg</span> {/* Replaced labelChildren */}
+ </Label>
+ <Input
+ className="h-7 flex-1 h-[26px] w-[117px]" {/* Merged input className */}
+ value={metadata.PatientWeight || ''}
+ onChange={handleWeightChange}
+ />
+ </div>
InputNumber > Numeric.NumberStepper
Key Changes:
- The
InputNumbercomponent is replaced by theNumericcomponent system usingmode="stepper". - Styling props like
sizeClassName,arrowsDirection, andlabelPositionare removed. Layout and styling are now controlled via standardclassNameand parent container layouts (e.g., Flexbox). - Props like
value,onChange,minValue,maxValue, andstepare now typically set on theNumeric.Container. - Labels are handled by the separate
Numeric.Labelsubcomponent. - Stepper controls are provided by the
Numeric.NumberSteppersubcomponent, which takes adirectionprop (horizontalorvertical).
Migration Steps:
- Replace Component: Replace
<InputNumber ... />with<Numeric.Container mode="stepper" ... >. - Transfer Props: Move
value,onChange,minValue(asmin),maxValue(asmax), andstepprops to theNumeric.Container. - Add Subcomponents:
- Inside
Numeric.Container, add<Numeric.NumberStepper />. Set itsdirectionprop (horizontalorvertical) based on the oldarrowsDirection. Apply sizing classes directly usingclassName. - Add a
<Numeric.Label>component for the label text.
- Inside
- Handle Layout: Wrap the
Numeric.Containeror arrange its children using standard layout techniques (like Flexbox) to achieve the desired positioning (equivalent to the oldlabelPosition). Apply styling classes as needed.
Example Diff:
- <InputNumber
- value={currentFrameIndex}
- onChange={onFrameChange}
- minValue={0}
- maxValue={framesLength - 1}
- label="Frame"
- sizeClassName="w-[58px] h-[28px]"
- arrowsDirection="horizontal"
- labelPosition="bottom"
- />
+ <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>
InputRange > Numeric.SingleRange
Key Changes:
InputRangeis replaced by theNumericcomponent system usingmode="singleRange".- Props like
value,onChange,minValue(min),maxValue(max), andstepare set on theNumeric.Container. - The slider element itself is rendered using
<Numeric.SingleRange />. - The
showLabelprop is replaced by explicitly adding a<Numeric.Label>subcomponent. The label text is passed as children toNumeric.Label. You can optionally show the current value(s) within the label using theshowValueprop onNumeric.Label. - The
allowNumberEditprop is replaced by theshowNumberInputprop on<Numeric.SingleRange />. - Layout props like
labelPositionare removed; use standard CSS/Tailwind for layout.
Migration Steps:
- Replace Component: Replace
<InputRange ... />with<Numeric.Container mode="singleRange" ... >. - Transfer Props: Move
value,onChange,minValue(asmin),maxValue(asmax), andstepto theNumeric.Container. - Add Subcomponents:
- Inside, add
<Numeric.SingleRange />. - Use the
showNumberInputprop on the range subcomponent if number editing was previously enabled (allowNumberEdit={true}). - If
showLabelwas true, add a<Numeric.Label>component. Pass the label text as children. Use theshowValueprop on the label if you want to display the numeric value alongside the text.
- Inside, add
- Handle Layout: Arrange the
Numeric.Labeland the Range subcomponent using standard layout techniques (Flexbox, Grid) as needed. Apply styling directly usingclassName.
Example Diff (Conceptual):
- <InputRange
- value={opacity}
- onChange={setOpacity}
- minValue={0}
- maxValue={100}
- step={1}
- showLabel={true}
- label="Opacity"
- allowNumberEdit={true}
- />
+ <Numeric.Container
+ mode="singleRange"
+ value={opacity}
+ onChange={setOpacity}
+ min={0}
+ max={100}
+ step={1}
+ >
+ <div className="flex items-center space-x-2"> {/* Example layout */}
+ <Numeric.Label showValue>Opacity</Numeric.Label>
+ <Numeric.SingleRange showNumberInput />
+ </div>
+ </Numeric.Container>
InputDoubleRange > Numeric.DoubleRange
Key Changes:
InputDoubleRangeis replaced by theNumericcomponent system usingmode="doubleRange".- Props like
values,onChange,minValue(min),maxValue(max), andstepare set on theNumeric.Container. - The slider element itself is rendered using
<Numeric.DoubleRange />. - The
showLabelprop is replaced by explicitly adding a<Numeric.Label>subcomponent. You can optionally show the current values within the label using theshowValueprop onNumeric.Label. - Editing numbers is controlled by the
showNumberInputs(plural) prop on<Numeric.DoubleRange />. - Layout props like
labelPositionare removed; use standard CSS/Tailwind for layout.
Migration Steps:
- Replace Component: Replace
<InputDoubleRange ... />with<Numeric.Container mode="doubleRange" ... >. - Transfer Props: Move
values,onChange,minValue(asmin),maxValue(asmax), andstepto theNumeric.Container. - Add Subcomponents:
- Inside, add
<Numeric.DoubleRange />. - Use the
showNumberInputsprop on the range subcomponent if number editing is desired. - If
showLabelwas true, add a<Numeric.Label>component. Pass the label text as children. Use theshowValueprop on the label if you want to display the numeric values alongside the text.
- Inside, add
- Handle Layout: Arrange the
Numeric.Labeland the Range subcomponent using standard layout techniques (Flexbox, Grid) as needed.
Example Diff:
- <InputDoubleRange
- values={rangeValues}
- onChange={handleSliderChange}
- minValue={1}
- maxValue={numDimensionGroups || 1}
- showLabel={false} // Assuming label wasn't shown, or handled separately
- step={1}
- // Assuming number edit might have been implicitly enabled or desired
- />
+ <Numeric.Container
+ mode="doubleRange"
+ min={1}
+ max={numDimensionGroups || 1}
+ values={rangeValues || [1, numDimensionGroups || 1]}
+ onChange={onDoubleRangeChange || (() => {})}
+ >
+ {/* Label could be added here if needed */}
+ {/* <Numeric.Label>Range</Numeric.Label> */}
+ <Numeric.DoubleRange showNumberInputs />
+ </Numeric.Container>
InputFilterText > InputFilter
Key Changes:
InputFilterTextis replaced by the more composableInputFiltercomponent from@ohif/ui-next.InputFilteruses subcomponents (InputFilter.SearchIcon,InputFilter.Input,InputFilter.ClearButton) which are included by default but can be customized.- The
onDebounceChangeprop is replaced by a standardonChangeprop on the mainInputFiltercomponent, which handles debouncing internally (configurable viadebounceTime). - Props like
placeholderandvalueare passed to theInputFilter.Inputsubcomponent (or directly toInputFilterfor simplicity if using the default structure).
Migration Steps:
- Replace Component: Replace
<InputFilterText ... />with<InputFilter ... >. - Transfer Props:
- Move
placeholderto theInputFiltercomponent or itsInputFilter.Inputsubcomponent. - Handle
valueusing controlled state if necessary, passing it toInputFilter.
- Move
- Update Handler: Replace the
onDebounceChangehandler with theonChangeprop on theInputFiltercomponent. - Styling: Apply necessary classes for layout and positioning, especially padding on the input (e.g.,
pl-9 pr-9) to accommodate the default icon and clear button if using the defaults.
Example Diff:
- <InputFilterText
- value={searchValue}
- onDebounceChange={handleSearchChange}
- placeholder={'Search all'}
- />
+ <InputFilter
+ value={searchValue}
+ onChange={setFilterValue} /* Direct state update or debounced handler */
+ placeholder="Search all"
+ >
+ {/* Using default structure which includes Icon, Input, ClearButton */}
+ {/* Example customization: */}
+ {/* <InputFilter.SearchIcon /> */}
+ {/* <InputFilter.Input placeholder="Search all" className="pl-9 pr-9" /> */}
+ {/* <InputFilter.ClearButton /> */}
+ </InputFilter>
InputGroup / InputLabelWrapper > Composition
Key Changes:
- These wrapper components (
InputGroup,InputLabelWrapper) are deprecated. - Functionality (grouping label and input, optional sorting indicators) is now achieved through composition using standard layout techniques (Flexbox/Grid) and the base
@ohif/ui-nextcomponents (Label,Input,Icons).
Migration Steps:
- Remove Wrapper: Delete the
<InputGroup>or<InputLabelWrapper>tags. - Create Container: Use a standard
divas the container. - Add Label and Input: Place the
<Label>and the corresponding input/control component (e.g.,<Input>,<Select>, custom component) inside thediv. - Apply Layout: Use Tailwind classes (
flex,grid,space-x-*,items-center, etc.) on the containerdivto arrange the label and input as needed. - Add Sorting Icons (if applicable): If migrating from
InputLabelWrapperwithisSortable, manually add the appropriate<Icons.ByName name="sorting-..." />component next to the label text within the<Label>component's children. ImportIconsfrom@ohif/ui-next. - Apply Styling: Add necessary styling classes directly to the
Label, input component, and containerdiv.
Example Diff (Conceptual - derived from InputLabelWrapper usage):
- <InputLabelWrapper
- label="Patient Name"
- isSortable={true}
- sortDirection={sortDir}
- onLabelClick={toggleSort}
- >
- <Input value={patientName} onChange={setPatientName} />
- </InputLabelWrapper>
+ <div className="flex flex-col space-y-1"> {/* Example layout */}
+ <Label
+ onClick={toggleSort}
+ className="flex cursor-pointer items-center"
+ >
+ Patient Name
+ {sortDir === 'ascending' && <Icons.ByName name="sorting-ascending" className="ml-1 h-4 w-4" />}
+ {sortDir === 'descending' && <Icons.ByName name="sorting-descending" className="ml-1 h-4 w-4" />}
+ {sortDir === 'none' && <Icons.ByName name="sorting" className="ml-1 h-4 w-4" />}
+ </Label>
+ <Input value={patientName} onChange={setPatientName} />
+ </div>
Summary of Changes
| Old Component | New Component/Pattern Equivalent | Notes |
|---|---|---|
<Input type="number"> | <Numeric.NumberInput> | Use Numeric.Container with mode="number" |
<Input> / <InputText> | @ohif/ui-next <Input> + <Label> | Use standard <div> and CSS/Tailwind for layout |
<InputNumber> | <Numeric.NumberStepper> | Use Numeric.Container with mode="stepper" |
<InputRange> | <Numeric.SingleRange> | Use Numeric.Container with mode="singleRange" |
<InputDoubleRange> | <Numeric.DoubleRange> | Use Numeric.Container with mode="doubleRange" |
<InputFilterText> | @ohif/ui-next <InputFilter> | Composable component with internal debouncing |
<InputGroup> | Composition (div, <Label>, <Input>, etc.) | Replaced by standard layout techniques |
<InputLabelWrapper> | Composition (div, <Label>, <Input>, <Icons>) | Replaced by standard layout techniques; manually add sort icons if needed |