Chuyển tới nội dung

Pro đang mở bán trước. Cài đặt mở từ tháng 1/2027. Xem Pro

Component

NumberInput

Field số với các button bước xếp chồng ở cuối, cho số lượng, số đếm và các khoản có bước đã biết (số lượng giỏ, số khách, số ghế). Đây là input văn bản (type text, inputmode decimal), không bao giờ là type number, nên hiện được văn bản đã định dạng như 1.000 ₫. Dùng Input cho văn bản tự do, Slider khi giá trị chính xác ít quan trọng hơn khoảng.

  • npm: 2 trực tiếp, 4 tổng
  • registry: 4 trực tiếp, 4 tổng

Xem trước

Props

  • min
  • max
  • step
  • largeStep
  • clampOnBlur
  • size
  • disabled
  • readOnly
  • invalid
  • required
  • name
  • showStepButtons
  • decrementLabel
  • incrementLabel
  • locale
  • label
  • defaultValue
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"

<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Số lượng</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} decrementLabel="Giảm" incrementLabel="Tăng" step={1} defaultValue={3} />
  </FieldControl>
</Field>

Cách dùng

Khi nào dùng

  • Nhập hoặc chỉnh một số lượng có các bước hợp lệ đã biết, chẳng hạn số ghế, số khách hoặc món trong giỏ.
  • Hiện một số với định dạng theo locale, chẳng hạn một khoản tiền, trong khi vẫn sửa được.

Khi nào không dùng

  • Nhập một mã định danh như số điện thoại hoặc số thẻ. Dùng Input với chế độ nhập numeric.
  • Chọn một vị trí mà khoảng quan trọng hơn con số chính xác. Dùng Slider.
  • Nhập văn bản tự do. Dùng Input.

Biến thể

size

Chiều cao control từ size-element-sm/md/lg/xl. Chọn theo vùng; md là mặc định.

  • sm

  • mdmặc định

  • lg

  • xl

import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"

<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Số lượng</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} size="sm" step={1} defaultValue={3} />
  </FieldControl>
</Field>
<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Số lượng</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} step={1} defaultValue={3} />
  </FieldControl>
</Field>
<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Số lượng</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} size="lg" step={1} defaultValue={3} />
  </FieldControl>
</Field>
<Field className="w-full max-w-sm" invalid={false}>
  <FieldLabel>Số lượng</FieldLabel>
  <FieldControl>
    <NumberInput min={0} max={10} size="xl" step={1} defaultValue={3} />
  </FieldControl>
</Field>

Ví dụ

Số lượng

import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"

<Field>
  <FieldLabel>Số lượng</FieldLabel>
  <FieldControl>
    <NumberInput defaultValue={1} min={1} max={10} />
  </FieldControl>
</Field>

Số khách

import { NumberInput } from "@/components/ui/number-input"

<NumberInput defaultValue={2} min={1} max={16} size="lg" />

Tiền tệ

import { NumberInput } from "@/components/ui/number-input"

<NumberInput defaultValue={1000} step={1000} min={0} locale="vi-VN" formatOptions={{ style: "currency", currency: "VND" }} />

Khối lượng

kg
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { NumberInput } from "@/components/ui/number-input"

<Field>
  <FieldLabel>Weight</FieldLabel>
  <FieldControl>
    <NumberInput defaultValue={12} min={0} trailingText="kg" />
  </FieldControl>
</Field>

Nên và không nên

Nên

  • Đặt min và max khi miền giá trị có chúng (một số lượng ít nhất là 1).
  • Dùng bên trong Field để label và lỗi được nối.
  • Dùng lg hoặc xl trên màn hình ưu tiên cảm ứng để các button dễ chạm.

Không nên

  • Đừng dùng cho các số là mã định danh (số thẻ, điện thoại, mã bưu chính); dùng Input với inputmode numeric.
  • Đừng dùng cho một khoảng mà vị trí quan trọng hơn giá trị; dùng Slider.
  • Đừng gửi request mạng trên mỗi bước; debounce trong app.

Props

