Yugen UI

Stepper

Stepper cho biết người dùng đang ở bước nào trong một quy trình nhiều bước; Wizard ghép Stepper với nội dung từng bước và nút Quay lại / Tiếp tục.

Stepper là danh sách có thứ tự (ngang hoặc dọc); bước hiện tại có aria-current="step", chỉ báo số / dấu tích / dấu chấm than luôn kèm chữ ẩn ("đã hoàn thành", "có lỗi"…) nên không chỉ dựa vào màu. Trạng thái tự suy từ activeStep hoặc đặt tường minh bằng status. Có onStepChange thì các bước thành nút: một tab stop, mũi tên (đảo trong RTL) / Home / End; linear chặn nhảy tới bước chưa qua. Đường nối là phần tử flex nên tự đảo chiều trong RTL. Bản ngang thu gọn thành "Bước 2/4 · Tên bước" + ProgressBar khi khung chứa hẹp hơn 32rem (container query). Wizard chạy validate (đồng bộ hoặc Promise) trước khi sang bước sau; chờ quá 300ms thì nút hiện Spinner (Button loading), lỗi hiện role="alert" và bước chuyển trạng thái error.

Bước 1/4 · Tài khoản

Bước 1/4: Tài khoản
Thử "da-dung@yugen.vn" để thấy lỗi từ server.

Import

Cài từ npm, chép mã nguồn vào dự án bằng @yugen-ui/cli hoặc shadcn CLI, hoặc import thẳng khi làm việc trong repo này.

TSX
// npm install yugen-uiimport { Stepper, Step, Wizard, type WizardStep, type StepStatus } from "yugen-ui";

Biến thể

Mỗi ô là một mẫu độc lập: hover để Copy, hoặc bấm Code để xem mã nguồn.

Bước 2/4 · Giao hàng

Ngang, điều khiển, không linear

activeStep + onStepChange: bấm bước bất kỳ hoặc dùng mũi tên rồi Enter. Thu hẹp cửa sổ để thấy bản thu gọn.

  1. Bước 1: Đã đặt hàng, đã hoàn thành08:12 · 21/10
  2. Bước 2: Thanh toán, đã hoàn thànhĐã xác nhận chuyển khoản
  3. Bước 3: Đóng gói, có lỗiThiếu hàng tại kho Thủ Đức
  4. Bước 4: Giao hàng, chưa thực hiện

Dọc, trạng thái lỗi, nội dung trong bước

Không có onStepChange → chỉ hiển thị. children của Step hiện thẳng cột chữ ở bản dọc.

Bước 2/4 · Thu nhập

Thu gọn trong khung hẹp

Khung 288px < 32rem → "Bước 2/4 · Thu nhập" + ProgressBar. Tắt bằng responsive={false}.

Cách dùng

Nên
  • Giữ 3–6 bước với tiêu đề ngắn (danh từ); gộp bước nếu quy trình dài hơn.
  • Kiểm tra từng bước bằng validate và giải thích lỗi bằng chữ — Wizard tự hiện thông báo và đánh dấu bước lỗi.
  • Dùng linear cho quy trình mà bước sau phụ thuộc bước trước (thanh toán, mở tài khoản).
Không nên
  • Không dùng Stepper thay Tabs cho các phần độc lập, không có thứ tự.
  • Không chỉ đổi màu để báo lỗi — dùng status="error" (có dấu chấm than và chữ ẩn "có lỗi").
  • Không xoá dữ liệu đã nhập khi người dùng Quay lại.

Props

