Mutation List Choreographer
完整 CRUD 列表編排:新增撐開間隙落入、刪除鄰項合攏、排序 FLIP 連續移動、批次級聯波次,並附 undo 幽靈項。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
4000
70
<MutationListChoreographer />
安裝
npx shadcn@latest add https://webberui.com/r/mutation-list-choreographer.json或在 components.json 設定 registries 後,改用 @webberui/mutation-list-choreographer 安裝。
安裝依賴後,從 registry JSON(/r/mutation-list-choreographer.json 的 files[0].content)複製 mutation-list-choreographer.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
元件為非受控:以 defaultItems 給定初始資料,透過 ref 取得命令式把手(MutationListHandle)從任意位置驅動增刪改排與復原。
import * as React from "react";
import {
MutationListChoreographer,
type MutationListHandle,
} from "@/components/ui/mutation-list-choreographer";
interface Task {
id: string;
title: string;
}
export function Example() {
const listRef = React.useRef<MutationListHandle<Task>>(null);
return (
<>
<button
onClick={() =>
listRef.current?.add({ id: crypto.randomUUID(), title: "新任務" })
}
>
新增
</button>
<MutationListChoreographer
ref={listRef}
defaultItems={[{ id: "1", title: "校對發佈稿" }]}
getKey={(task) => task.id}
renderItem={(task) => <span>{task.title}</span>}
/>
</>
);
}Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
defaultItems | T[] | [] | 初始項目(非受控) |
getKey | (item: T) => string | — | 從項目取得穩定唯一鍵 |
renderItem | (item: T) => ReactNode | — | 渲染單一項目內容 |
onItemsChange | (items: T[]) => void | — | 有效(非幽靈)項目變動時的回呼 |
undoTimeout | number | 4000 | 幽靈項可復原的時間窗(毫秒) |
waveInterval | number | 70 | 批次級聯每項的波次間隔(毫秒) |
removedLabel | string | "已移除" | 幽靈項文字 |
undoLabel | string | "復原" | 復原按鈕文字 |
emptyState | ReactNode | — | 列表為空時顯示的內容 |
ref | Ref<MutationListHandle<T>> | — | 命令式操作把手 |
itemClassName | string | — | 追加到每個項目容器的 className |
MutationListHandle
透過 ref.current 取得的操作方法:
| 方法 | 簽名 | 說明 |
|---|---|---|
add | (item: T, index?: number) => void | 新增項目,先撐開間隙再讓項目落入 |
remove | (key: string) => void | 以 undo 幽靈項軟刪除單一項目 |
removeMany | (keys: string[]) => void | 批次軟刪除,幽靈項與收合以級聯波次執行 |
update | (key: string, updater: (item: T) => T) => void | 就地更新項目內容 |
move | (from: number, to: number) => void | 依索引搬移,位移以 FLIP 呈現 |
sort | (compare: (a: T, b: T) => number) => void | 重新排序,位移以 FLIP 呈現 |
shuffle | () => void | 隨機洗牌 |
undo | () => void | 復原最近一次刪除 |
getItems | () => T[] | 讀取目前有效項目 |
細節
- 新增:外層列以高度
0 → auto撐開間隙,內層內容延遲0.08s才位移落入,形成「先開縫、後落定」的節奏。 - 刪除:以
undoTimeout為時間窗顯示幽靈項覆蓋原列並提供復原按鈕與倒數進度條;逾時才真正卸載,鄰項隨高度收合合攏補位。 - 排序 / 搬移 / 洗牌:僅重排底層陣列,位置變化交由 Motion 的
layout(FLIP)連續移動。 - 批次:
removeMany依傳入順序給每項一個波次序號,幽靈項淡入、倒數進度與最終收合皆按waveInterval級聯錯開。
可及性
- 列表以
role="list"標記,每次增刪改與復原透過aria-live="polite"區域即時播報。 - 幽靈項的復原鍵、示範中的完成切換與移除鍵皆為原生
button,可鍵盤聚焦操作並帶focus-visible外框。 - 使用者系統開啟「減少動態效果」時,撐開、落入、FLIP、級聯與倒數皆停用,僅保留即時的狀態切換,DOM 結構不變。