互動編排
總開關連鎖
總開關連鎖 React 元件:翻動一個總開關,網格中的子開關依與起點的距離像漣漪般依序跟著啟用或關閉,每格切換時閃一圈光環;也能單獨切換子開關,總開關自動呈現全開、全關或部分三態。
把一整組設定收在一個總開關底下。翻動總開關時,元件量測每個子格中心到起點(總開關、網格左上角或網格中心)的距離,依距離比例排出延遲,子開關像漣漪一樣由近到遠依序切換,切換瞬間該格閃一圈強調色光環。子開關也能單獨切換,總開關會即時反映全開、全關或部分(中間顯示「—」)三種狀態。停用的子項不參與連鎖。開關本體(track + thumb)在元件內自行實作,不依賴其他元件;值支援受控/非受控雙模式。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add master-switch-cascadePlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
600
3
編輯中的呼叫預設屬性
<MasterSwitchCascade/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/master-switch-cascade.json或在 components.json 設定 registries 後,改用 @webberui/master-switch-cascade 安裝。
使用
import { Package, CreditCard, Truck, MessageSquare } from "lucide-react";
import { MasterSwitchCascade } from "@/components/ui/master-switch-cascade";
const items = [
{ id: "orders", label: "訂單更新", icon: <Package /> },
{ id: "payments", label: "付款提醒", icon: <CreditCard /> },
{ id: "shipping", label: "出貨通知", icon: <Truck /> },
{ id: "reviews", label: "評論回覆", icon: <MessageSquare /> },
];
export function NotificationSettings() {
const [value, setValue] = React.useState<Record<string, boolean>>({
orders: true,
});
return (
<MasterSwitchCascade
items={items}
value={value}
onChange={setValue}
masterLabel="全部啟用"
origin="master"
spread={600}
columns={4}
/>
);
}Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
items | MasterSwitchItem[] | — | 子開關清單 |
value | Record<string, boolean> | — | 受控值:以 item id 為鍵的開關狀態;未提供時由元件內部管理(非受控) |
defaultValue | Record<string, boolean> | {} | 非受控模式的初始狀態;缺少的 id 視為關閉 |
onChange | (value: Record<string, boolean>) => void | — | 任一子開關改變時回呼;連鎖期間每個子開關切換都會各回呼一次 |
masterLabel | string | "全部啟用" | 總開關列的標題,也是總開關的無障礙名稱 |
origin | "master" | "top-left" | "center" | "master" | 漣漪起點:總開關中心、網格左上角或網格中心 |
spread | number | 600 | 連鎖總時長(ms):最遠的子開關在這個延遲後切換 |
columns | number | 4 | 網格欄數 |
accent | string | "#3b82f6" | 強調色,寫入 --wb-master-switch-accent |
className | string | — | 透傳到最外層容器 |
MasterSwitchItem
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別碼,同時是 value 物件的鍵 |
label | string | — | 顯示名稱,也是該開關的無障礙名稱 |
icon | ReactNode | — | 顯示在格子左上角的圖示(建議 lucide-react 圖示) |
disabled | boolean | false | 停用:不可單獨切換,也不參與總開關連鎖 |
細節
- 延遲公式:每格延遲 = 該格中心到起點的距離 ÷ 所有可參與格子的最遠距離 ×
spread。最遠距離以所有未停用的格子計算,所以同一格每次的延遲都一樣,節奏穩定。 - 切換規則:總開關為全開時翻動會全部關閉;全關或部分時翻動會全部啟用。只有狀態需要改變的子開關會排入佇列並閃光環。
- 佇列管理:子開關的延遲切換用
setTimeout佇列排程;再次翻動總開關會先清空佇列,從當下狀態重新起算;元件卸載時一併清除。連鎖途中單獨切換某格,會取消那一格尚未觸發的切換,以手動為準。 - 連鎖進行中,總開關直接顯示目標狀態,不會中途閃成「部分」。
- 三態只計算未停用的子項:停用項的開關狀態不影響總開關。
- CSS 變數:
--wb-master-switch-duration(thumb 位移與 track 變色,預設 150ms)、--wb-master-switch-ring(光環時長,預設 400ms)、--wb-master-switch-accent(強調色,由accentprop 寫入)。
可及性
- 每個子開關是
type="button"的role="switch",以aria-checked表示開關狀態,無障礙名稱來自格內的名稱文字;停用項設定disabled - 總開關需要表達「部分」狀態,而
role="switch"不支援aria-checked="mixed",因此總開關一律使用role="checkbox",aria-checked為true/false/mixed,並以aria-labelledby指向總開關標題 - 子開關網格為
role="group",同樣以總開關標題命名 - 所有開關都是原生按鈕,空白鍵與 Enter 皆可切換,並有 focus-visible 外框
- 連鎖完成後,
role="status"、aria-live="polite"區域宣告結果,例如「已啟用 12 項」或「已關閉 11 項」 - 使用者系統開啟「減少動態效果」時,所有子開關同時切換、不閃光環,thumb 也不播放過渡動畫