Yugen UI

DataGrid

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.

Tương đương DataGrid của Fluent 2. Khai báo cột bằng createColumn<T>() (label, renderCell, compare, width…), truyền items + getRowId. Lưới có role="grid" với aria-rowcount / aria-rowindex đúng cả khi ảo hoá, một tab stop duy nhất (roving tabindex): mũi tên đi từng ô (đảo trái / phải trong RTL), Home / End, Ctrl + Home / End, PageUp / PageDown; Space chọn hàng, Shift + mũi tên hoặc Shift + click chọn theo khoảng. Người dùng tự chọn cột hiển thị và thứ tự cột bằng DataGridColumnPicker (hiddenColumns, columnOrder). Tiêu đề sticky; loading hiện khung xương theo Wait UX (useSpinDelay), không có hàng thì hiện Empty. Logic dùng lại được qua useDataGridSort, useDataGridSelection, useDataGridColumnVisibility và các hàm thuần sortItems, getVisibleColumns, getOrderedColumns.
Khách hàng
Mã đơn
Trạng thái
Ngày(giảm dần)
Số tiền
Bùi Thanh Nam
DH-10012
Thất bại
10 thg 12, 2026
345,00 US$
Nguyễn Văn An
DH-10011
Đã thanh toán
27 thg 11, 2026
2.620,00 US$
Phạm Minh Dũng
DH-10010
Đã thanh toán
16 thg 10, 2026
4.895,00 US$
Đặng Mai Linh
DH-10009
Đã thanh toán
5 thg 9, 2026
865,00 US$
Ngô Đức Phúc
DH-10008
Đang xử lý
22 thg 8, 2026
3.140,00 US$
Phạm Minh Dũng
DH-10020
Đã thanh toán
14 thg 8, 2026
1.060,00 US$
Lê Hoàng Cường
DH-10007
Thất bại
11 thg 7, 2026
5.415,00 US$
Đặng Mai Linh
DH-10019
Đã thanh toán
3 thg 7, 2026
3.335,00 US$
Vũ Quốc Khánh
DH-10006
Đã thanh toán
28 thg 6, 2026
1.385,00 US$
Ngô Đức Phúc
DH-10018
Đang xử lý
20 thg 6, 2026
5.610,00 US$
Đỗ Ngọc Oanh
DH-10005
Đã thanh toán
17 thg 5, 2026
3.660,00 US$
Lê Hoàng Cường
DH-10017
Thất bại
9 thg 5, 2026
1.580,00 US$
Vũ Quốc Khánh
DH-10016
Đã thanh toán
26 thg 4, 2026
3.855,00 US$
Trần Thị Bình
DH-10004
Đã thanh toán
6 thg 4, 2026
5.935,00 US$
Hoàng Thu Hà
DH-10003
Đang xử lý
23 thg 3, 2026
1.905,00 US$
Đỗ Ngọc Oanh
DH-10015
Đã thanh toán
15 thg 3, 2026
6.130,00 US$
Bùi Thanh Nam
DH-10002
Thất bại
12 thg 2, 2026
4.180,00 US$
Trần Thị Bình
DH-10014
Đã thanh toán
4 thg 2, 2026
2.100,00 US$
Hoàng Thu Hà
DH-10013
Đang xử lý
21 thg 1, 2026
4.375,00 US$
Nguyễn Văn An
DH-10001
Đã thanh toán
1 thg 1, 2026
150,00 US$

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.

TSX
// 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.

Đang sắp xếp: amount · descending
Khách hàng
Mã đơn
Trạng thái
Ngày
Số tiền(giảm dần)
Trần Thị Bình
DH-10004
Đã thanh toán
6 thg 4, 2026
5.935,00 US$
Bùi Thanh Nam
DH-10002
Thất bại
12 thg 2, 2026
4.180,00 US$
Đỗ Ngọc Oanh
DH-10005
Đã thanh toán
17 thg 5, 2026
3.660,00 US$
Hoàng Thu Hà
DH-10003
Đang xử lý
23 thg 3, 2026
1.905,00 US$
Vũ Quốc Khánh
DH-10006
Đã thanh toán
28 thg 6, 2026
1.385,00 US$
Nguyễn Văn An
DH-10001
Đã thanh toán
1 thg 1, 2026
150,00 US$

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.

Khách hàng
Mã đơn
Trạng thái
Nguyễn Văn An
DH-10001
Đã thanh toán
Bùi Thanh Nam
DH-10002
Thất bại
Hoàng Thu Hà
DH-10003
Đang xử lý
Trần Thị Bình
DH-10004
Đã thanh toán
Đỗ Ngọc Oanh
DH-10005
Đã thanh toán
Vũ Quốc Khánh
DH-10006
Đã thanh toán
Lê Hoàng Cường
DH-10007
Thất bại
Ngô Đức Phúc
DH-10008
Đang xử lý

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.

Khách hàng
Trạng thái
Ngày(giảm dần)
Số tiền
Vũ Quốc Khánh
Đã thanh toán
28 thg 6, 2026
1.385,00 US$
Đỗ Ngọc Oanh
Đã thanh toán
17 thg 5, 2026
3.660,00 US$
Trần Thị Bình
Đã thanh toán
6 thg 4, 2026
5.935,00 US$
Hoàng Thu Hà
Đang xử lý
23 thg 3, 2026
1.905,00 US$
Bùi Thanh Nam
Thất bại
12 thg 2, 2026
4.180,00 US$
Nguyễn Văn An
Đã thanh toán
1 thg 1, 2026
150,00 US$

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 đó.

