WebberUI

Archive Index

工作室檔案館式的全頁索引表格,多欄可排序並以 FLIP 重排,點列展開成滿版詳情,其餘列彈性讓位下沉。

載入預覽⋯

Playground

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

<ArchiveIndex />

安裝

npx shadcn@latest add https://webberui.com/r/archive-index.json

或在 components.json 設定 registries 後,改用 @webberui/archive-index 安裝。

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

npm install motion lucide-react clsx tailwind-merge

調校點用到的 --wb-duration-fast--wb-ease-out 有內建 fallback,未設定也可運作;如需自訂可在全域 CSS 加入:

:root {
  --wb-duration-fast: 200ms;
  --wb-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

使用

import {
  ArchiveIndex,
  type ArchiveColumn,
  type ArchiveEntry,
} from "@/components/ui/archive-index";

const columns: ArchiveColumn[] = [
  { key: "year", header: "年份", width: "4rem", align: "right" },
  { key: "project", header: "專案", width: "minmax(0,1.6fr)" },
  { key: "category", header: "類別" },
];

const entries: ArchiveEntry[] = [
  {
    id: "aperture",
    label: "Aperture 識別系統",
    fields: { year: "2024", project: "Aperture 識別系統", category: "品牌" },
    detail: <p>為光學儀器製造商打造的完整視覺識別。</p>,
  },
  // ...
];

<ArchiveIndex columns={columns} entries={entries} />

排序與展開皆支援受控/非受控雙模式。傳入 sort / expandedId 即進入受控模式,並透過 onSortChange / onExpandedChange 回寫狀態:

const [sort, setSort] = React.useState<ArchiveSort | null>({
  key: "year",
  direction: "desc",
});

<ArchiveIndex
  columns={columns}
  entries={entries}
  sort={sort}
  onSortChange={setSort}
/>

Props

ArchiveIndex

Prop型別預設值說明
columnsArchiveColumn[]欄位定義(年份/專案/類別等多欄)
entriesArchiveEntry[]資料列
expandedIdstring | null受控的展開列 id;傳入即進入受控模式
defaultExpandedIdstring | nullnull非受控模式的初始展開列 id
onExpandedChange(id: string | null) => void展開列變更時觸發(收合時為 null
sortArchiveSort | null受控的排序狀態;傳入即進入受控模式
defaultSortArchiveSort | nullnull非受控模式的初始排序狀態
onSortChange(sort: ArchiveSort | null) => void排序變更時觸發(清除排序時為 null
renderDetail(entry: ArchiveEntry) => React.ReactNode以 render prop 產生詳情內容(優先於 entry.detail
stickyHeaderbooleantrue表頭是否黏著於捲動容器頂端
classNamestring附加到最外層容器的 class

ArchiveColumn

Prop型別預設值說明
keystring欄位唯一 key,對應 ArchiveEntry.fields 的鍵
headerReact.ReactNode表頭顯示內容
sortablebooleantrue此欄是否可排序
align"left" | "right""left"儲存格對齊方向
widthstring"minmax(0,1fr)"CSS grid 軌道寬度,如 "6rem""minmax(0,2fr)"
classNamestring附加在此欄儲存格上的 class

ArchiveEntry

Prop型別預設值說明
idstring資料列唯一識別,用於受控展開、FLIP 配對與 React key
fieldsRecord<string, React.ReactNode>各欄位顯示值,key 對應 ArchiveColumn.key
sortValuesRecord<string, string | number>各欄位排序值;未提供時退回以 fields 的字串/數字值排序
detailReact.ReactNode展開後的滿版詳情內容;未提供則此列不可展開
labelstring無障礙用的列名稱,套用於展開鈕 aria-label(未提供時退回 id

細節

  • 排序循環:點同一欄表頭依序切換 asc → desc → 清除排序;清除後回到 entries 原始順序。排序採穩定排序,不會更動傳入的陣列。
  • FLIP 重排:排序改變時,各列以 layout="position" 走 FLIP 動畫平滑換位,帶輕微回彈的 spring。
  • 展開讓位:點某列時該列詳情以高度 spring 撐開成滿版區塊,下方列順著流式版面被彈性推移下沉;同時其餘列淡淡降暗以聚焦目前列。
  • 排序值:數字對數字採數值比較,其餘以 numeric 感知的 localeCompare,故 "2019""2021" 或年份數字都能正確排序。若顯示值非純文字(如帶樣式的節點),請用 sortValues 提供可比較的值。

可及性

  • 表頭排序控制為原生 <button>,可鍵盤聚焦與操作,並帶 aria-label 標示排序欄位。
  • 可展開列為原生 <button>,帶 aria-expandedaria-controls 指向詳情區塊;詳情區塊為 role="region" 並帶 aria-label
  • 使用者系統開啟「減少動態效果」時,停用 FLIP、高度與降暗動畫,直接切換展開/收合狀態,DOM 結構維持一致。

On this page