資料展示

可排序資料表

通用資料表:點欄頭排序時列以 FLIP 滑到新位置、sticky 表頭、列展開明細、勾選與批次操作列、空狀態

這是 WebberUI Pro 元件

下方的安裝指令需要授權才能執行,未授權時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

後台最常用的那張表:點欄頭在遞增、遞減、不排序之間循環,每一列以 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型別預設值說明
columnsSortableColumn<Row>[]—欄位定義(見下方子型別)
rowsRow[]—資料列,每列需有穩定唯一的 id(FLIP 動畫與勾選都依它配對)
defaultSortSortState—非受控模式的初始排序狀態
sortSortState | null—受控排序狀態(null 表示未排序);不提供時為非受控模式
onSortChange(sort: SortState | null) => void—排序變更時回呼(點欄頭循環 asc → desc → 無排序)
selectablebooleanfalse是否顯示勾選欄
selectedstring[]—受控已勾選 id 清單;不提供時為非受控模式
onSelectedChange(ids: string[]) => void—勾選變更時回呼
bulkActionsBulkAction[]—批次操作定義;勾選數大於 0 時操作列從頂部滑入
onBulkAction(actionId: string, ids: string[]) => void—點擊批次操作按鈕時回呼
expandable(row: Row) => ReactNode—列明細渲染函式;有提供則列首出現展開箭頭
maxHeightnumber400內容區最大高度(px),超過時內部捲動、表頭 sticky 置頂
emptyTextstring"沒有符合的資料"無資料時顯示的文字
zebrabooleanfalse偶數列淡色底的斑馬紋
compactbooleanfalse緊湊模式:較小的字級與間距
classNamestring—透傳到最外層容器

SortableColumn

欄位型別預設值說明
keystring—對應資料列上的欄位鍵
labelReactNode—欄頭顯示內容
sortablebooleanfalse是否可點擊欄頭排序
align"left" | "center" | "right""left"儲存格內容對齊
widthstring—欄寬(任何合法 CSS 寬度值,例如 "120px")
render(row: Row) => ReactNode—自訂儲存格渲染;未提供時直接顯示欄位值(空值顯示 —)
sortFn(a: Row, b: Row) => number—自訂比較函式,一律以遞增語意撰寫,方向由元件反轉;未提供時自動判別

SortState

欄位型別預設值說明
keystring—排序中的欄位鍵
dir"asc" | "desc"—排序方向

BulkAction

欄位型別預設值說明
idstring—動作識別碼,點擊時傳給 onBulkAction
labelstring—按鈕文字
iconReactNode—按鈕左側圖示

另外具名匯出 SortableRowBase、SortableColumn、SortState、BulkAction 型別,以及預設比較函式 compareCellValues(a, b),可在自訂 sortFn 裡沿用。

細節

  • 排序循環:點同一欄的欄頭依序切換遞增 → 遞減 → 不排序;換點另一欄則從遞增開始。不排序時回到 rows 的原始順序
  • 預設比較:兩邊都是數字或純數字字串時比數值(避免 "12" 排在 "3" 前面),都是 YYYY-MM-DD/YYYY/M/D 開頭的日期字串時比時間,其餘以 localeCompare 比較;空值無論方向都排在最後,同值列以原始順序保持穩定
  • FLIP 重排:每一列以 motion 的 layout 搭配穩定的 id key 做重排滑動。刻意不用 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 滑動,批次操作列與明細列直接出現、欄頭箭頭直接翻轉