Kéo mép phải tiêu đề, hoặc Tab tới tay nắm rồi dùng phím mũi tên.
Chọn hàng
Khách hàng
Mã đơn
Trạng thái
Ngày
Số tiền
Nguyễn Văn An
DH-10001
Đã thanh toán
1 thg 1, 2026
150,00 US$
Bùi Thanh Nam
DH-10002
Thất bại
12 thg 2, 2026
4.180,00 US$
Hoàng Thu Hà
DH-10003
Đang xử lý
23 thg 3, 2026
1.905,00 US$
Trần Thị Bình
DH-10004
Đã thanh toán
6 thg 4, 2026
5.935,00 US$
Đỗ Ngọc Oanh
DH-10005
Đã thanh toán
17 thg 5, 2026
3.660,00 US$

Đổ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.

Khách hàng
Mã đơn
Trạng thái
Ngày
Số tiền
Nguyễn Văn An
DH-10001
Đã thanh toán
1 thg 1, 2026
150,00 US$
Bùi Thanh Nam
DH-10002
Thất bại
12 thg 2, 2026
4.180,00 US$
Hoàng Thu Hà
DH-10003
Đang xử lý
23 thg 3, 2026
1.905,00 US$
Trần Thị Bình
DH-10004
Đã thanh toán
6 thg 4, 2026
5.935,00 US$
Đỗ Ngọc Oanh
DH-10005
Đã thanh toán
17 thg 5, 2026
3.660,00 US$
Vũ Quốc Khánh
DH-10006
Đã thanh toán
28 thg 6, 2026
1.385,00 US$
Lê Hoàng Cường
DH-10007
Thất bại
11 thg 7, 2026
5.415,00 US$
Ngô Đức Phúc
DH-10008
Đang xử lý
22 thg 8, 2026
3.140,00 US$
Đặng Mai Linh
DH-10009
Đã thanh toán
5 thg 9, 2026
865,00 US$
Phạm Minh Dũng
DH-10010
Đã thanh toán
16 thg 10, 2026
4.895,00 US$
Nguyễn Văn An
DH-10011
Đã thanh toán
27 thg 11, 2026
2.620,00 US$
Bùi Thanh Nam
DH-10012
Thất bại
10 thg 12, 2026
345,00 US$
Hoàng Thu Hà
DH-10013
Đang xử lý
21 thg 1, 2026
4.375,00 US$
Trần Thị Bình
DH-10014
Đã thanh toán
4 thg 2, 2026
2.100,00 US$
Đỗ Ngọc Oanh
DH-10015
Đã thanh toán
15 thg 3, 2026
6.130,00 US$
Vũ Quốc Khánh
DH-10016
Đã thanh toán
26 thg 4, 2026
3.855,00 US$
Lê Hoàng Cường
DH-10017
Thất bại
9 thg 5, 2026
1.580,00 US$

Ả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.

Khách hàng
Mã đơn
Trạng thái
Nguyễn Văn An
DH-10001
Đã thanh toán
Bùi Thanh Nam
DH-10002
Thất bại
Hoàng Thu Hà
DH-10003
Đang xử lý
Trần Thị Bình
DH-10004
Đã thanh toán

Đ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.

المدينة
الدولة
السكان (ألف)
القاهرة
مصر
22,183
الرياض
السعودية
7,682
دبي
الإمارات
3,604

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

Nên
  • Đặ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 nê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ểuMặc địnhMô 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) => stringMặ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.
sortablebooleanMặc định: falseBấ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) => voidMặ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> }) => voidMặ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.
subtleSelectionbooleanMặc định: falseCheckbox chỉ hiện khi hover / focus hoặc khi hàng đã chọn.
resizableColumnsbooleanMặc định: falseTay 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 }) => voidMặc định: —Gọi khi độ rộng cột đổi. Không lưu gì mặc định.
virtualizedbooleanMặc định: falseChỉ render hàng trong khung nhìn; aria-rowcount / aria-rowindex vẫn là tổng số thật.
rowHeightnumberMặc định: —Chiều cao hàng (px), mặc định theo size.
heightnumber | stringMặc định: —Chiều cao khung cuộn. virtualized mà không truyền → 400px (h-100).
overscannumberMặc định: 6Số 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.
loadingbooleanMặc định: falseKhung xương sau 300ms, giữ ≥ 500ms (useSpinDelay); aria-busy trên lưới.
emptyReactNodeMặc định: —Nội dung khi không có hàng; mặc định Empty "Không có dữ liệu".
DataGridColumnPicker.columnsDataGridColumn<T>[] | { columnId, label?, hideable? }[]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.hiddenColumnsReadonlySet<string>Mặc định: —Id các cột đang ẩn — cùng giá trị truyền cho DataGrid.
DataGridColumnPicker.onHiddenColumnsChange(event, { hiddenColumns: Set<string> }) => voidMặ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.columnOrderstring[]Mặc định: —Thứ tự cột — cùng giá trị truyền cho DataGrid.
DataGridColumnPicker.onColumnOrderChange(event, { columnOrder: string[] }) => voidMặ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.sortStateDataGridSortStateMặc định: —Cột đang sắp xếp có mũi tên trong menu, kể cả khi đang ẩn.
DataGridColumnPicker.align"start" | "center" | "end"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) => columnsMặ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ểuMặ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)"
sortDescendingstringMặ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"
emptyTitlestringMặc định (tiếng Anh)"No data"messagesVi"Không có dữ liệu"
emptyDescriptionstringMặ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`