Stateful Button
idle → loading → success / error 的狀態機按鈕,async onClick 自動切換,內容以 crossfade 淡切、寬度隨之形變。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<StatefulButton />
安裝
npx shadcn@latest add https://webberui.com/r/stateful-button.json或在 components.json 設定 registries 後,改用 @webberui/stateful-button 安裝。
安裝依賴後,從 registry JSON(/r/stateful-button.json 的 files[0].content)複製 stateful-button.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
import { StatefulButton } from "@/components/ui/stateful-button";
async function save() {
await fetch("/api/save", { method: "POST" });
}
<StatefulButton onClick={save} successChildren="已儲存" errorChildren="失敗">
儲存
</StatefulButton>;點擊時只要 onClick 回傳一個 Promise,按鈕便自動進入 loading;Promise resolve 後顯示 success、reject 後顯示 error,1.5 秒後自動回到 idle。
受控模式
傳入 state 即改為受控,內建的自動切換與重置會關閉,狀態完全由外部主導:
const [state, setState] = React.useState<"idle" | "loading" | "success" | "error">("idle");
<StatefulButton state={state} onClick={() => setState("loading")}>
送出
</StatefulButton>;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
state | "idle" | "loading" | "success" | "error" | — | 受控狀態;提供時由外部主導,關閉內建自動切換與重置 |
onClick | (e) => void | Promise<unknown> | — | 點擊處理器;非受控時回傳 Promise 會自動跑狀態機 |
children | React.ReactNode | — | idle/loading 顯示的內容 |
successChildren | React.ReactNode | children | 成功態內容 |
errorChildren | React.ReactNode | children | 錯誤態內容 |
disabled | boolean | false | 停用按鈕;非受控載入中會自動停用 |
其餘原生 <button> 屬性皆會透傳(onDrag / onDragStart / onDragEnd / onAnimationStart / onClick 除外)。
狀態機
idle:預設態,可點擊;按下時輕微縮放回饋loading:spinner 持續旋轉,非受控時自動鎖住按鈕避免重複送出success:綠底,勾勾以pathLength畫線進場error:紅底,叉叉依序畫出,整顆按鈕左右抖動一次- 內容切換以
AnimatePresence(popLayout)crossfade 淡切,按鈕寬度同步layout形變
可及性
aria-busy於載入態標記忙碌狀態- 內建
aria-live="polite"的狀態播報區,切換時朗讀「處理中/成功/錯誤」 - 使用者系統開啟「減少動態效果」時,停用旋轉、抖動與位移,勾勾/叉叉直接顯示,仍保留內容切換以維持語意