電商購物體驗
交易清單
金融 App 式交易清單 React 元件:依日期自動分組為今天、昨天與完整日期,金額逐位滾動並以正負上色,新交易插入時高度展開、鄰列順移,右滑或按圖釘即可置頂,附處理中、失敗、已退款狀態標籤。
銀行與記帳 App 首頁那種交易清單。把交易陣列丟進來,元件依本地日期自動分組(「今天」「昨天」,其餘如「10月23日 週五」,跨年加年份),組內依時間新到舊排列;每列左側是圓形圖示或依標題首字產生的字母頭像,右側是逐位滾動的金額——收入加「+」並上色、支出保持中性——以及處理中、失敗、已退款的狀態標籤。新交易插入時該列高度展開、下方的列平順下移;向右滑動任一列或按右側圖釘可把它釘到最上方的「已釘選」組,以 layout 動畫飛過去。釘選狀態支援受控/非受控雙模式。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add transaction-listPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
380
編輯中的呼叫預設屬性
<TransactionList/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/transaction-list.json或在 components.json 設定 registries 後,改用 @webberui/transaction-list 安裝。
使用
import { TransactionList } from "@/components/ui/transaction-list";
<TransactionList
items={[
{ id: "1", date: "2026-10-23T09:00:00", title: "晨光設計有限公司", subtitle: "薪資入帳", amount: 52000 },
{ id: "2", date: "2026-10-23T14:20:00", title: "巷口便利商店", subtitle: "日用品", amount: -85, status: "pending" },
{ id: "3", date: "2026-10-22T19:30:00", title: "雲朵影音", subtitle: "月訂閱扣款", amount: -390, status: "failed" },
]}
currency="TWD"
defaultPinnedIds={["1"]}
onSelect={(id) => console.log(`點選 ${id}`)}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
items | TransactionItem[] | — | 交易資料;順序不拘,元件會依時間新→舊排序 |
grouped | boolean | true | 是否依日期分組(今天/昨天/完整日期);關閉時為單一清單,副標改顯示日期 |
currency | string | "TWD" | 預設幣別(ISO 4217),item 未指定 currency 時使用 |
pinnedIds | string[] | — | 受控的已釘選 id 清單;未提供時由元件內部管理(非受控) |
defaultPinnedIds | string[] | [] | 非受控模式的初始已釘選 id 清單 |
onPinnedChange | (ids: string[]) => void | — | 釘選狀態變動時回呼,參數為新的已釘選 id 清單 |
onSelect | (id: string) => void | — | 點選某列時回呼;提供後每列成為可聚焦的按鈕 |
stiffness | number | 380 | 列位移、展開與金額滾動的 spring 剛度 |
emptyState | ReactNode | 收據圖示 +「目前沒有交易紀錄」 | 沒有任何交易時顯示的內容 |
now | Date | — | 決定「今天/昨天」的基準時間;未提供時掛載後才以 new Date() 計算,首次渲染顯示完整日期 |
className | string | — | 透傳到最外層容器 |
TransactionItem
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別碼;也用來確定性地決定字母頭像的底色 |
date | string | Date | — | 交易時間(ISO 字串或 Date),依本地日期分組 |
title | string | — | 標題(商家或對象名稱) |
subtitle | string | — | 副標(類別、備註等),會與時間 HH:mm 並列 |
amount | number | — | 金額;正數為收入、負數為支出 |
currency | string | 元件的 currency | ISO 4217 幣別代碼 |
status | "completed" | "pending" | "failed" | "refunded" | "completed" | 交易狀態;completed 不顯示標籤 |
icon | ReactNode | — | 左側 40px 圓形圖示;未提供時依標題首字產生字母頭像 |
細節
- 金額以
Intl.NumberFormat("zh-TW")格式化,台幣不顯示小數,其他幣別最多兩位;zh-TW 會把台幣顯示成「$」,元件改成「NT$」避免與美元混淆。 - 逐位滾動:每一位數字是 0 到 9 的直列,以 spring(
stiffness與 damping 20)滾到目標值;NT$、逗號等非數字字元保持靜態。金額或幣別改變時從舊值滾到新值。 - 新增與刪除:新列從高度 0 展開,下方的列與日期組跟著順移;被移除的列反向收合。釘選與取消釘選時,列以共用 layoutId 從原位置飛到新組,不做高度展開。
- 右滑釘選:列可水平拖曳,左側露出「釘選」提示,放開時位移超過門檻就切換;拖曳過後的放開不會觸發
onSelect。 - 顏色變數:收入色
--wb-txl-positive(深色模式--wb-txl-positive-dark);狀態標籤--wb-txl-pending、--wb-txl-failed、--wb-txl-refunded,各自有-bg與-dark版本。 - SSR:沒有傳
now時,首次渲染組標題一律顯示含年份的完整日期,掛載後才換成「今天/昨天」,避免伺服器與瀏覽器時間不同造成 hydration 不一致。
可及性
- 每個日期組是
section,以aria-labelledby指向組標題;列是ul/li - 傳入
onSelect時每列是按鈕,aria-label完整朗讀標題、收入或支出金額、日期時間與狀態,例如「巷口便利商店,支出 NT$85,今天 14:20,處理中」 - 滾動中的數字對輔助科技隱藏,旁邊附上螢幕閱讀器專用的完整金額
- 釘選按鈕以
aria-pressed表示狀態,可用 Tab 聚焦並以 Enter/Space 切換;右滑只是額外的快捷操作 - 新增交易時以
aria-live="polite"宣告 - 使用者系統開啟「減少動態效果」時:沒有進場 stagger、金額直接顯示不滾動、新增的列直接出現(高度不動畫)、釘選時直接換位