Data Grid
DataGrid là lưới dữ liệu tương tác: sắp xếp, chọn hàng, kéo đổi độ rộng cột và ảo hoá cho hàng chục nghìn hàng.
Import
Cài từ npm, chép mã nguồn vào dự án bằng @yugen-ui/cli hoặc shadcn CLI, hoặc import thẳng khi làm việc trong repo này.
// npm install yugen-uiimport { DataGrid, DataGridColumnPicker, createColumn, sortItems, getVisibleColumns, getOrderedColumns, useDataGridSort, useDataGridSelection, useDataGridColumnVisibility, type DataGridColumn, type DataGridSortState,} from "yugen-ui";Biến thể
Mỗi ô là một mẫu độc lập: hover để Copy, hoặc bấm Code để xem mã nguồn.
Sắp xếp (điều khiển từ ngoài)
Cột có compare mới sắp xếp được; sắp xếp ổn định, aria-sort trên tiêu đề, icon kèm chữ cho trình đọc màn hình.
Chọn nhiều + thanh công cụ
selectedItems / onSelectionChange là Set<string> các id. Checkbox tiêu đề có trạng thái indeterminate.
Chọn và sắp xếp cột
DataGridColumnPicker và DataGrid dùng chung hiddenColumns và columnOrder, cả hai được lưu vào localStorage. Menu không đóng khi tích từng cột; cột hideable: false (Khách hàng) và cột hiện cuối cùng không bỏ tích được. Kéo một mục trong menu (hoặc Alt + ↑ ↓) để đổi thứ tự cột, lưới đổi theo ngay. Ẩn cột đang sắp xếp không đổi thứ tự hàng; menu vẫn hiện mũi tên ở cột đó.
Đổi độ rộng cột, ghim cột đầu
Tay nắm nhận focus khi Tab từ ô tiêu đề: ← / → ±10px, Shift ±50px, nhấp đúp để vừa nội dung. Cột sticky giữ nguyên khi cuộn ngang.
Ảo hoá 10 000 hàng
virtualized + height: chỉ render các hàng trong khung nhìn (+ overscan). Ctrl + End cuộn tới và focus hàng cuối.
Đang tải · rỗng · lỗi
loading hiện khung xương sau 300ms (giữ ≥ 500ms); rỗng hiện Empty; lỗi do AsyncContent lo, kèm nút Thử lại.
RTL
Cột xếp phải → trái, ArrowLeft đi về cuối dòng, kéo tay nắm sang trái để nới rộng cột. Không cần prop riêng — đọc từ DirectionProvider / YugenProvider.
Cách dùng
- Đặt aria-label (hoặc aria-labelledby) cho lưới và getRowId trả về id ổn định — không dùng chỉ số mảng.
- Bật virtualized cho danh sách > vài trăm hàng, kèm height cố định và rowHeight bằng nhau.
- Dùng align="end" cho cột số / tiền tệ và width cố định cho cột hẹp; để cột chính co giãn.
- Không đặt nhiều nút bấm trong mỗi ô — mỗi nút là một tab stop; gom vào một Menu thao tác.
- Không dùng DataGrid cho bố cục hay danh sách thẻ — dùng Table (tĩnh) hoặc List.
- Không tự hiện spinner riêng khi tải lại — dùng prop loading để không nháy loader dưới 300ms.
Props
| Prop · Kiểu | Mặc định | Mô tả |
|---|---|---|
| itemsreadonly T[] | Mặc định: — | Dữ liệu theo thứ tự gốc; T được suy ra cho columns và getRowId. |
| columnsDataGridColumn<T>[] | Mặc định: — | Tạo bằng createColumn<T>({ columnId, label?, renderHeaderCell?, renderCell, compare?, width?, minWidth?, maxWidth?, resizable?, align?, sticky?, hideable? }). label là tiêu đề dạng chữ và tên trong DataGridColumnPicker; renderHeaderCell khi tiêu đề cần hơn chữ. |
| columnOrderstring[] | Mặc định: — | Thứ tự hiển thị của cột (id cột). Cột không có trong mảng đứng sau, theo thứ tự columns; id lạ bị bỏ qua. |
| hiddenColumnsReadonlySet<string> | Mặc định: — | Id các cột không hiển thị. Cột hideable: false luôn hiện; ẩn hết thì giữ cột đầu. Cột ẩn giữ độ rộng đã kéo và vẫn sắp xếp nếu đang là cột sắp xếp; focus chuyển sang cột kế bên khi cột đang focus bị ẩn. |
| getRowId(item: T) => string | Mặc định: — | Id ổn định của hàng (key, lựa chọn). |
| size"small" | "medium" | "large" | Mặc định: "medium" | Chiều cao hàng 34 / 44 / 56px. |
| sortableboolean | Mặc định: false | Bấm / Enter / Space trên tiêu đề cột có compare để sắp xếp (cùng cột → đảo chiều). |
| sortState / defaultSortState{ sortColumn: string | undefined; sortDirection: "ascending" | "descending" } | Mặc định: — | Trạng thái sắp xếp (điều khiển / ban đầu). |
| onSortChange(event, state) => void | Mặc định: — | Gọi khi đổi cột hoặc chiều sắp xếp. |
| selectionMode"single" | "multiselect" | Mặc định: — | Bật cột chọn. Không truyền = không chọn được. |
| selectedItems / defaultSelectedItemsReadonlySet<string> / Iterable<string> | Mặc định: — | Tập id đang chọn (điều khiển / ban đầu). |
| onSelectionChange(event, { selectedItems: Set<string> }) => void | Mặc định: — | Gọi khi lựa chọn đổi (click, Space, Shift + mũi tên, chọn tất cả, Ctrl + A). |
| selectionAppearance"brand" | "neutral" | "none" | Mặc định: "brand" | Nền hàng đã chọn. |
| subtleSelectionboolean | Mặc định: false | Checkbox chỉ hiện khi hover / focus hoặc khi hàng đã chọn. |
| resizableColumnsboolean | Mặc định: false | Tay nắm đổi độ rộng ở mép cuối tiêu đề (chuột, cảm ứng, bàn phím; nhấp đúp = vừa nội dung). |
| columnSizingOptionsRecord<columnId, { defaultWidth?, minWidth?, maxWidth? }> | Mặc định: — | Ghi đè kích thước theo cột (giống Fluent). |
| onColumnResize(event, { columnId, width }) => void | Mặc định: — | Gọi khi độ rộng cột đổi. Không lưu gì mặc định. |
| virtualizedboolean | Mặc định: false | Chỉ render hàng trong khung nhìn; aria-rowcount / aria-rowindex vẫn là tổng số thật. |
| rowHeightnumber | Mặc định: — | Chiều cao hàng (px), mặc định theo size. |
| heightnumber | string | Mặc định: — | Chiều cao khung cuộn. virtualized mà không truyền → 400px (h-100). |
| overscannumber | Mặc định: 6 | Số hàng render thêm trên / dưới khung nhìn. |
| focusMode"cell" | "row" | Mặc định: "cell" | Mũi tên đi từng ô hoặc từng hàng. |
| loadingboolean | Mặc định: false | Khung xương sau 300ms, giữ ≥ 500ms (useSpinDelay); aria-busy trên lưới. |
| emptyReactNode | Mặc định: — | Nội dung khi không có hàng; mặc định Empty "Không có dữ liệu". |
| DataGridColumnPicker | Mặc định: — | Cùng mảng cột truyền cho DataGrid; với Table là mảng { columnId, label }. Tên trong menu là label. |
| DataGridColumnPicker | Mặc định: — | Id các cột đang ẩn — cùng giá trị truyền cho DataGrid. |
| DataGridColumnPicker | Mặc định: — | Gọi khi người dùng tích / bỏ tích hoặc chọn Hiện tất cả cột. Id của cột tạm không có trong columns được giữ nguyên để lưu lại. |
| DataGridColumnPicker | Mặc định: — | Thứ tự cột — cùng giá trị truyền cho DataGrid. |
| DataGridColumnPicker | Mặc định: — | Có thì mục trong menu kéo được (hoặc Alt + ↑ ↓) để đổi thứ tự cột. Gọi cả trong lúc kéo để lưới đổi theo. |
| DataGridColumnPicker | Mặc định: — | Cột đang sắp xếp có mũi tên trong menu, kể cả khi đang ẩn. |
| DataGridColumnPicker | Mặc định: "end" | Căn menu theo nút. Trong Toolbar nút tự lấy size của Toolbar và appearance subtle. |
| useDataGridSort / useDataGridSelection / useDataGridColumnVisibility / sortItems / getVisibleColumns / getOrderedColumnshook / hook / hook / (items, columns, sortState) => T[] / (columns, hiddenColumns) => columns / (columns, columnOrder) => columns | Mặc định: — | Logic dùng riêng được — ví dụ để sắp xếp phía server, điều khiển lựa chọn từ toolbar hoặc tự làm nút chọn cột. |
Messages
Chữ có sẵn của component: nhãn, aria-label, thông báo cho trình đọc màn hình.
Mặc định là tiếng Anh. Đổi qua prop messages của YugenProvider, theo namespace bên dưới; messagesVi chứa bản tiếng Việt. Xem Ngôn ngữ giao diện.
messages.dataGrid
| Khoá · Kiểu | Mặc định (tiếng Anh) | messagesVi |
|---|---|---|
| selectRowstring Tên ô chọn của từng hàng (và tiêu đề cột chọn khi chọn một hàng). | Mặc định (tiếng Anh)"Select row" | messagesVi"Chọn hàng" |
| selectAllstring Tên ô chọn tất cả ở hàng tiêu đề. | Mặc định (tiếng Anh)"Select all rows" | messagesVi"Chọn tất cả hàng" |
| sortAscendingstring Chữ ẩn sau tên cột đang sắp xếp. | Mặc định (tiếng Anh)"(ascending)" | messagesVi"(tăng dần)" |
| sortDescendingstring | Mặc định (tiếng Anh)"(descending)" | messagesVi"(giảm dần)" |
| resizeColumnstring Tên tay nắm đổi độ rộng cột (ghép trước tên cột). | Mặc định (tiếng Anh)"Resize column" | messagesVi"Đổi độ rộng cột" |
| emptyTitlestring | Mặc định (tiếng Anh)"No data" | messagesVi"Không có dữ liệu" |
| emptyDescriptionstring | Mặc định (tiếng Anh)"There are no rows to display." | messagesVi"Chưa có hàng nào để hiển thị." |
| columnsstring Nhãn nút của DataGridColumnPicker. | Mặc định (tiếng Anh)"Columns" | messagesVi"Cột" |
| showAllColumnsstring Mục cuối menu của DataGridColumnPicker. | Mặc định (tiếng Anh)"Show all columns" | messagesVi"Hiện tất cả cột" |
| hiddenColumns(count: number) => string Mô tả nút DataGridColumnPicker khi có cột bị ẩn. | Mặc định (tiếng Anh)(count) => (count === 1 ? "1 column hidden" : `${count} columns hidden`) | messagesVi(count) => `${count} cột đang ẩn` |