互動編排

總開關連鎖

總開關連鎖 React 元件:翻動一個總開關,網格中的子開關依與起點的距離像漣漪般依序跟著啟用或關閉,每格切換時閃一圈光環;也能單獨切換子開關,總開關自動呈現全開、全關或部分三態。

把一整組設定收在一個總開關底下。翻動總開關時,元件量測每個子格中心到起點(總開關、網格左上角或網格中心)的距離,依距離比例排出延遲,子開關像漣漪一樣由近到遠依序切換,切換瞬間該格閃一圈強調色光環。子開關也能單獨切換,總開關會即時反映全開、全關或部分(中間顯示「—」)三種狀態。停用的子項不參與連鎖。開關本體(track + thumb)在元件內自行實作,不依賴其他元件;值支援受控/非受控雙模式。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add master-switch-cascade

還沒有專案,或安裝遇到問題?看快速開始 →

Playground

即時調整 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型別預設值說明
itemsMasterSwitchItem[]—子開關清單
valueRecord<string, boolean>—受控值:以 item id 為鍵的開關狀態;未提供時由元件內部管理(非受控)
defaultValueRecord<string, boolean>{}非受控模式的初始狀態;缺少的 id 視為關閉
onChange(value: Record<string, boolean>) => void—任一子開關改變時回呼;連鎖期間每個子開關切換都會各回呼一次
masterLabelstring"全部啟用"總開關列的標題,也是總開關的無障礙名稱
origin"master" | "top-left" | "center""master"漣漪起點:總開關中心、網格左上角或網格中心
spreadnumber600連鎖總時長(ms):最遠的子開關在這個延遲後切換
columnsnumber4網格欄數
accentstring"#3b82f6"強調色,寫入 --wb-master-switch-accent
classNamestring—透傳到最外層容器

MasterSwitchItem

欄位型別預設值說明
idstring—唯一識別碼,同時是 value 物件的鍵
labelstring—顯示名稱,也是該開關的無障礙名稱
iconReactNode—顯示在格子左上角的圖示(建議 lucide-react 圖示)
disabledbooleanfalse停用:不可單獨切換,也不參與總開關連鎖

細節

  • 延遲公式:每格延遲 = 該格中心到起點的距離 ÷ 所有可參與格子的最遠距離 × spread。最遠距離以所有未停用的格子計算,所以同一格每次的延遲都一樣,節奏穩定。
  • 切換規則:總開關為全開時翻動會全部關閉;全關或部分時翻動會全部啟用。只有狀態需要改變的子開關會排入佇列並閃光環。
  • 佇列管理:子開關的延遲切換用 setTimeout 佇列排程;再次翻動總開關會先清空佇列,從當下狀態重新起算;元件卸載時一併清除。連鎖途中單獨切換某格,會取消那一格尚未觸發的切換,以手動為準。
  • 連鎖進行中,總開關直接顯示目標狀態,不會中途閃成「部分」。
  • 三態只計算未停用的子項:停用項的開關狀態不影響總開關。
  • CSS 變數:--wb-master-switch-duration(thumb 位移與 track 變色,預設 150ms)、--wb-master-switch-ring(光環時長,預設 400ms)、--wb-master-switch-accent(強調色,由 accent prop 寫入)。

可及性

  • 每個子開關是 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 也不播放過渡動畫