WebberUI

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.jsonfiles[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 會自動跑狀態機
childrenReact.ReactNodeidle/loading 顯示的內容
successChildrenReact.ReactNodechildren成功態內容
errorChildrenReact.ReactNodechildren錯誤態內容
disabledbooleanfalse停用按鈕;非受控載入中會自動停用

其餘原生 <button> 屬性皆會透傳(onDrag / onDragStart / onDragEnd / onAnimationStart / onClick 除外)。

狀態機

  • idle:預設態,可點擊;按下時輕微縮放回饋
  • loading:spinner 持續旋轉,非受控時自動鎖住按鈕避免重複送出
  • success:綠底,勾勾以 pathLength 畫線進場
  • error:紅底,叉叉依序畫出,整顆按鈕左右抖動一次
  • 內容切換以 AnimatePresencepopLayout)crossfade 淡切,按鈕寬度同步 layout 形變

可及性

  • aria-busy 於載入態標記忙碌狀態
  • 內建 aria-live="polite" 的狀態播報區,切換時朗讀「處理中/成功/錯誤」
  • 使用者系統開啟「減少動態效果」時,停用旋轉、抖動與位移,勾勾/叉叉直接顯示,仍保留內容切換以維持語意

On this page