@openway/ui 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +203 -181
- package/dist/chunk-CJQNI3QX.js +3 -0
- package/dist/chunk-CJQNI3QX.js.map +1 -0
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2642 -1103
- package/dist/index.d.ts +2642 -1103
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/query.cjs +3 -0
- package/dist/query.cjs.map +1 -0
- package/dist/query.d.cts +309 -0
- package/dist/query.d.ts +309 -0
- package/dist/query.js +3 -0
- package/dist/query.js.map +1 -0
- package/dist/useInfiniteScroll-DAlgjUN4.d.cts +325 -0
- package/dist/useInfiniteScroll-DAlgjUN4.d.ts +325 -0
- package/docs/AGENTS.md +144 -0
- package/docs/README.md +150 -0
- package/docs/SKILL.md +54 -0
- package/docs/components/alert.md +246 -0
- package/docs/components/badge.md +238 -0
- package/docs/components/button.md +262 -0
- package/docs/components/carousel.md +354 -0
- package/docs/components/checkbox.md +252 -0
- package/docs/components/collapse.md +318 -0
- package/docs/components/confirm.md +322 -0
- package/docs/components/datepicker.md +259 -0
- package/docs/components/daterangepicker.md +260 -0
- package/docs/components/datetimepicker.md +226 -0
- package/docs/components/datetimerangepicker.md +222 -0
- package/docs/components/dropdown.md +275 -0
- package/docs/components/empty.md +200 -0
- package/docs/components/file-preview.md +180 -0
- package/docs/components/input.md +559 -0
- package/docs/components/modal.md +293 -0
- package/docs/components/popover.md +255 -0
- package/docs/components/radio.md +245 -0
- package/docs/components/select.md +254 -0
- package/docs/components/skeleton.md +150 -0
- package/docs/components/slider.md +346 -0
- package/docs/components/table.md +316 -0
- package/docs/components/tabs.md +432 -0
- package/docs/components/textarea.md +193 -0
- package/docs/components/timepicker.md +242 -0
- package/docs/components/timerangepicker.md +210 -0
- package/docs/components/toast.md +282 -0
- package/docs/components/toggle.md +211 -0
- package/docs/components/tooltip.md +213 -0
- package/docs/components/upload-avatar.md +318 -0
- package/docs/components/upload-file.md +245 -0
- package/docs/components/upload-image.md +126 -0
- package/docs/hooks/useDebounce.md +92 -0
- package/docs/hooks/useInfiniteScroll.md +95 -0
- package/docs/hooks/useSelectInfiniteQuery.md +123 -0
- package/docs/hooks/useTableQuery.md +124 -0
- package/package.json +31 -12
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# 📊 Hook `useTableQuery` (`@openway/ui/query`)
|
|
2
|
+
|
|
3
|
+
Hook adapter chuyên dụng kết nối **TanStack Query v5** (`useQuery`) với component `<Table />`, tự động hóa toàn bộ quy trình phân trang máy chủ (Server-side pagination), sắp xếp nhiều cột (Sorting), lọc dữ liệu (Filtering), và tối ưu UX qua `keepPreviousData`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🌟 Điểm nổi bật
|
|
8
|
+
|
|
9
|
+
- **Tự động đồng bộ với Table**: Trả về trọn gói `tableProps` sẵn sàng spread thẳng vào `<Table {...tableProps} />`.
|
|
10
|
+
- **Server Pagination**: Quản lý `page` (1-indexed), `pageSize`, tự động chuyển trang và tính toán tổng số trang (`pageCount`).
|
|
11
|
+
- **Server Sorting**: Đồng bộ `sortBy` và `sortOrder` ("asc" | "desc") từ trạng thái click header của Table gửi lên server.
|
|
12
|
+
- **Server Filtering & Auto-Reset**: Tự động chuyển đổi mảng filter của TanStack Table thành dictionary phẳng `Record<string, unknown>`, tự động đưa trang về trang 1 khi người dùng thay đổi bộ lọc (`autoResetPageIndex`).
|
|
13
|
+
- **Giữ dữ liệu mượt mà (`keepPreviousData`)**: Dữ liệu trang cũ vẫn hiển thị trong lúc trang mới đang tải ngầm, loại bỏ hiện tượng nhấp nháy trắng bảng.
|
|
14
|
+
- **Zero `any`**: Type-safe 100% với Generic type `TData` và `TResponse`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🚀 Import
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useTableQuery } from "@openway/ui/query";
|
|
22
|
+
import type {
|
|
23
|
+
TableQueryParams,
|
|
24
|
+
UseTableQueryOptions,
|
|
25
|
+
UseTableQueryReturn,
|
|
26
|
+
} from "@openway/ui/query";
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 📖 Hướng dẫn sử dụng
|
|
32
|
+
|
|
33
|
+
### Phân trang, Sắp xếp & Lọc phía Máy chủ
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Table, type ColumnDef } from "@openway/ui";
|
|
37
|
+
import { useTableQuery } from "@openway/ui/query";
|
|
38
|
+
|
|
39
|
+
interface User {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
email: string;
|
|
43
|
+
role: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface UserApiResponse {
|
|
47
|
+
data: User[];
|
|
48
|
+
total: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const columns: ColumnDef<User>[] = [
|
|
52
|
+
{ accessorKey: "name", header: "Họ và tên", enableSorting: true },
|
|
53
|
+
{ accessorKey: "email", header: "Email" },
|
|
54
|
+
{ accessorKey: "role", header: "Vai trò" },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
export function UserManagementTable() {
|
|
58
|
+
const { tableProps, query, page, setPage } = useTableQuery<User, UserApiResponse>({
|
|
59
|
+
queryKey: ["users-table"],
|
|
60
|
+
queryFn: async (params) => {
|
|
61
|
+
const searchParams = new URLSearchParams({
|
|
62
|
+
page: String(params.page),
|
|
63
|
+
pageSize: String(params.pageSize),
|
|
64
|
+
...(params.sortBy && { sortBy: params.sortBy, sortOrder: params.sortOrder ?? "asc" }),
|
|
65
|
+
});
|
|
66
|
+
const res = await fetch(`/api/users?${searchParams}`);
|
|
67
|
+
return res.json();
|
|
68
|
+
},
|
|
69
|
+
// Tự động bóc tách mảng dữ liệu và tổng số dòng từ response
|
|
70
|
+
selectData: (res) => res.data,
|
|
71
|
+
selectTotal: (res) => res.total,
|
|
72
|
+
initialPageSize: 10,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<div className="space-y-4">
|
|
77
|
+
<Table
|
|
78
|
+
{...tableProps}
|
|
79
|
+
columns={columns}
|
|
80
|
+
border
|
|
81
|
+
striped
|
|
82
|
+
hoverable
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 🎛️ Bảng Options (`UseTableQueryOptions`)
|
|
92
|
+
|
|
93
|
+
| Tên Option | Kiểu dữ liệu | Mặc định | Mô tả |
|
|
94
|
+
| :--- | :--- | :--- | :--- |
|
|
95
|
+
| `queryKey` | `readonly unknown[]` | **Bắt buộc** | Query key gốc của TanStack Query. |
|
|
96
|
+
| `queryFn` | `(params, context) => Promise<TResponse>` | **Bắt buộc** | Hàm fetch dữ liệu từ API nhận `TableQueryParams`. |
|
|
97
|
+
| `selectData` | `(res) => TData[]` | Tự trích xuất `data/items/results/rows` | Hàm lấy mảng dữ liệu từ API response. |
|
|
98
|
+
| `selectTotal` | `(res) => number` | Tự trích xuất `total/totalCount/count` | Hàm lấy tổng số bản ghi từ API response. |
|
|
99
|
+
| `initialPage` | `number` | `1` | Trang bắt đầu (1-indexed). |
|
|
100
|
+
| `initialPageSize` | `number` | `10` | Số dòng hiển thị mỗi trang. |
|
|
101
|
+
| `autoResetPageIndex` | `boolean` | `true` | Tự động quay về trang 1 khi đổi bộ lọc hoặc sắp xếp. |
|
|
102
|
+
| `queryOptions` | `Omit<UseQueryOptions, ...>` | `undefined` | Các cấu hình nâng cao của TanStack Query (`staleTime`, `refetchInterval`...). |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 📦 Giá trị trả về (`UseTableQueryReturn`)
|
|
107
|
+
|
|
108
|
+
- `tableProps`: Gói props truyền thẳng vào `<Table />`:
|
|
109
|
+
- `data: TData[]`
|
|
110
|
+
- `pageCount: number`
|
|
111
|
+
- `pagination: { pageIndex, pageSize }`
|
|
112
|
+
- `onPaginationChange: OnChangeFn<PaginationState>`
|
|
113
|
+
- `sorting: SortingState`
|
|
114
|
+
- `onSortingChange: OnChangeFn<SortingState>`
|
|
115
|
+
- `columnFilters: ColumnFiltersState`
|
|
116
|
+
- `onColumnFiltersChange: OnChangeFn<ColumnFiltersState>`
|
|
117
|
+
- `isLoading: boolean`
|
|
118
|
+
- `manualPagination: true`
|
|
119
|
+
- `manualSorting: true`
|
|
120
|
+
- `manualFiltering: true`
|
|
121
|
+
- `query`: Query result từ `useQuery`.
|
|
122
|
+
- `page`, `setPage`: Xem và thay đổi số trang hiện tại.
|
|
123
|
+
- `pageSize`, `setPageSize`: Xem và thay đổi số dòng/trang.
|
|
124
|
+
- `resetFilters`: Đặt lại toàn bộ bộ lọc.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openway/ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -15,49 +15,68 @@
|
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"import": "./dist/index.js",
|
|
17
17
|
"require": "./dist/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./query": {
|
|
20
|
+
"types": "./dist/query.d.ts",
|
|
21
|
+
"import": "./dist/query.js",
|
|
22
|
+
"require": "./dist/query.cjs"
|
|
18
23
|
}
|
|
19
24
|
},
|
|
20
25
|
"files": [
|
|
21
26
|
"dist",
|
|
27
|
+
"docs",
|
|
22
28
|
"README.md",
|
|
23
29
|
"LICENSE"
|
|
24
30
|
],
|
|
25
|
-
"scripts": {
|
|
26
|
-
"build": "tsup",
|
|
27
|
-
"prepublishOnly": "pnpm build",
|
|
28
|
-
"lint": "eslint",
|
|
29
|
-
"check-types": "tsc --noEmit",
|
|
30
|
-
"doctor": "pnpm dlx react-doctor@latest",
|
|
31
|
-
"cypress:open": "cypress open --component",
|
|
32
|
-
"cypress:run": "cypress run --component"
|
|
33
|
-
},
|
|
34
31
|
"peerDependencies": {
|
|
32
|
+
"@tanstack/react-query": ">=5.0.0",
|
|
35
33
|
"next": ">=16.0.0",
|
|
36
34
|
"react": ">=19.0.0",
|
|
37
35
|
"react-dom": ">=19.0.0",
|
|
38
36
|
"tailwindcss": ">=4.0.0"
|
|
39
37
|
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"@tanstack/react-query": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
40
43
|
"dependencies": {
|
|
44
|
+
"@dnd-kit/core": "^6.3.1",
|
|
45
|
+
"@dnd-kit/modifiers": "^9.0.0",
|
|
46
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
47
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
41
48
|
"@floating-ui/react": "^0.27.20",
|
|
42
49
|
"@radix-ui/react-slider": "^1.4.7",
|
|
43
50
|
"@tanstack/match-sorter-utils": "^9.1.2",
|
|
51
|
+
"@tanstack/react-table": "^9.2.4",
|
|
52
|
+
"react-dropzone": "^20.1.1",
|
|
44
53
|
"react-easy-crop": "^6.2.3",
|
|
45
54
|
"react-textarea-autosize": "^8.5.9",
|
|
46
55
|
"sonner": "^2.0.8"
|
|
47
56
|
},
|
|
48
57
|
"devDependencies": {
|
|
49
|
-
"eslint-config-next": "16.3.4",
|
|
50
58
|
"@tailwindcss/postcss": "^4.3.3",
|
|
59
|
+
"@tanstack/react-query": "^5.102.8",
|
|
51
60
|
"@types/node": "^22.15.3",
|
|
52
61
|
"@types/react": "19.2.2",
|
|
53
62
|
"@types/react-dom": "19.2.2",
|
|
54
63
|
"cypress": "^15.21.0",
|
|
55
64
|
"eslint": "^9.39.1",
|
|
65
|
+
"eslint-config-next": "16.3.4",
|
|
66
|
+
"eslint-config-prettier": "^10.1.8",
|
|
56
67
|
"next": "16.3.0",
|
|
57
68
|
"react": "^19.2.0",
|
|
58
69
|
"react-dom": "^19.2.0",
|
|
59
70
|
"tailwindcss": "^4.3.3",
|
|
60
71
|
"tsup": "^8.5.1",
|
|
61
72
|
"typescript": "^5.9.2"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "tsup",
|
|
76
|
+
"lint": "eslint",
|
|
77
|
+
"check-types": "tsc --noEmit",
|
|
78
|
+
"doctor": "pnpm dlx react-doctor@latest",
|
|
79
|
+
"cypress:open": "cypress open --component",
|
|
80
|
+
"cypress:run": "cypress run --component"
|
|
62
81
|
}
|
|
63
|
-
}
|
|
82
|
+
}
|