next-data-kit 2.0.0 → 3.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 +152 -221
- package/dist/client/hooks/useDataKit.d.ts.map +1 -1
- package/dist/client/hooks/useDataKit.js +20 -18
- package/dist/client/hooks/useDataKit.js.map +1 -1
- package/dist/index.cjs +4 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +4 -18
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +2 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,14 +4,14 @@ A powerful table utility for server-side pagination, filtering, and sorting with
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
7
|
+
- 🚀 **Server-side pagination** - Efficient data fetching with page-based navigation
|
|
8
|
+
- 🔍 **Flexible filtering** - Support for regex, exact match, and custom filters
|
|
9
|
+
- 📊 **Multi-column sorting** - Sort by multiple columns with customizable order
|
|
10
|
+
- ⚛️ **React hooks** - `useDataKit`, `useSelection`, `usePagination` for state management
|
|
11
|
+
- 🎨 **Components** - `DataKitTable` for tables, `DataKit` for custom layouts
|
|
12
|
+
- 📝 **TypeScript** - Fully typed with generics support
|
|
13
|
+
- 🔌 **Framework agnostic** - Works with any database ORM/ODM (Mongoose, Prisma, etc.)
|
|
14
|
+
- 📦 **Tree-shakeable** - Import only what you need
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -28,57 +28,50 @@ pnpm add next-data-kit
|
|
|
28
28
|
### Server-side (Next.js Server Action)
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
|
-
|
|
31
|
+
'use server';
|
|
32
32
|
|
|
33
|
-
import { dataKitServerAction, createSearchFilter } from
|
|
34
|
-
import type { TDataKitInput } from
|
|
35
|
-
import UserModel from
|
|
33
|
+
import { dataKitServerAction, createSearchFilter } from 'next-data-kit/server';
|
|
34
|
+
import type { TDataKitInput } from 'next-data-kit/types';
|
|
35
|
+
import UserModel from '@/models/User';
|
|
36
36
|
|
|
37
37
|
export async function fetchUsers(input: TDataKitInput) {
|
|
38
38
|
return dataKitServerAction({
|
|
39
39
|
model: UserModel,
|
|
40
40
|
input,
|
|
41
|
-
item: async
|
|
41
|
+
item: async user => ({
|
|
42
42
|
id: user._id.toString(),
|
|
43
43
|
name: user.name,
|
|
44
44
|
email: user.email,
|
|
45
45
|
}),
|
|
46
|
-
filter: () => ({
|
|
47
|
-
active: true,
|
|
48
|
-
}),
|
|
49
46
|
filterCustom: {
|
|
50
|
-
search: createSearchFilter([
|
|
51
|
-
|
|
52
|
-
age: {
|
|
53
|
-
$gte: value,
|
|
54
|
-
}
|
|
55
|
-
})
|
|
47
|
+
search: createSearchFilter(['name', 'email']),
|
|
48
|
+
age: value => ({ age: { $gte: value } }),
|
|
56
49
|
},
|
|
57
|
-
filterAllowed: ["search", "age"],
|
|
58
50
|
});
|
|
59
51
|
}
|
|
60
52
|
```
|
|
61
53
|
|
|
62
|
-
|
|
63
54
|
### Input Validation (Optional)
|
|
64
55
|
|
|
65
56
|
You can use the built-in Zod schema to validate inputs before processing:
|
|
66
57
|
|
|
67
58
|
```typescript
|
|
68
|
-
|
|
59
|
+
'use server';
|
|
69
60
|
|
|
70
|
-
import { dataKitServerAction, dataKitSchemaZod } from
|
|
61
|
+
import { dataKitServerAction, dataKitSchemaZod } from 'next-data-kit/server';
|
|
71
62
|
|
|
72
63
|
export async function fetchUsers(input: unknown) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
64
|
+
const parsedInput = dataKitSchemaZod.parse(input);
|
|
65
|
+
|
|
66
|
+
return dataKitServerAction({
|
|
67
|
+
model: UserModel,
|
|
68
|
+
input: parsedInput,
|
|
69
|
+
item: user => ({ id: user._id.toString(), name: user.name }),
|
|
70
|
+
filterCustom: {
|
|
71
|
+
search: value => ({ name: { $regex: value, $options: 'i' } }),
|
|
72
|
+
role: value => ({ role: value }),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
82
75
|
}
|
|
83
76
|
```
|
|
84
77
|
|
|
@@ -87,10 +80,10 @@ export async function fetchUsers(input: unknown) {
|
|
|
87
80
|
Ready-to-use table with built-in filtering, sorting, and selection:
|
|
88
81
|
|
|
89
82
|
```tsx
|
|
90
|
-
|
|
83
|
+
'use client';
|
|
91
84
|
|
|
92
|
-
import { DataKitTable } from
|
|
93
|
-
import { fetchUsers } from
|
|
85
|
+
import { DataKitTable } from 'next-data-kit/client';
|
|
86
|
+
import { fetchUsers } from '@/actions/users';
|
|
94
87
|
|
|
95
88
|
export function UsersTable() {
|
|
96
89
|
return (
|
|
@@ -98,14 +91,14 @@ export function UsersTable() {
|
|
|
98
91
|
action={fetchUsers}
|
|
99
92
|
limit={{ default: 10 }}
|
|
100
93
|
filters={[
|
|
101
|
-
{ id:
|
|
94
|
+
{ id: 'search', label: 'Search', type: 'TEXT', placeholder: 'Search...' },
|
|
102
95
|
{
|
|
103
|
-
id:
|
|
104
|
-
label:
|
|
105
|
-
type:
|
|
96
|
+
id: 'role',
|
|
97
|
+
label: 'Role',
|
|
98
|
+
type: 'SELECT',
|
|
106
99
|
dataset: [
|
|
107
|
-
{ id:
|
|
108
|
-
{ id:
|
|
100
|
+
{ id: 'admin', name: 'admin', label: 'Admin' },
|
|
101
|
+
{ id: 'user', name: 'user', label: 'User' },
|
|
109
102
|
],
|
|
110
103
|
},
|
|
111
104
|
]}
|
|
@@ -113,9 +106,9 @@ export function UsersTable() {
|
|
|
113
106
|
enabled: true,
|
|
114
107
|
actions: {
|
|
115
108
|
delete: {
|
|
116
|
-
name:
|
|
117
|
-
function: async
|
|
118
|
-
await deleteUsers(items.map(
|
|
109
|
+
name: 'Delete Selected',
|
|
110
|
+
function: async items => {
|
|
111
|
+
await deleteUsers(items.map(i => i.id));
|
|
119
112
|
return [true, { deselectAll: true }];
|
|
120
113
|
},
|
|
121
114
|
},
|
|
@@ -125,7 +118,7 @@ export function UsersTable() {
|
|
|
125
118
|
{
|
|
126
119
|
head: <DataKitTable.Head>Name</DataKitTable.Head>,
|
|
127
120
|
body: ({ item }) => <DataKitTable.Cell>{item.name}</DataKitTable.Cell>,
|
|
128
|
-
sortable: { path:
|
|
121
|
+
sortable: { path: 'name', default: 0 },
|
|
129
122
|
},
|
|
130
123
|
{
|
|
131
124
|
head: <DataKitTable.Head>Email</DataKitTable.Head>,
|
|
@@ -142,22 +135,18 @@ export function UsersTable() {
|
|
|
142
135
|
Use `DataKit` for grids, cards, or any custom layout. It provides toolbar/pagination but lets you render content:
|
|
143
136
|
|
|
144
137
|
```tsx
|
|
145
|
-
|
|
138
|
+
'use client';
|
|
146
139
|
|
|
147
|
-
import { DataKit } from
|
|
148
|
-
import { fetchUsers } from
|
|
140
|
+
import { DataKit } from 'next-data-kit/client';
|
|
141
|
+
import { fetchUsers } from '@/actions/users';
|
|
149
142
|
|
|
150
143
|
export function UsersGrid() {
|
|
151
144
|
return (
|
|
152
|
-
<DataKit
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
{(dataKit) => (
|
|
158
|
-
<div className="grid grid-cols-4 gap-4">
|
|
159
|
-
{dataKit.items.map((user) => (
|
|
160
|
-
<div key={user.id} className="rounded-lg border p-4">
|
|
145
|
+
<DataKit action={fetchUsers} limit={{ default: 12 }} filters={[{ id: 'search', label: 'Search', type: 'TEXT' }]}>
|
|
146
|
+
{dataKit => (
|
|
147
|
+
<div className='grid grid-cols-4 gap-4'>
|
|
148
|
+
{dataKit.items.map(user => (
|
|
149
|
+
<div key={user.id} className='rounded-lg border p-4'>
|
|
161
150
|
<h3>{user.name}</h3>
|
|
162
151
|
<p>{user.email}</p>
|
|
163
152
|
</div>
|
|
@@ -173,10 +162,12 @@ export function UsersGrid() {
|
|
|
173
162
|
|
|
174
163
|
```tsx
|
|
175
164
|
<DataKit action={fetchUsers} manual>
|
|
176
|
-
{
|
|
165
|
+
{dataKit => (
|
|
177
166
|
<>
|
|
178
167
|
{dataKit.state.isLoading && <Spinner />}
|
|
179
|
-
{dataKit.items.map(
|
|
168
|
+
{dataKit.items.map(user => (
|
|
169
|
+
<Card key={user.id} user={user} />
|
|
170
|
+
))}
|
|
180
171
|
</>
|
|
181
172
|
)}
|
|
182
173
|
</DataKit>
|
|
@@ -187,10 +178,10 @@ export function UsersGrid() {
|
|
|
187
178
|
For fully custom implementations:
|
|
188
179
|
|
|
189
180
|
```tsx
|
|
190
|
-
|
|
181
|
+
'use client';
|
|
191
182
|
|
|
192
|
-
import { useDataKit } from
|
|
193
|
-
import { fetchUsers } from
|
|
183
|
+
import { useDataKit } from 'next-data-kit/client';
|
|
184
|
+
import { fetchUsers } from '@/actions/users';
|
|
194
185
|
|
|
195
186
|
export function UsersTable() {
|
|
196
187
|
const {
|
|
@@ -208,7 +199,7 @@ export function UsersTable() {
|
|
|
208
199
|
|
|
209
200
|
return (
|
|
210
201
|
<div>
|
|
211
|
-
<input placeholder=
|
|
202
|
+
<input placeholder='Search...' onChange={e => setFilter('search', e.target.value)} />
|
|
212
203
|
|
|
213
204
|
{isLoading ? (
|
|
214
205
|
<p>Loading...</p>
|
|
@@ -216,12 +207,12 @@ export function UsersTable() {
|
|
|
216
207
|
<table>
|
|
217
208
|
<thead>
|
|
218
209
|
<tr>
|
|
219
|
-
<th onClick={() => setSort(
|
|
220
|
-
<th onClick={() => setSort(
|
|
210
|
+
<th onClick={() => setSort('name', 1)}>Name</th>
|
|
211
|
+
<th onClick={() => setSort('email', 1)}>Email</th>
|
|
221
212
|
</tr>
|
|
222
213
|
</thead>
|
|
223
214
|
<tbody>
|
|
224
|
-
{items.map(
|
|
215
|
+
{items.map(user => (
|
|
225
216
|
<tr key={user.id}>
|
|
226
217
|
<td>{user.name}</td>
|
|
227
218
|
<td>{user.email}</td>
|
|
@@ -255,12 +246,16 @@ type TDataKitServerActionOptions<T, R> = {
|
|
|
255
246
|
model: TMongoModel<T>;
|
|
256
247
|
item: (item: T) => Promise<R> | R;
|
|
257
248
|
filter?: (filterInput?: Record<string, unknown>) => TMongoFilterQuery<T>;
|
|
249
|
+
// ** Custom filter configuration (defines allowed filter keys)
|
|
258
250
|
filterCustom?: TFilterCustomConfigWithFilter<T, TMongoFilterQuery<T>>;
|
|
259
251
|
defaultSort?: TSortOptions<T>;
|
|
260
|
-
|
|
261
|
-
|
|
252
|
+
// ** Maximum limit per page (default: 100)
|
|
253
|
+
maxLimit?: number;
|
|
254
|
+
// ** Whitelist of allowed query fields
|
|
255
|
+
queryAllowed?: string[];
|
|
262
256
|
};
|
|
263
257
|
```
|
|
258
|
+
|
|
264
259
|
```
|
|
265
260
|
|
|
266
261
|
// ... inside dataKitServerAction options
|
|
@@ -268,138 +263,74 @@ type TDataKitServerActionOptions<T, R> = {
|
|
|
268
263
|
}
|
|
269
264
|
```
|
|
270
265
|
|
|
271
|
-
### Security
|
|
266
|
+
### Security & Filtering
|
|
272
267
|
|
|
273
|
-
|
|
268
|
+
**Two ways to query data:**
|
|
269
|
+
|
|
270
|
+
1. **`filterCustom`** - User-facing filters (search, dropdowns, etc.)
|
|
271
|
+
- Client `filters` prop → validated against `filterCustom` keys
|
|
272
|
+
- Only defined keys are allowed (throws error otherwise)
|
|
273
|
+
|
|
274
|
+
2. **`queryAllowed`** - Direct field matching (fixed filters)
|
|
275
|
+
- Explicit whitelist required
|
|
276
|
+
- Use for: `{ active: true }`, user-specific queries
|
|
274
277
|
|
|
275
278
|
```typescript
|
|
276
|
-
// Strict Security Example
|
|
277
279
|
dataKitServerAction({
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
280
|
+
model: UserModel,
|
|
281
|
+
input,
|
|
282
|
+
item: u => u,
|
|
283
|
+
filterCustom: {
|
|
284
|
+
search: createSearchFilter(['name', 'email']),
|
|
285
|
+
role: value => ({ role: value }),
|
|
286
|
+
},
|
|
287
|
+
queryAllowed: ['organizationId', 'active'],
|
|
288
|
+
});
|
|
286
289
|
```
|
|
287
290
|
|
|
288
|
-
### Error Handling
|
|
291
|
+
### Error Handling
|
|
289
292
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
- **`DataKitTable`**: Automatically displays the error message in Red within the table body.
|
|
293
|
-
- **`useDataKit`**: The error is available in `state.error` for custom handling.
|
|
293
|
+
Errors are automatically displayed in `DataKitTable` or available via `state.error` in `useDataKit`.
|
|
294
294
|
|
|
295
295
|
```tsx
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (error) {
|
|
300
|
-
return <div className="text-red-500">Error: {error.message}</div>;
|
|
301
|
-
}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Input Validation (Optional)
|
|
305
|
-
type TDataKitServerActionOptions<T, R> = {
|
|
306
|
-
input: TDataKitInput<T>;
|
|
307
|
-
model: TMongoModel<T>;
|
|
308
|
-
item: (item: T) => Promise<R> | R;
|
|
309
|
-
filter?: (filterInput?: Record<string, unknown>) => TMongoFilterQuery<T>;
|
|
310
|
-
filterCustom?: TFilterCustomConfigWithFilter<T, TMongoFilterQuery<T>>;
|
|
311
|
-
defaultSort?: TSortOptions<T>;
|
|
312
|
-
// ** Whitelist of allowed filter fields (crucial for security when using query prop)
|
|
313
|
-
filterAllowed?: string[];
|
|
314
|
-
};
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
#### `createSearchFilter(fields)`
|
|
318
|
-
|
|
319
|
-
Create a search filter for multiple fields.
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
filterCustom: {
|
|
323
|
-
search: createSearchFilter(["name", "email", "phone"]);
|
|
324
|
-
}
|
|
296
|
+
const {
|
|
297
|
+
state: { error },
|
|
298
|
+
} = useDataKit({ action: fetchUsers });
|
|
299
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
325
300
|
```
|
|
326
301
|
|
|
327
|
-
####
|
|
328
|
-
|
|
329
|
-
Escape regex special characters in a string.
|
|
330
|
-
|
|
331
|
-
#### Custom Filter Implementation
|
|
332
|
-
|
|
333
|
-
You can implement custom filters manually without using `createSearchFilter`. This gives you full control over the database query.
|
|
302
|
+
#### Custom Filters
|
|
334
303
|
|
|
335
304
|
```typescript
|
|
336
|
-
import { escapeRegex } from
|
|
305
|
+
import { createSearchFilter, escapeRegex } from 'next-data-kit/server';
|
|
337
306
|
|
|
338
|
-
// ... inside dataKitServerAction options
|
|
339
307
|
filterCustom: {
|
|
340
|
-
//
|
|
341
|
-
search: (
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
$or: [
|
|
346
|
-
{ name: { $regex: term, $options: "i" } },
|
|
347
|
-
{ email: { $regex: term, $options: "i" } },
|
|
348
|
-
],
|
|
349
|
-
};
|
|
350
|
-
},
|
|
351
|
-
// Filter by range
|
|
308
|
+
// Use built-in helper
|
|
309
|
+
search: createSearchFilter(['name', 'email', 'phone']),
|
|
310
|
+
|
|
311
|
+
// Or implement custom logic
|
|
352
312
|
priceRange: (value: { min: number; max: number }) => ({
|
|
353
313
|
price: { $gte: value.min, $lte: value.max },
|
|
354
314
|
}),
|
|
355
|
-
}
|
|
356
|
-
filterAllowed: ["search", "priceRange"]
|
|
315
|
+
}
|
|
357
316
|
```
|
|
358
317
|
|
|
359
|
-
####
|
|
360
|
-
|
|
361
|
-
To use custom filters effectively, you must match the **Key** on the client with the **Key** on the server.
|
|
362
|
-
|
|
363
|
-
1. **Client-side**: Define a filter with a specific `id` (e.g., `'priceRange'`).
|
|
364
|
-
```tsx
|
|
365
|
-
// Client Component
|
|
366
|
-
<DataKitTable
|
|
367
|
-
filters={[
|
|
368
|
-
{ id: 'priceRange', label: 'Price Range', type: 'TEXT' }
|
|
369
|
-
]}
|
|
370
|
-
// ...
|
|
371
|
-
/>
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
_Note: When use interact with this filter, `DataKit` sends `{ filter: { priceRange: "value" } }` to the server._
|
|
318
|
+
#### Filter Flow
|
|
375
319
|
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
// Server Action
|
|
379
|
-
filterCustom: {
|
|
380
|
-
// MATCHES 'priceRange' FROM CLIENT
|
|
381
|
-
priceRange: (value) => ({
|
|
382
|
-
price: { $lte: Number(value) }
|
|
383
|
-
})
|
|
384
|
-
}
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
The `filterCustom` function intercepts the value sent from the client before it hits the database query builder, allowing you to transform simple values into complex queries.
|
|
388
|
-
|
|
389
|
-
**Client Usage:**
|
|
320
|
+
Match client filter `id` with server `filterCustom` key:
|
|
390
321
|
|
|
391
322
|
```tsx
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
} = useDataKit({
|
|
395
|
-
/* ... */
|
|
396
|
-
});
|
|
323
|
+
// Client
|
|
324
|
+
<DataKitTable filters={[{ id: 'priceRange', label: 'Price', type: 'TEXT' }]} />
|
|
397
325
|
|
|
398
|
-
//
|
|
399
|
-
|
|
326
|
+
// Server
|
|
327
|
+
filterCustom: {
|
|
328
|
+
priceRange: value => ({ price: { $lte: Number(value) } }),
|
|
329
|
+
}
|
|
400
330
|
|
|
401
|
-
//
|
|
402
|
-
|
|
331
|
+
// Or use programmatically
|
|
332
|
+
const { actions: { setFilter } } = useDataKit({ ... });
|
|
333
|
+
setFilter('priceRange', 100);
|
|
403
334
|
```
|
|
404
335
|
|
|
405
336
|
### Client
|
|
@@ -408,29 +339,29 @@ setFilter("priceRange", { min: 10, max: 100 });
|
|
|
408
339
|
|
|
409
340
|
Full-featured table component with built-in UI.
|
|
410
341
|
|
|
411
|
-
| Prop
|
|
412
|
-
|
|
413
|
-
| `action`
|
|
414
|
-
| `table`
|
|
415
|
-
| `filters`
|
|
416
|
-
| `selectable`
|
|
417
|
-
| `limit`
|
|
418
|
-
| `controller`
|
|
419
|
-
| `className`
|
|
420
|
-
| `bordered`
|
|
421
|
-
| `refetchInterval` | `number`
|
|
342
|
+
| Prop | Type | Description |
|
|
343
|
+
| ----------------- | ---------------------------- | -------------------------- |
|
|
344
|
+
| `action` | `(input) => Promise<Result>` | Server action function |
|
|
345
|
+
| `table` | `Column[]` | Column definitions |
|
|
346
|
+
| `filters` | `FilterItem[]` | Filter configurations |
|
|
347
|
+
| `selectable` | `{ enabled, actions? }` | Selection & bulk actions |
|
|
348
|
+
| `limit` | `{ default: number }` | Items per page |
|
|
349
|
+
| `controller` | `Ref<Controller>` | External control ref |
|
|
350
|
+
| `className` | `string` | Container class |
|
|
351
|
+
| `bordered` | `boolean \| 'rounded'` | Border style |
|
|
352
|
+
| `refetchInterval` | `number` | Auto-refresh interval (ms) |
|
|
422
353
|
|
|
423
354
|
#### `<DataKit>` Component
|
|
424
355
|
|
|
425
356
|
Headless component for custom layouts (grids, cards, etc).
|
|
426
357
|
|
|
427
|
-
| Prop
|
|
428
|
-
|
|
429
|
-
| `action`
|
|
430
|
-
| `filters`
|
|
431
|
-
| `limit`
|
|
432
|
-
| `manual`
|
|
433
|
-
| `children` | `(dataKit) => ReactNode`
|
|
358
|
+
| Prop | Type | Description |
|
|
359
|
+
| ---------- | ---------------------------- | --------------------------------- |
|
|
360
|
+
| `action` | `(input) => Promise<Result>` | Server action function |
|
|
361
|
+
| `filters` | `FilterItem[]` | Filter configurations |
|
|
362
|
+
| `limit` | `{ default: number }` | Items per page |
|
|
363
|
+
| `manual` | `boolean` | Skip loading/empty state handling |
|
|
364
|
+
| `children` | `(dataKit) => ReactNode` | Render function |
|
|
434
365
|
|
|
435
366
|
#### `useDataKit(options)`
|
|
436
367
|
|
|
@@ -455,23 +386,23 @@ interface TDataKitControllerOptions<T, R> {
|
|
|
455
386
|
|
|
456
387
|
Returns:
|
|
457
388
|
|
|
458
|
-
-
|
|
459
|
-
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
-
|
|
463
|
-
-
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
-
|
|
467
|
-
-
|
|
468
|
-
-
|
|
469
|
-
-
|
|
470
|
-
-
|
|
471
|
-
-
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
-
|
|
389
|
+
- `items` - Current page items
|
|
390
|
+
- `page` - Current page number
|
|
391
|
+
- `limit` - Items per page
|
|
392
|
+
- `total` - Total document count
|
|
393
|
+
- `sorts` - Current sort configuration
|
|
394
|
+
- `filter` - Current filter values
|
|
395
|
+
- `state`
|
|
396
|
+
- `isLoading` - Loading state
|
|
397
|
+
- `error` - Error state
|
|
398
|
+
- `actions`
|
|
399
|
+
- `setPage(page)` - Go to a specific page
|
|
400
|
+
- `setLimit(limit)` - Set items per page
|
|
401
|
+
- `setSort(path, value)` - Set sort for a column
|
|
402
|
+
- `setFilter(key, value)` - Set a filter value
|
|
403
|
+
- `clearFilters()` - Clear all filters
|
|
404
|
+
- `refresh()` - Refresh the table data
|
|
405
|
+
- `reset()` - Reset to initial state
|
|
475
406
|
|
|
476
407
|
#### `useSelection<T>()`
|
|
477
408
|
|
|
@@ -512,7 +443,7 @@ const { pages, hasNextPage, hasPrevPage, totalPages } = usePagination({
|
|
|
512
443
|
|
|
513
444
|
```typescript
|
|
514
445
|
interface TDataKitInput<T = unknown> {
|
|
515
|
-
action?:
|
|
446
|
+
action?: 'FETCH';
|
|
516
447
|
page?: number;
|
|
517
448
|
limit?: number;
|
|
518
449
|
sort?: TSortOptions<T>;
|
|
@@ -527,7 +458,7 @@ interface TDataKitInput<T = unknown> {
|
|
|
527
458
|
|
|
528
459
|
```typescript
|
|
529
460
|
interface TDataKitResult<R> {
|
|
530
|
-
type:
|
|
461
|
+
type: 'ITEMS';
|
|
531
462
|
items: R[];
|
|
532
463
|
documentTotal: number;
|
|
533
464
|
}
|
|
@@ -538,7 +469,7 @@ interface TDataKitResult<R> {
|
|
|
538
469
|
```typescript
|
|
539
470
|
interface TFilterConfig {
|
|
540
471
|
[key: string]: {
|
|
541
|
-
type:
|
|
472
|
+
type: 'REGEX' | 'EXACT';
|
|
542
473
|
field?: string;
|
|
543
474
|
};
|
|
544
475
|
}
|
|
@@ -549,7 +480,7 @@ interface TFilterConfig {
|
|
|
549
480
|
The package provides generic database types that work with any ORM/ODM:
|
|
550
481
|
|
|
551
482
|
```typescript
|
|
552
|
-
import type { TModel, TMongoFilterQuery, TQueryBuilder } from
|
|
483
|
+
import type { TModel, TMongoFilterQuery, TQueryBuilder } from 'next-data-kit/types';
|
|
553
484
|
|
|
554
485
|
// Your model just needs to implement the Model interface
|
|
555
486
|
interface MyModel extends TModel<MyDocument> {
|
|
@@ -566,8 +497,8 @@ MIT © muhgholy
|
|
|
566
497
|
|
|
567
498
|
This repo includes a **dev-only** playground you can open in the browser to preview components and validate end-to-end behavior against a **temporary in-memory MongoDB**.
|
|
568
499
|
|
|
569
|
-
-
|
|
570
|
-
-
|
|
500
|
+
- It is **not exported** in `package.json#exports`.
|
|
501
|
+
- It is **not published** to npm because `package.json#files` only includes `dist`.
|
|
571
502
|
|
|
572
503
|
### Run
|
|
573
504
|
|
|
@@ -577,8 +508,8 @@ npm run playground:dev
|
|
|
577
508
|
|
|
578
509
|
Then open:
|
|
579
510
|
|
|
580
|
-
-
|
|
581
|
-
-
|
|
511
|
+
- Web UI: http://localhost:5173
|
|
512
|
+
- API health: http://127.0.0.1:8787/api/health
|
|
582
513
|
|
|
583
514
|
### Reset seed data
|
|
584
515
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDataKit.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useDataKit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"useDataKit.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useDataKit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAiC,yBAAyB,EAAc,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAO3H,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,OAAO,QAAQ,CAAC,yBAAyB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAG,iBAAiB,CAAC,CAAC,EAAE,CAAC,CA2Q7H,CAAC;AAEF,YAAY,EAAE,yBAAyB,EAAE,CAAC"}
|