123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- import type { FC } from 'react'
- import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react'
- import Input, { type InputProps } from '../input'
- import classNames from '@/utils/classnames'
- export type InputNumberProps = {
- unit?: string
- value?: number
- onChange: (value?: number) => void
- amount?: number
- size?: 'sm' | 'md'
- max?: number
- min?: number
- defaultValue?: number
- disabled?: boolean
- wrapClassName?: string
- controlWrapClassName?: string
- controlClassName?: string
- } & Omit<InputProps, 'value' | 'onChange' | 'size' | 'min' | 'max' | 'defaultValue'>
- export const InputNumber: FC<InputNumberProps> = (props) => {
- const { unit, className, onChange, amount = 1, value, size = 'md', max, min, defaultValue, wrapClassName, controlWrapClassName, controlClassName, disabled, ...rest } = props
- const isValidValue = (v: number) => {
- if (max && v > max)
- return false
- if (min && v < min)
- return false
- return true
- }
- const inc = () => {
- if (disabled) return
- if (value === undefined) {
- onChange(defaultValue)
- return
- }
- const newValue = value + amount
- if (!isValidValue(newValue))
- return
- onChange(newValue)
- }
- const dec = () => {
- if (disabled) return
- if (value === undefined) {
- onChange(defaultValue)
- return
- }
- const newValue = value - amount
- if (!isValidValue(newValue))
- return
- onChange(newValue)
- }
- return <div className={classNames('flex', wrapClassName)}>
- <Input {...rest}
- // disable default controller
- type='text'
- className={classNames('rounded-r-none', className)}
- value={value}
- max={max}
- min={min}
- disabled={disabled}
- onChange={(e) => {
- if (e.target.value === '')
- onChange(undefined)
- const parsed = Number(e.target.value)
- if (Number.isNaN(parsed))
- return
- if (!isValidValue(parsed))
- return
- onChange(parsed)
- }}
- unit={unit}
- />
- <div className={classNames(
- 'flex flex-col bg-components-input-bg-normal rounded-r-md border-l border-divider-subtle text-text-tertiary focus:shadow-xs',
- disabled && 'opacity-50 cursor-not-allowed',
- controlWrapClassName)}
- >
- <button onClick={inc} disabled={disabled} className={classNames(
- size === 'sm' ? 'pt-1' : 'pt-1.5',
- 'px-1.5 hover:bg-components-input-bg-hover',
- disabled && 'cursor-not-allowed hover:bg-transparent',
- controlClassName,
- )}>
- <RiArrowUpSLine className='size-3' />
- </button>
- <button
- onClick={dec}
- disabled={disabled}
- className={classNames(
- size === 'sm' ? 'pb-1' : 'pb-1.5',
- 'px-1.5 hover:bg-components-input-bg-hover',
- disabled && 'cursor-not-allowed hover:bg-transparent',
- controlClassName,
- )}>
- <RiArrowDownSLine className='size-3' />
- </button>
- </div>
- </div>
- }
|