Stepper
Hiển thị các bước của một luồng theo thứ tự và vị trí người dùng đang ở (thanh toán, onboarding, thiết lập). Bước đã hoàn thành là button để quay lại bước đó; không đến được bước sắp tới từ đây. Dùng Tabs để chuyển view, không dùng cho một chuỗi tuần tự.
Xem trước
import { Stepper } from "@/components/ui/stepper"
const checkoutSteps = [
{ id: "cart", label: "Giỏ hàng" },
{ id: "shipping", label: "Vận chuyển" },
{ id: "payment", label: "Thanh toán" },
{ id: "review", label: "Xem xét" },
]
const goToStep = (index: number) => { /* Hiện bước đó của luồng. */ }
<Stepper statusLabels={{ completed: "đã xong", current: "bước hiện tại", upcoming: "chưa tới", error: "có lỗi", goBack: "quay lại bước này" }} steps={checkoutSteps} current={1} onStepSelect={goToStep} />Cách dùng
Khi nào dùng
- Hiện vị trí của một người trong luồng theo thứ tự như thanh toán, onboarding hoặc thiết lập.
- Cho phép quay lại bước đã hoàn thành mà không mở sớm các bước sắp tới.
Khi nào không dùng
- Chuyển giữa các view của cùng nội dung. Dùng Tabs.
- Hiện mức độ hoàn thành công việc mà không đặt tên các bước tuần tự. Dùng Progress.
- Điều hướng giữa các trang hoặc phần. Dùng Sidebar hoặc BottomNav.
Biến thể
orientation
Hàng hoặc cột. Ở dạng cột, mỗi label nằm cạnh indicator và connector chạy xuống bước tiếp theo. Dùng vertical dưới md khi label không vừa, hoặc trong cột bên.
import { Stepper } from "@/components/ui/stepper"
const checkoutSteps = [
{ id: "cart", label: "Giỏ hàng" },
{ id: "shipping", label: "Vận chuyển" },
{ id: "payment", label: "Thanh toán" },
{ id: "review", label: "Xem xét" },
]
const goToStep = (index: number) => { /* Hiện bước đó của luồng. */ }
<Stepper statusLabels={{ completed: "đã xong", current: "bước hiện tại", upcoming: "chưa tới", error: "có lỗi", goBack: "quay lại bước này" }} steps={checkoutSteps} current={1} onStepSelect={goToStep} />
<Stepper orientation="vertical" statusLabels={{ completed: "đã xong", current: "bước hiện tại", upcoming: "chưa tới", error: "có lỗi", goBack: "quay lại bước này" }} steps={checkoutSteps} current={1} onStepSelect={goToStep} />size
Indicator 24, 32 hoặc 40px với icon 12, 16 hoặc 20px; label font-size-sm, -md hoặc -lg. sm cho cột bên và panel, lg cho trang onboarding thoáng. Một size cho mỗi vùng.
import { Stepper } from "@/components/ui/stepper"
const checkoutSteps = [
{ id: "cart", label: "Giỏ hàng" },
{ id: "shipping", label: "Vận chuyển" },
{ id: "payment", label: "Thanh toán" },
{ id: "review", label: "Xem xét" },
]
const goToStep = (index: number) => { /* Hiện bước đó của luồng. */ }
<Stepper size="sm" statusLabels={{ completed: "đã xong", current: "bước hiện tại", upcoming: "chưa tới", error: "có lỗi", goBack: "quay lại bước này" }} steps={checkoutSteps} current={1} onStepSelect={goToStep} />
<Stepper statusLabels={{ completed: "đã xong", current: "bước hiện tại", upcoming: "chưa tới", error: "có lỗi", goBack: "quay lại bước này" }} steps={checkoutSteps} current={1} onStepSelect={goToStep} />
<Stepper size="lg" statusLabels={{ completed: "đã xong", current: "bước hiện tại", upcoming: "chưa tới", error: "có lỗi", goBack: "quay lại bước này" }} steps={checkoutSteps} current={1} onStepSelect={goToStep} />Ví dụ
Thanh toán
import { Stepper } from "@/components/ui/stepper"
const checkoutSteps = [
{ id: "cart", label: "Giỏ hàng" },
{ id: "shipping", label: "Vận chuyển" },
{ id: "payment", label: "Thanh toán" },
{ id: "review", label: "Xem xét" },
]
const goToStep = (index: number) => { /* Hiện bước đó của luồng. */ }
<Stepper current={1} steps={checkoutSteps} onStepSelect={goToStep} />Dọc, có mô tả
Mỗi label nằm trên đường giữa của indicator, với mô tả bên dưới.
import { Stepper } from "@/components/ui/stepper"
const setupSteps = [
{ id: "account", label: "Tài khoản", description: "Email và mật khẩu" },
{ id: "team", label: "Nhóm", description: "Mời mọi người" },
{ id: "workspace", label: "Không gian làm việc", description: "Tên và địa chỉ" },
]
const goToStep = (index: number) => { /* Hiện bước đó của luồng. */ }
<Stepper orientation="vertical" current={1} steps={setupSteps} onStepSelect={goToStep} />Có icon
Icon của bước thay số cho đến khi bước hoàn thành.
import { IconCreditCard, IconReceipt, IconShoppingCart, IconTruck } from "@tabler/icons-react"
import { Stepper } from "@/components/ui/stepper"
const iconSteps = [
{ id: "cart", label: "Giỏ hàng", icon: <IconShoppingCart size={16} /> },
{ id: "shipping", label: "Vận chuyển", icon: <IconTruck size={16} /> },
{ id: "payment", label: "Thanh toán", icon: <IconCreditCard size={16} /> },
{ id: "review", label: "Xem xét", icon: <IconReceipt size={16} /> },
]
const goToStep = (index: number) => { /* Hiện bước đó của luồng. */ }
<Stepper current={2} steps={iconSteps} onStepSelect={goToStep} />Nhỏ, trong cột bên
import { Stepper } from "@/components/ui/stepper"
const setupSteps = [
{ id: "account", label: "Tài khoản", description: "Email và mật khẩu" },
{ id: "team", label: "Nhóm", description: "Mời mọi người" },
{ id: "workspace", label: "Không gian làm việc", description: "Tên và địa chỉ" },
]
const goToStep = (index: number) => { /* Hiện bước đó của luồng. */ }
<Stepper orientation="vertical" size="sm" current={1} steps={setupSteps} onStepSelect={goToStep} />Nên và không nên
Nên
- Giữ label trong một hoặc hai từ.
- Dùng 3 đến 7 bước; nhiều hơn thì cần nhóm lại.
- Cho mọi bước một icon, hoặc không bước nào.
- Cũng hiện số bước trong heading của trang (Bước 2 trên 4) khi Stepper bị ẩn trên màn hình nhỏ.
Không nên
- Đừng dùng Tabs cho một chuỗi mà người dùng phải đi theo thứ tự.
- Đừng dùng làm điều hướng tới các bước người dùng chưa đến.
- Đừng trộn hai size của Stepper trên một màn hình.
Props
| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
|---|---|---|---|---|
| steps bắt buộc | { id: string; label: string; description?: string; status?: "error"; icon?: React.ReactNode }[] | - | - | Các bước theo thứ tự. icon thay số trên bước hiện tại hoặc bước sắp tới; indicator đặt kích thước và màu, còn bước đã hoàn thành và bước lỗi giữ icon check và icon cảnh báo. status error ghi đè trạng thái hiển thị được tính của bước đó. |
| current bắt buộc | number | - | - | Chỉ số bắt đầu từ 0 của bước hiện tại. Các bước trước đó là đã hoàn thành. |
| orientation | enum | horizontal | vertical | horizontal | Hàng hoặc cột. Ở dạng cột, mỗi label nằm cạnh indicator và connector chạy xuống bước tiếp theo. Dùng vertical dưới md khi label không vừa, hoặc trong cột bên. |
| size | enum | sm | md | lg | md | Indicator 24, 32 hoặc 40px với icon 12, 16 hoặc 20px; label font-size-sm, -md hoặc -lg. sm cho cột bên và panel, lg cho trang onboarding thoáng. Một size cho mỗi vùng. |
| onStepSelect | (index: number) => void | - | - | Được gọi với chỉ số khi một bước đã hoàn thành được kích hoạt, để quay lại bước đó. Type bắt buộc khi current lớn hơn 0, vì một bước trước đó có thể được kích hoạt. |
| statusLabels | { completed?: string; current?: string; upcoming?: string; error?: string; goBack?: string } | - | - | Từ thêm vào tên truy cập của mỗi bước, dạng {label}, {status}: { completed, current, upcoming, error }. Mặc định: completed, current step, not reached, has an error. Tiếng Việt: đã xong, bước hiện tại, chưa đến, có lỗi. goBack đặt tên button của bước đã hoàn thành: EN `{label}, completed, go back to this step`, VI `{label}, đã xong, quay lại bước này`. |
Slot
| Tên | Bắt buộc | Mô tả |
|---|---|---|
| stepIcon | không | Icon tùy chọn cho mỗi bước, thay số trên bước hiện tại hoặc bước sắp tới (không bao giờ là icon check hoặc icon cảnh báo). Bất kỳ node SVG nào; indicator đặt kích thước và màu. |
Cấu tạo
| Phần | Mô tả |
|---|---|
| root | ol sắp các bước thành một hàng hoặc một cột. |
| step | Một li: nội dung (button trên bước đã hoàn thành) và các connector. Ở dạng hàng, indicator nằm trên label; ở dạng cột, nằm cạnh label, với khoảng trống bên dưới cho connector. |
| indicator | Vòng tròn đặc, không có outline: số bước hoặc icon của bước, dấu check khi hoàn thành, icon cảnh báo khi lỗi. 24, 32 hoặc 40px theo size. |
| stepIndicator | Indicator bên trong button của bước đã hoàn thành. Hover và nhấn chỉ làm đậm vòng tròn, không phải một khung quanh label. |
| icon | Bất kỳ icon nào trong indicator (icon của bước, dấu check, cảnh báo): 12, 16 hoặc 20px theo size, màu theo trạng thái của bước. |
| label | Tiêu đề bước, độ đậm medium ở bước hiện tại. Ở dạng cột, label nằm trên đường giữa của indicator. |
| description | Dòng phụ tùy chọn dưới label. |
| connector | Đường 2px giữa hai indicator, có khoảng hở ở mỗi đầu; accent khi bước trước đã hoàn thành. |
Khả năng truy cập
Role: list
| Phím | Hành động |
|---|---|
| Tab | Chuyển tới bước đã hoàn thành tiếp theo (một button). Bước hiện tại và bước sắp tới không focus được. |
| Enter / Space | Quay lại bước đã hoàn thành đang focus thông qua onStepSelect. |
- Một danh sách có thứ tự. Bước hiện tại có aria-current="step" và đặt data-current.
- Bước đã hoàn thành là một button có tên EN `{label}, completed, go back to this step`, VI `{label}, đã xong, quay lại bước này`.
- Văn bản truy cập của mỗi bước gồm từ trạng thái (completed, current, upcoming, error) từ statusLabels, không chỉ icon hoặc màu.
- Indicator bị ẩn với trình đọc màn hình: danh sách cho vị trí và label cho trạng thái. Các đường connector chỉ để trang trí (aria-hidden).
- Bước có lỗi hiện icon cảnh báo trên nền danger mềm và từ từ statusLabels.error.
- Button của bước đã hoàn thành đạt size-target-min theo chiều cao trên cảm ứng, qua ::before trong suốt.
Cài đặt
npx werewolf-ui add stepperChạ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.