WebberUI

Data View Morph

同一資料集在清單、表格、看板、卡片四種版面間切換時,每筆項目以共享元素動畫連續變形飛至新位置,維持使用者的空間記憶。

載入預覽⋯

Playground

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

<DataViewMorph />

安裝

npx shadcn@latest add https://webberui.com/r/data-view-morph.json

或在 components.json 設定 registries 後,改用 @webberui/data-view-morph 安裝。

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

npm install motion clsx tailwind-merge lucide-react

使用

import { DataViewMorph, type DataViewItem } from "@/components/ui/data-view-morph";

const items: DataViewItem[] = [
  { id: "auth", title: "登入流程改版", subtitle: "OAuth", group: "doing", value: 8 },
  { id: "billing", title: "計費頁重構", subtitle: "年繳方案", group: "todo", value: 5 },
  { id: "export", title: "資料匯出", subtitle: "CSV / PDF", group: "done", value: 2 },
];

<DataViewMorph
  items={items}
  groups={[
    { id: "todo", label: "待辦" },
    { id: "doing", label: "進行中" },
    { id: "done", label: "已完成" },
  ]}
/>;

四種版面共用同一份 items——切換時每筆項目以其穩定 idlayoutId 配對前後位置,連續飛移變形,而非淡出重繪。id 必須穩定(不要用陣列索引),共享元素動畫才能正確配對。

受控模式

不傳 layout 時元件自帶工具列並管理版面狀態。傳入 layout 即進入受控,可自訂觸發按鈕或與網址狀態同步;此時可用 showToolbar={false} 隱藏內建工具列。

const [view, setView] = React.useState<DataViewLayout>("board");

<DataViewMorph
  items={items}
  layout={view}
  onLayoutChange={setView}
  showToolbar={false}
/>;

Props

DataViewMorph

Prop型別預設值說明
itemsDataViewItem[]資料集;每筆在四種版面間以共享元素動畫連續變形
layoutDataViewLayout受控版面;提供時由外部主導,停用內建狀態
defaultLayoutDataViewLayout"list"非受控初始版面
onLayoutChange(layout: DataViewLayout) => void版面切換時觸發(受控與非受控皆會呼叫)
layoutsDataViewLayout[]四種全上工具列顯示哪些版面及排列順序
groupsDataViewGroup[]items 推導看板欄位順序與標題
showToolbarbooleantrue是否顯示內建版面切換工具列
headingsPartial<Record<"title" | "group" | "value", string>>表格三欄表頭文字覆寫
classNamestring附加到最外層容器的 class

DataViewItem

欄位型別說明
idstring穩定唯一識別碼,共享元素動畫據此配對同一筆項目
titlestring主標題
subtitlestring次要說明(可選)
groupstring分組鍵:看板依此分欄,其餘版面顯示為分組標籤
valuestring | number量化欄位(表格數值欄、卡片/看板角標,可選)
mediaReact.ReactNode左側媒體,未提供時以標題首字佔位(可選)

DataViewLayout

"list" | "table" | "board" | "grid"——分別對應清單、表格、看板、卡片網格。

細節

  • 共享元素變形:每筆項目以穩定 id 作 Motion layoutId,切換版面時由 LayoutGroup 統籌,項目從舊版面的邊界框以 spring 連續飛移、縮放到新版面位置,維持使用者的空間記憶
  • 抗拉伸:外框動畫尺寸,內層內容以 layout="position" 只重新定位不被縮放,文字與媒體在大幅變形時不會扭曲;媒體框四版面固定同尺寸
  • 看板分組:依 group 分欄,欄位順序與標題由 groups 指定或自資料推導,欄頭顯示該欄項目數;分組色調(點/標籤)依欄位順序循環套用
  • 實例隔離layoutId 以元件 useId() 命名空間(LayoutGroup id),同頁多個 DataViewMorph 不會互相吸附
  • 表格對齊:三欄採固定寬度格線,1fr 才能跨列對齊;分組與數值欄寬固定

可及性

  • 內建工具列為 radiogroup,各版面按鈕為 radio,支援方向鍵(←→↑↓)、Home/End 切換並移動焦點(roving tabindex)
  • 版面切換以 aria-live="polite" 朗讀目前檢視名稱
  • 資料區為語意化清單(role="list" / listitem),看板每欄為 role="group" 並帶 aria-label;分組色點為裝飾性元素,標記 aria-hidden
  • 使用者系統開啟「減少動態效果」時,共享元素動畫改為瞬時完成(項目直接跳到新位置),DOM 結構與一般狀態一致

On this page