Prop · KiểuMặc địnhMô tả
activeStep / defaultActiveStepnumber / numberMặc định: — / 0Stepper: chỉ số bước hiện tại, từ 0 (controlled / uncontrolled).
onStepChange(index: number) => voidMặc định: —Có → các bước là nút bấm (roving focus, mũi tên / Home / End).
orientation"horizontal" | "vertical"Mặc định: "horizontal"Hướng hiển thị.
linearbooleanMặc định: false (Wizard: true)Chỉ cho quay lại hoặc tới bước đã complete; bước khác có aria-disabled.
responsivebooleanMặc định: trueNgang + khung < 32rem → "Bước n/N · Tên" + ProgressBar.
Step.title / descriptionReactNodeMặc định: —Tiêu đề và mô tả ngắn của bước.
Step.status"complete" | "current" | "upcoming" | "error"Mặc định: —Ghi đè trạng thái tự suy từ vị trí.
Step.optional / disabledbooleanMặc định: —Hiện "Tuỳ chọn" / không cho chuyển tới.
Wizard.stepsWizardStep[]Mặc định: —{ id?, title, description?, optional?, content, validate? }.
WizardStep.validate() => boolean | string | void | Promise<…>Mặc định: —false / chuỗi → chặn và báo lỗi; Promise → nút Tiếp tục loading (sau 300ms).
Wizard.activeStep / defaultActiveStep / onStepChangenumber / number / (index) => voidMặc định: —Điều khiển hoặc không điều khiển bước hiện tại.
Wizard.onFinish() => void | Promise<void>Mặc định: —Gọi khi bước cuối qua validate; Promise → nút Hoàn tất loading.
Wizard.backLabel / nextLabel / finishLabelReactNodeMặc định: "Back" / "Next" / "Finish"Nhãn nút của Wizard.

Messages

Chữ có sẵn của component: nhãn, aria-label, thông báo cho trình đọc màn hình.

Mặc định là tiếng Anh. Đổi qua prop messages của YugenProvider, theo namespace bên dưới; messagesVi chứa bản tiếng Việt. Xem Ngôn ngữ giao diện.

messages.stepper

Khoá · KiểuMặc định (tiếng Anh)messagesVi
labelstring

aria-label mặc định của danh sách bước.

Mặc định (tiếng Anh)"Steps"messagesVi"Các bước"
step(index: number) => string

Tiền tố ẩn (sr-only) trước tiêu đề bước, theo sau là ":"; index đếm từ 1.

Mặc định (tiếng Anh)(index) => `Step ${index}`messagesVi(index) => `Bước ${index}`
completestring

Trạng thái đọc sau tiêu đề bước (sr-only).

Mặc định (tiếng Anh)"completed"messagesVi"đã hoàn thành"
currentstringMặc định (tiếng Anh)"current step"messagesVi"bước hiện tại"
upcomingstringMặc định (tiếng Anh)"not started"messagesVi"chưa thực hiện"
errorstringMặc định (tiếng Anh)"error"messagesVi"có lỗi"
optionalstring

Nhãn của bước optional.

Mặc định (tiếng Anh)"Optional"messagesVi"Tuỳ chọn"
progress(current: number, total: number) => string

Dòng tóm tắt khi Stepper ngang thu gọn ở màn hẹp; current đếm từ 1.

Mặc định (tiếng Anh)(current, total) => `Step ${current} of ${total}`messagesVi(current, total) => `Bước ${current}/${total}`
progressLabel(current: number, total: number) => string

aria-label thanh tiến độ khi thu gọn.

Mặc định (tiếng Anh)(current, total) => `Step ${current} of ${total}`messagesVi(current, total) => `Bước ${current} trên ${total}`

messages.wizard

Khoá · KiểuMặc định (tiếng Anh)messagesVi
backstringMặc định (tiếng Anh)"Back"messagesVi"Quay lại"
nextstringMặc định (tiếng Anh)"Next"messagesVi"Tiếp tục"
finishstringMặc định (tiếng Anh)"Finish"messagesVi"Hoàn tất"
invalidstring

Lỗi khi validate trả false.

Mặc định (tiếng Anh)"Check the information in this step."messagesVi"Vui lòng kiểm tra lại thông tin ở bước này."
validateFailedstring

Lỗi khi validate ném lỗi.

Mặc định (tiếng Anh)"Couldn't validate this step. Try again."messagesVi"Không kiểm tra được bước này. Hãy thử lại."
panel(current: number, total: number) => string

Tên ẩn của vùng nội dung bước, theo sau là ": " và tiêu đề bước; current đếm từ 1.

Mặc định (tiếng Anh)(current, total) => `Step ${current} of ${total}`messagesVi(current, total) => `Bước ${current}/${total}`