forked from admin/hair-keeper
89 lines
1.9 KiB
TypeScript
89 lines
1.9 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import { Input } from '@/components/ui/input'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
// 数值范围类型
|
|
export interface NumberRange {
|
|
min?: number
|
|
max?: number
|
|
}
|
|
|
|
export interface NumberRangeInputProps {
|
|
value?: NumberRange
|
|
onChange?: (value: NumberRange) => void
|
|
placeholder?: {
|
|
min?: string
|
|
max?: string
|
|
}
|
|
className?: string
|
|
disabled?: boolean
|
|
step?: number
|
|
min?: number
|
|
max?: number
|
|
}
|
|
|
|
export function NumberRangeInput({
|
|
value = {},
|
|
onChange,
|
|
placeholder = { min: '最小值', max: '最大值' },
|
|
className,
|
|
disabled = false,
|
|
step = 0.01,
|
|
min,
|
|
max
|
|
}: NumberRangeInputProps) {
|
|
const handleMinChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const inputValue = e.target.value
|
|
const newMin = inputValue === '' ? undefined : parseFloat(inputValue)
|
|
|
|
if (inputValue === '' || !isNaN(newMin!)) {
|
|
onChange?.({
|
|
...value,
|
|
min: newMin
|
|
})
|
|
}
|
|
}
|
|
|
|
const handleMaxChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const inputValue = e.target.value
|
|
const newMax = inputValue === '' ? undefined : parseFloat(inputValue)
|
|
|
|
if (inputValue === '' || !isNaN(newMax!)) {
|
|
onChange?.({
|
|
...value,
|
|
max: newMax
|
|
})
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={cn('flex items-center gap-2', className)}>
|
|
<Input
|
|
type="number"
|
|
step={step}
|
|
min={min}
|
|
max={max}
|
|
value={value.min ?? ''}
|
|
onChange={handleMinChange}
|
|
placeholder={placeholder.min}
|
|
disabled={disabled}
|
|
className="flex-1"
|
|
/>
|
|
<span className="text-muted-foreground text-sm">至</span>
|
|
<Input
|
|
type="number"
|
|
step={step}
|
|
min={min}
|
|
max={max}
|
|
value={value.max ?? ''}
|
|
onChange={handleMaxChange}
|
|
placeholder={placeholder.max}
|
|
disabled={disabled}
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
)
|
|
}
|