Chuyển tới nội dung chính

Mobile Table Card Pattern

Vấn đề​

Bảng dữ liệu (Table) trên desktop hoạt động tốt với nhiều cột. Trên mobile, bảng bị tràn ngang và khó tương tác. Cần một cách hiển thị linh hoạt:

  • Desktop: bảng truyền thống (thead/tbody)
  • Mobile: danh sách card, mỗi row là 1 card

Giải pháp — Kiến trúc Slotted & Escape Hatch​

Hệ thống Table mới cung cấp cơ chế tự động đáp ứng (responsive) theo hai cấp độ:

Table (Public API)
├── Desktop Mode (md+) → <table> HTML chuẩn
└── Mobile Mode (<md) → TableMobileCard (Internal Logic)
├── Level 1: Auto-columns (Mặc định)
└── Level 2: Custom Render (Dùng renderMobileCard)

Luồng xử lý Logic​

  1. Mặc định: Nếu không truyền gì thêm, Table tự động chuyển sang dạng danh sách các cột (Label: Value) khi ở màn hình nhỏ.
  2. Tùy biến: Nếu muốn card có UI đặc thù (như Đơn hàng), Developer cung cấp hàm renderMobileCard.

Cách dùng chuẩn​

1. Hiển thị mặc định (Auto Columns)​

Áp dụng cho các bảng báo cáo hoặc danh sách đơn giản. Không cần cấu hình thêm.

import { Table } from "@vppos/core/ui/react";

<Table columns={columns} data={data} />;
// Desktop: <table>
// Mobile: Card list (tự động loop qua columns)

2. Hiển thị tùy biến (Custom Render)​

Áp dụng cho các trang cần UI mobile đặc biệt (Đơn hàng, Giao dịch). Sử dụng renderMobileCard.

import { Table } from "@vppos/core/ui/react";

<Table
columns={columns}
data={orders}
renderMobileCard={(order) => (
<div className="rounded-xl border border-gray-100 bg-white p-4 shadow-sm">
<div className="flex items-start justify-between gap-3">
<h3 className="text-lg font-medium">{order.code}</h3>
<Badge>{order.status}</Badge>
</div>
<p className="mt-2 text-sm text-gray-500">{order.customerName}</p>
<div className="mt-4 flex items-center justify-between">
<span className="text-xl font-medium">{formatCurrency(order.total)}</span>
<Button onClick={() => handleApprove(order)}>Phê duyệt</Button>
</div>
</div>
)}
/>;

Tại sao kiến trúc này tối ưu?​

  1. Zero-Config: Đa số các bảng sẽ hoạt động tốt trên mobile mà không cần code thêm một dòng nào.
  2. Toàn quyền kiểm soát (Full Control): Với renderMobileCard, bạn có thể vẽ bất cứ thứ gì (Avatar, ProgressBar, Map...) mà không bị giới hạn bởi các "variant" cứng nhắc trong Core.
  3. Dễ bảo trì: Logic hiển thị của module nào nằm ở module đó (Local Logic). Core chỉ đóng vai trò là "khung xương" điều phối.

Loading & Skeletion​

Hệ thống tự động sử dụng Skeleton phù hợp:

  • Nếu dùng mặc định: Skeleton dạng danh sách dòng.
  • Nếu dùng renderMobileCard: Developer có thể tự định nghĩa skeleton hoặc hệ thống sẽ dùng skeleton card tiêu chuẩn.

Files liên quan​

FileVai trò
core/src/ui/react/Table.tsxPublic API — điều phối Desktop/Mobile và nhận renderMobileCard.
core/src/ui/react/TableMobileCard.tsxInternal — thực hiện việc render item hoặc mặc định.
apps/sale-manage/.../order-mobile-list.tsxVí dụ áp dụng thực tế (Best Practice).