ข้ามไปเนื้อหา
WhereUI

ค้นหาในเอกสาร

ค้นหาชื่อ component หรือหน้าเอกสาร

Stepper

WhereUI

แสดงขั้นตอนเป็นแนวนอนหรือแนวตั้ง พร้อมสถานะเสร็จแล้ว ขั้นปัจจุบัน รอ และผิดพลาด เหมาะกับฟอร์มหลายขั้น เช่น การสมัครงาน หรือการส่งคำขอ

  1. ข้อมูลผู้สมัคร
  2. ประวัติการทำงาน
  3. แนบเอกสาร
  4. ยืนยันส่งคำขอ

ติดตั้ง

Terminal
pnpm dlx shadcn@latest add @whereui/stepper

ต้องผูก registry และติดตั้ง @whereui/theme นำหน้าเสมอเมื่อติดตั้งครั้งแรก

การใช้งาน

import { Stepper } from "@/components/ui/stepper"<Stepper  steps={[{ label: "กรอกข้อมูล" }, { label: "แนบเอกสาร" }, { label: "ยืนยัน" }]}  current={1}/>

ตัวอย่างเพิ่มเติม

แนวตั้งพร้อมคำอธิบาย
  1. ข้อมูลผู้สมัคร
    ชื่อ ตำแหน่งที่สมัคร และช่องทางติดต่อ
  2. ประวัติการทำงาน
    ประสบการณ์ย้อนหลังไม่เกิน 3 แห่ง
  3. แนบเอกสาร
    ใบรับรองและผลงานเป็น PDF
ขั้นปัจจุบันผิดพลาด
  1. ข้อมูลพนักงาน
  2. ระยะเวลาลา
  3. เหตุผล
  4. ยืนยัน

Props

proptypeค่าเริ่มต้นคำอธิบาย
steps{ label: string; description?: string }[]—รายการขั้นตอนเรียงจากซ้ายไปขวา (หรือบนลงล่าง)
currentnumber0ดัชนีขั้นปัจจุบัน เริ่มที่ 0 — ขั้นก่อนหน้าถือว่าเสร็จแล้ว
errorbooleanfalseขั้นปัจจุบันผิดพลาด เช่น ตรวจฟอร์มไม่ผ่าน วงกลมจะเป็นสีแดงพร้อมเครื่องหมายกากบาท
orientation"horizontal" | "vertical""horizontal"ทิศทางการเรียงขั้นตอน

ข้อควรรู้

  • บนจอ 375px ควรใช้ไม่เกิน 4 ขั้นในแนวนอน หรือเปลี่ยนเป็นแนวตั้งเมื่อป้ายยาว
  • ขั้นปัจจุบันมี aria-current="step" เพื่อการอ่านด้วยโปรแกรมอ่านหน้าจอ

ซอร์สโค้ด

components/ui/stepper.tsx
import * as React from "react"import { CheckIcon, XIcon } from "lucide-react"import { cn } from "cn"export type StepperStep = {  label: string  description?: string}// สถานะของแต่ละขั้น คำนวณจาก current + error (error ใช้กับขั้นปัจจุบัน)function getStepStatus(  index: number,  current: number,  error: boolean): "complete" | "current" | "error" | "upcoming" {  if (index < current) return "complete"  if (index === current) return error ? "error" : "current"  return "upcoming"}function Stepper({  className,  steps,  current = 0,  error = false,  orientation = "horizontal",  ...props}: React.ComponentProps<"ol"> & {  steps: StepperStep[]  /** ดัชนีขั้นปัจจุบัน (เริ่มที่ 0) */  current?: number  /** ขั้นปัจจุบันผิดพลาด เช่น ตรวจฟอร์มไม่ผ่าน */  error?: boolean  orientation?: "horizontal" | "vertical"}) {  return (    <ol      data-slot="stepper"      data-orientation={orientation}      className={cn(        "flex w-full",        orientation === "horizontal" ? "items-start" : "flex-col",        className      )}      {...props}    >      {steps.map((step, i) => {        const status = getStepStatus(i, current, error)        const circle = (          <span            aria-hidden="true"            className={cn(              "grid size-7 shrink-0 place-items-center rounded-full text-xs font-semibold [&_svg]:size-4",              status === "upcoming" && "bg-muted text-muted-foreground",              (status === "complete" || status === "current") &&                "bg-primary text-primary-foreground",              status === "error" && "bg-destructive text-primary-foreground"            )}          >            {status === "complete" ? (              <CheckIcon />            ) : status === "error" ? (              <XIcon />            ) : (              i + 1            )}          </span>        )        const labelBlock = (          <div className={cn("min-w-0", orientation === "horizontal" && "max-w-44")}>            <div              className={cn(                "text-sm leading-snug font-medium",                status === "upcoming" && "text-muted-foreground"              )}            >              {step.label}            </div>            {step.description ? (              <div className="mt-0.5 text-xs text-muted-foreground">                {step.description}              </div>            ) : null}          </div>        )        return (          <li            key={i}            aria-current={status === "current" ? "step" : undefined}            className={cn(              orientation === "horizontal"                ? "flex min-w-0 flex-1 flex-col items-center gap-1.5 text-center"                : "relative flex gap-3 pb-6 last:pb-0"            )}          >            {orientation === "horizontal" ? (              <>                <div className="flex w-full items-center" aria-hidden="true">                  <span                    className={cn(                      "h-px flex-1",                      i === 0 && "invisible",                      i <= current ? "bg-primary" : "bg-border"                    )}                  />                  {circle}                  <span                    className={cn(                      "h-px flex-1",                      i === steps.length - 1 && "invisible",                      i < current ? "bg-primary" : "bg-border"                    )}                  />                </div>                {labelBlock}              </>            ) : (              <>                <div className="flex flex-col items-center self-stretch">                  {circle}                  {i < steps.length - 1 ? (                    <span                      aria-hidden="true"                      className={cn(                        "mt-1 w-px flex-1",                        i < current ? "bg-primary" : "bg-border"                      )}                    />                  ) : null}                </div>                <div className="pt-0.5">{labelBlock}</div>              </>            )}          </li>        )      })}    </ol>  )}export { Stepper }