NumberInput
WhereUIช่องกรอกตัวเลขพร้อมปุ่มเพิ่ม/ลด พิมพ์ค่าเองได้ กดลูกศรขึ้นลงได้ และกดค้างที่ปุ่มเพื่อเพิ่ม/ลดต่อเนื่อง
ลา 3 วันทำการ
ติดตั้ง
Terminal
pnpm dlx shadcn@latest add @whereui/number-inputต้องผูก registry และติดตั้ง @whereui/theme นำหน้าเสมอเมื่อติดตั้งครั้งแรก
การใช้งาน
import { NumberInput } from "@/components/ui/number-input"const [days, setDays] = React.useState<number | null>(null)<NumberInput value={days} onChange={setDays} min={0} max={30} />ตัวอย่างเพิ่มเติม
จำกัดค่าและสถานะปิดใช้งาน
ชั่วโมงล่วงเวลา เพิ่มทีละ 0.5
ปิดใช้งาน
Props
| prop | type | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
| value | number | null | — | ค่าตัวเลข (null = ว่าง) |
| onChange | (value: number | null) => void | — | ส่งค่าตัวเลขหรือ null เมื่อว่าง |
| min | number | — | ค่าต่ำสุด |
| max | number | — | ค่าสูงสุด |
| step | number | 1 | ระยะเพิ่ม/ลดของปุ่มและปุ่มลูกศร (กด Alt/Shift เพื่อย่อ/ขยายระยะได้) |
| disabled | boolean | — | ปิดการใช้งาน |
| placeholder | string | — | ข้อความเมื่อว่าง |
ข้อควรรู้
- ตัวเลขแสดงคั่นหลักพันอัตโนมัติตามภาษาไทย เช่น 1,500
- คีย์ลูกศรขึ้น-ลง เพิ่ม/ลดค่า Alt = ขั้นเล็ก Shift = ขั้นใหญ่
ซอร์สโค้ด
components/ui/number-input.tsx
"use client"import * as React from "react"import { NumberField } from "@base-ui/react/number-field"import { MinusIcon, PlusIcon } from "lucide-react"import { cn } from "cn"function NumberInput({ value, onChange, min, max, step = 1, disabled, placeholder, className, ...props}: Omit<React.ComponentProps<"input">, "value" | "onChange" | "size" | "type"> & { /** ค่าตัวเลข (null = ว่าง) */ value?: number | null onChange?: (value: number | null) => void min?: number max?: number step?: number}) { return ( <NumberField.Root data-slot="number-input" value={value ?? null} onValueChange={(v) => onChange?.(v)} min={min} max={max} step={step} disabled={disabled} locale="th-TH" className={cn( "flex h-8 w-full min-w-0 items-center rounded-lg border border-input bg-transparent transition-colors outline-none focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 data-invalid:border-destructive data-invalid:ring-3 data-invalid:ring-destructive/20 disabled:cursor-not-allowed disabled:opacity-50 disabled:pointer-events-none md:text-sm dark:bg-input/30 dark:data-invalid:border-destructive/50 dark:data-invalid:ring-destructive/40", className )} > <NumberField.Group className="flex w-full min-w-0 items-center"> <NumberField.Decrement type="button" aria-label="ลดค่า" className="flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-lg text-muted-foreground outline-none select-none hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:pointer-events-none" > <MinusIcon aria-hidden="true" /> </NumberField.Decrement> <NumberField.Input placeholder={placeholder} className="w-full min-w-0 bg-transparent py-1 text-center text-base outline-none placeholder:text-muted-foreground md:text-sm" {...props} /> <NumberField.Increment type="button" aria-label="เพิ่มค่า" className="flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-lg text-muted-foreground outline-none select-none hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:pointer-events-none" > <PlusIcon aria-hidden="true" /> </NumberField.Increment> </NumberField.Group> </NumberField.Root> )}export { NumberInput }