資料展示
可排序資料表
通用資料表:點欄頭排序時列以 FLIP 滑到新位置、sticky 表頭、列展開明細、勾選與批次操作列、空狀態
後台最常用的那張表:點欄頭在遞增、遞減、不排序之間循環,每一列以 FLIP 動畫從舊位置滑到新位置,看得出誰換到了哪裡;內容超過 maxHeight 時在表內捲動、表頭 sticky 置頂。勾選任一列後,頂部以 spring 滑入批次操作列(「已選 N 筆」+動作按鈕+清除);提供 expandable 時列首多一個展開箭頭,明細列以高度動畫展開。欄位可自訂渲染與比較函式,預設比較自動判別數字、純數字字串與日期字串。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add "https://webberui.com/r/sortable-data-table.json?t=<安裝 token>"Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
400
編輯中的呼叫預設屬性
<SortableDataTable/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add "https://webberui.com/r/sortable-data-table.json?t=<安裝 token>"或在 components.json 設定 registries 後,改用 @webberui/sortable-data-table 安裝。
使用
import {
SortableDataTable,
type SortableColumn,
} from "@/components/ui/sortable-data-table";
type Order = {
id: string;
customer: string;
amount: number;
status: string;
date: string;
};
const columns: SortableColumn<Order>[] = [
{ key: "id", label: "訂單編號", sortable: true },
{ key: "customer", label: "客戶", sortable: true },
{
key: "amount",
label: "金額",
sortable: true,
align: "right",
render: (row) => `NT$ ${row.amount.toLocaleString("zh-TW")}`,
},
{ key: "status", label: "狀態" },
{ key: "date", label: "日期", sortable: true },
];
<SortableDataTable
columns={columns}
rows={orders}
defaultSort={{ key: "date", dir: "desc" }}
selectable
bulkActions={[{ id: "ship", label: "標記出貨" }]}
onBulkAction={(actionId, ids) => markShipped(ids)}
expandable={(row) => <OrderDetail order={row} />}
/>;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
columns | SortableColumn<Row>[] | — | 欄位定義(見下方子型別) |
rows | Row[] | — | 資料列,每列需有穩定唯一的 id(FLIP 動畫與勾選都依它配對) |
defaultSort | SortState | — | 非受控模式的初始排序狀態 |
sort | SortState | null | — | 受控排序狀態(null 表示未排序);不提供時為非受控模式 |
onSortChange | (sort: SortState | null) => void | — | 排序變更時回呼(點欄頭循環 asc → desc → 無排序) |
selectable | boolean | false | 是否顯示勾選欄 |
selected | string[] | — | 受控已勾選 id 清單;不提供時為非受控模式 |
onSelectedChange | (ids: string[]) => void | — | 勾選變更時回呼 |
bulkActions | BulkAction[] | — | 批次操作定義;勾選數大於 0 時操作列從頂部滑入 |
onBulkAction | (actionId: string, ids: string[]) => void | — | 點擊批次操作按鈕時回呼 |
expandable | (row: Row) => ReactNode | — | 列明細渲染函式;有提供則列首出現展開箭頭 |
maxHeight | number | 400 | 內容區最大高度(px),超過時內部捲動、表頭 sticky 置頂 |
emptyText | string | "沒有符合的資料" | 無資料時顯示的文字 |
zebra | boolean | false | 偶數列淡色底的斑馬紋 |
compact | boolean | false | 緊湊模式:較小的字級與間距 |
className | string | — | 透傳到最外層容器 |
SortableColumn
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
key | string | — | 對應資料列上的欄位鍵 |
label | ReactNode | — | 欄頭顯示內容 |
sortable | boolean | false | 是否可點擊欄頭排序 |
align | "left" | "center" | "right" | "left" | 儲存格內容對齊 |
width | string | — | 欄寬(任何合法 CSS 寬度值,例如 "120px") |
render | (row: Row) => ReactNode | — | 自訂儲存格渲染;未提供時直接顯示欄位值(空值顯示 —) |
sortFn | (a: Row, b: Row) => number | — | 自訂比較函式,一律以遞增語意撰寫,方向由元件反轉;未提供時自動判別 |
SortState
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
key | string | — | 排序中的欄位鍵 |
dir | "asc" | "desc" | — | 排序方向 |
BulkAction
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 動作識別碼,點擊時傳給 onBulkAction |
label | string | — | 按鈕文字 |
icon | ReactNode | — | 按鈕左側圖示 |
另外具名匯出 SortableRowBase、SortableColumn、SortState、BulkAction 型別,以及預設比較函式 compareCellValues(a, b),可在自訂 sortFn 裡沿用。
細節
- 排序循環:點同一欄的欄頭依序切換遞增 → 遞減 → 不排序;換點另一欄則從遞增開始。不排序時回到
rows的原始順序 - 預設比較:兩邊都是數字或純數字字串時比數值(避免
"12"排在"3"前面),都是YYYY-MM-DD/YYYY/M/D開頭的日期字串時比時間,其餘以localeCompare比較;空值無論方向都排在最後,同值列以原始順序保持穩定 - FLIP 重排:每一列以
motion的layout搭配穩定的idkey 做重排滑動。刻意不用layoutId——它是全域命名空間,同一頁有兩張表出現相同的 row id 時會誤觸共享元素動畫 - 受控與非受控:
sort與selected各自可受控或非受控;展開狀態由元件內部管理 - 表頭全選 checkbox 在部分勾選時呈現半選(indeterminate)狀態;全選只作用於目前
rows內的列
可及性
- 使用語意化的
table/thead/th scope="col";可排序欄頭是type="button"的按鈕,th帶aria-sort(ascending/descending/none) - 全選與每列 checkbox 都有
aria-label;展開按鈕帶aria-expanded與「展開/收合 明細」標籤 - 批次操作列為
role="toolbar",清除按鈕附aria-label;所有按鈕都有focus-visible焦點環 - 使用者系統開啟「減少動態效果」時,列直接重排不做 FLIP 滑動,批次操作列與明細列直接出現、欄頭箭頭直接翻轉