TênKiểuGiá trịMặc địnhMô tả
valuenumber | null--Giá trị khi bạn điều khiển field. null là field trống.
defaultValuenumber | null-nullGiá trị ở lần render đầu khi bạn không điều khiển field.
onValueChange(value: number | null) => void--Được gọi với số đã chốt (sau parse, clamp và làm tròn theo step) khi nhấn phím bước, nhấn button, Enter hoặc blur, và với null khi field bị xóa. Không được gọi cho văn bản không parse được (field hiện invalid thay vào đó) hoặc trên mỗi lần gõ.
minnumber--Giá trị thấp nhất. Các button và ArrowDown dừng tại đây.
maxnumber--Giá trị cao nhất. Phải lớn hơn min.
stepnumber-1Lượng của mỗi bước.
largeStepnumber--Lượng cho PageUp và PageDown. Mặc định bằng step nhân 10.
clampOnBlurboolean-trueClamp và làm tròn giá trị đã gõ khi field mất focus. Khi false, giá trị ngoài khoảng được giữ và field hiện invalid.
sizeenumsm | md | lg | xlmdChiều cao control từ size-element-sm/md/lg/xl. Chọn theo vùng; md là mặc định.
disabledboolean-falseKhông gõ, không có button, kiểu disabled.
readOnlyboolean-falseGiá trị đọc, focus và sao chép được nhưng không đổi được; các button bước bị ẩn và field bỏ nền. input bên trong nhận thuộc tính readOnly gốc (các binding dùng :read-only); group gốc đặt data-read-only.
invalidboolean-falseĐặt aria-invalid và viền danger.
requiredboolean-falserequired gốc trên input.
namestring--Tên field của form; giá trị gửi đi là số thuần, không phải văn bản đã định dạng.
showStepButtonsboolean-trueHiện các button giảm và tăng. Ẩn chúng trong table đặc, nơi phím mũi tên là đủ.
decrementLabelstring-DecreaseAccessible name của button giảm. Tiếng Việt: Giảm.
incrementLabelstring-IncreaseAccessible name của button tăng. Tiếng Việt: Tăng.
localestring--Locale BCP 47 cho định dạng và cho các dấu phân cách dùng khi parse. Mặc định theo ngôn ngữ của tài liệu.
formatOptionsIntl.NumberFormatOptions--Cách hiện giá trị khi field không đang được sửa, và trong aria-valuetext: currency, percent, unit, fraction digits. Khi đang focus, field hiện số thuần đã theo locale.

Slot

TênBắt buộcMô tả
leadingTextkhôngVăn bản cố định trước số, chẳng hạn một đơn vị tiền. Một đoạn nền lõm sát mép, tách khỏi giá trị bằng một đường border-border, và field được mô tả bởi nó (giống leadingText của Input).
trailingTextkhôngVăn bản cố định sau số, chẳng hạn kg. Một đoạn nền lõm sát mép, sau các button bước, tách khỏi chúng bằng một đường border-border (giống trailingText của Input).

Cấu tạo

PhầnMô tả
rootGroup vẽ hộp field: border, nền, các góc, vòng focus thông qua :has(). Các button bước nằm bên trong, ở cuối.
inputInput văn bản với role spinbutton, font-size-input, chữ số tabular, căn về phía đầu (trái ở LTR, phải ở RTL), với các button bước ở cuối.
decrementButton icon ghost phía dưới của chồng ở cuối giá trị (Tabler chevron down, 16px), ngay trước trailing text nếu có.
incrementButton icon ghost phía trên của chồng ở cuối giá trị (Tabler chevron up, 16px), ngay trước trailing text nếu có.
placeholderVăn bản placeholder của input, bằng chữ secondary như Input.

Khả năng truy cập

Role: spinbutton

PhímHành động
ArrowUp / ArrowDownTăng hoặc giảm theo step, clamp trong min và max.
PageUp / PageDownTăng hoặc giảm theo largeStep.
Home / EndĐặt min hoặc max khi chúng được đặt.
EnterChốt văn bản đã gõ: parse, clamp, làm tròn theo step.
TabChuyển tới field kế tiếp. Các button bước bị bỏ qua (tabIndex -1); các phím phía trên thay cho chúng.
  • input có type text, inputmode decimal (numeric khi step là số nguyên và min ít nhất là 0) và role spinbutton với aria-valuenow, aria-valuemin và aria-valuemax.
  • aria-valuetext là giá trị được định dạng bằng formatOptions và locale, ví dụ 1.000 ₫, nên trình đọc màn hình đọc đúng văn bản field đang hiện.
  • Parse: dấu phân cách nhóm và dấu thập phân của locale lấy từ Intl.NumberFormat formatToParts. Dấu phân cách nhóm bị bỏ, dấu thập phân đổi thành dấu chấm, rồi văn bản được parse. Chuỗi không parse được thì đặt trạng thái invalid và không gọi onValueChange.
  • Xóa field thì gọi onValueChange(null).
  • Các button bước là button thật, được đặt tên bởi decrementLabel và incrementLabel, disabled tại min và max, bị Tab bỏ qua (tabIndex -1) vì các phím mũi tên thay cho chúng.
  • Bố cục: hai button xếp chồng ở cuối giá trị, increment ở trên, ngay trước trailing text nếu có, cả với chuột và cảm ứng. Chúng không vẽ border. Trên cảm ứng, cặp button dùng chung một cột size-target-min. Mỗi button chỉ chiếm nửa cột của nó, nên hai chevron không đè lên nhau.
  • Giữ một button bước thì lặp sau một độ trễ ngắn; thông báo là giá trị cuối, không phải từng bước.
  • invalid đặt aria-invalid; Field truyền lỗi của nó bằng aria-describedby.

Cài đặt

npx werewolf-ui add number-input

Chạy npx werewolf-ui init một lần trong app trước khi thêm component. Init cài token, thêm CSS import và luật cho agent. Add cài component này cùng các dependency và giữ nguyên theme của bạn. Xem Cài đặt để biết yêu cầu và cách cài qua registry.