WebberUI

Anchored Settings Shell

錨點設定頁框架:左側 scroll-spy 導覽、右側兩欄設定區塊,指示器平滑跟隨捲動,未儲存變更時儲存列以彈簧滑入。

載入預覽⋯

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

<AnchoredSettingsShell />

安裝

npx shadcn@latest add https://webberui.com/r/anchored-settings-shell.json

或在 components.json 設定 registries 後,改用 @webberui/anchored-settings-shell 安裝。

安裝依賴後,從 registry JSON(/r/anchored-settings-shell.jsonfiles[0].content)複製 anchored-settings-shell.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion clsx tailwind-merge lucide-react

使用

import {
  AnchoredSettingsShell,
  SettingsSection,
  SettingsRow,
} from "@/components/ui/anchored-settings-shell";
import { Bell, User } from "lucide-react";

export function Example() {
  const [dirty, setDirty] = React.useState(false);

  return (
    <AnchoredSettingsShell
      dirty={dirty}
      onSave={() => setDirty(false)}
      onDiscard={() => setDirty(false)}
    >
      <SettingsSection id="profile" label="個人資料" icon={User} description="公開身分與聯絡方式">
        <SettingsRow label="顯示名稱" htmlFor="name">
          <input id="name" onChange={() => setDirty(true)} />
        </SettingsRow>
      </SettingsSection>

      <SettingsSection id="notifications" label="通知" icon={Bell}>
        <SettingsRow label="產品更新">{/* ...控制項 */}</SettingsRow>
      </SettingsSection>
    </AnchoredSettingsShell>
  );
}

導覽項目由 SettingsSection 子元件的 idlabelicon 自動推導,順序與內容一致。

非受控模式

省略 dirty prop 時改用內部狀態,於表單欄位的 onChange 呼叫 useSettingsDirty() 回傳的函式即可標記未儲存變更;onSaveonDiscard 後會自動重置。

import { useSettingsDirty } from "@/components/ui/anchored-settings-shell";

function NameField() {
  const markDirty = useSettingsDirty();
  return <input onChange={markDirty} />;
}

捲動容器

若設定頁位於巢狀 overflow-y-auto 容器內,將該容器的 ref 傳入 container,scroll-spy 便會以該容器為捲動根。省略時以視窗為捲動容器。

Props

AnchoredSettingsShell

Prop型別預設值說明
childrenReactNode放入 SettingsSection 子元件
dirtyboolean受控:是否有未儲存變更;省略則用內部非受控狀態
onSave() => void | Promise<void>按下儲存時觸發,Promise pending 期間顯示載入狀態
onDiscard() => void按下捨棄時觸發
containerRefObject<HTMLElement | null>巢狀捲動容器的 ref,作為 scroll-spy 根
dirtyMessagestring"你有尚未儲存的變更"儲存列提示文字
saveLabelstring"儲存變更"儲存按鈕文字
discardLabelstring"捨棄"捨棄按鈕文字
navLabelstring"設定區塊"錨點導覽的無障礙標籤

SettingsSection

Prop型別預設值說明
idstring區塊錨點 id,導覽項連結至此
labelReactNode區塊標題,同時作為導覽項文字
iconLucideIcon導覽項圖示
descriptionReactNode標題下方說明文字
childrenReactNode區塊內容(表單欄位等)

SettingsRow

Prop型別預設值說明
labelReactNode欄位標籤
hintReactNode標籤下方輔助說明
htmlForstring綁定控制項 id 的 label for
childrenReactNode右側控制項

細節

  • 導覽指示器由 useSpring 驅動 topheight,切換區塊時在導覽項之間連續滑動。
  • scroll-spy 以 IntersectionObserver 取樣各區塊可見比例,選可見度最高者為目前焦點,同時支援視窗與巢狀容器兩種捲動根。
  • 未儲存變更時底部儲存列以彈簧滑入,sticky bottom 定位使其始終貼齊捲動視區底緣。

可及性

  • 導覽為 <nav> + 錨點連結,目前區塊標記 aria-current="location";點擊平滑捲動至對應區塊。
  • 有未儲存變更時支援 ⌘/Ctrl+S 快捷鍵觸發儲存。
  • 儲存中會停用按鈕並顯示轉圈載入圖示,避免重複送出。
  • 開啟「減少動態效果」時,指示器與儲存列改為即時定位、捲動改為 auto,不影響版面與功能。
  • 每個區塊以 aria-labelledby 對應其標題,切換開關使用 role="switch"aria-checked

On this page