next-data-kit 0.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/LICENSE +21 -0
- package/README.md +478 -0
- package/dist/client/components/data-kit-table.d.ts +29 -0
- package/dist/client/components/data-kit.d.ts +19 -0
- package/dist/client/components/index.d.ts +3 -0
- package/dist/client/components/ui/button.d.ts +14 -0
- package/dist/client/components/ui/checkbox.d.ts +5 -0
- package/dist/client/components/ui/dropdown-menu.d.ts +28 -0
- package/dist/client/components/ui/index.d.ts +9 -0
- package/dist/client/components/ui/pagination.d.ts +14 -0
- package/dist/client/components/ui/popover.d.ts +10 -0
- package/dist/client/components/ui/select.d.ts +18 -0
- package/dist/client/components/ui/switch.d.ts +5 -0
- package/dist/client/components/ui/table.d.ts +11 -0
- package/dist/client/context/index.d.ts +22 -0
- package/dist/client/hooks/index.d.ts +7 -0
- package/dist/client/hooks/useDataKit.d.ts +9 -0
- package/dist/client/hooks/usePagination.d.ts +18 -0
- package/dist/client/hooks/useSelection.d.ts +13 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/utils/cn.d.ts +10 -0
- package/dist/client/utils/index.d.ts +60 -0
- package/dist/client.d.ts +7 -0
- package/dist/index.d.cts +652 -0
- package/dist/index.d.ts +652 -0
- package/dist/react-data-kit-DmTzxNTc.d.cts +225 -0
- package/dist/react-data-kit-DmTzxNTc.d.ts +225 -0
- package/dist/server.cjs +222 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +68 -0
- package/dist/server.d.ts +68 -0
- package/dist/server.js +216 -0
- package/dist/server.js.map +1 -0
- package/dist/types/component.d.ts +106 -0
- package/dist/types/database/mongo.d.ts +129 -0
- package/dist/types/hook.d.ts +100 -0
- package/dist/types/index.cjs +15 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +252 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.js +13 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/react-data-kit.d.ts +95 -0
- package/dist/types/selectable.d.ts +43 -0
- package/package.json +127 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 muhgholy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
# next-data-kit
|
|
2
|
+
|
|
3
|
+
A powerful table utility for server-side pagination, filtering, and sorting with React hooks and components.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
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
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install next-data-kit
|
|
20
|
+
# or
|
|
21
|
+
yarn add next-data-kit
|
|
22
|
+
# or
|
|
23
|
+
pnpm add next-data-kit
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### Server-side (Next.js Server Action)
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
"use server";
|
|
32
|
+
|
|
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
|
+
|
|
37
|
+
export async function fetchUsers(input: TDataKitInput) {
|
|
38
|
+
return dataKitServerAction({
|
|
39
|
+
model: UserModel,
|
|
40
|
+
input,
|
|
41
|
+
item: async (user) => ({
|
|
42
|
+
id: user._id.toString(),
|
|
43
|
+
name: user.name,
|
|
44
|
+
email: user.email,
|
|
45
|
+
}),
|
|
46
|
+
filter: () => ({
|
|
47
|
+
active: true,
|
|
48
|
+
}),
|
|
49
|
+
filterCustom: {
|
|
50
|
+
search: createSearchFilter(["name", "email"]),
|
|
51
|
+
age: (value) => ({
|
|
52
|
+
age: {
|
|
53
|
+
$gte: value,
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Client-side (DataKitTable Component)
|
|
62
|
+
|
|
63
|
+
Ready-to-use table with built-in filtering, sorting, and selection:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
"use client";
|
|
67
|
+
|
|
68
|
+
import { DataKitTable } from "next-data-kit/client";
|
|
69
|
+
import { fetchUsers } from "@/actions/users";
|
|
70
|
+
|
|
71
|
+
export function UsersTable() {
|
|
72
|
+
return (
|
|
73
|
+
<DataKitTable
|
|
74
|
+
action={fetchUsers}
|
|
75
|
+
limit={{ default: 10 }}
|
|
76
|
+
filters={[
|
|
77
|
+
{ id: "search", label: "Search", type: "TEXT", placeholder: "Search..." },
|
|
78
|
+
{
|
|
79
|
+
id: "role",
|
|
80
|
+
label: "Role",
|
|
81
|
+
type: "SELECT",
|
|
82
|
+
dataset: [
|
|
83
|
+
{ id: "admin", name: "admin", label: "Admin" },
|
|
84
|
+
{ id: "user", name: "user", label: "User" },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
]}
|
|
88
|
+
selectable={{
|
|
89
|
+
enabled: true,
|
|
90
|
+
actions: {
|
|
91
|
+
delete: {
|
|
92
|
+
name: "Delete Selected",
|
|
93
|
+
function: async (items) => {
|
|
94
|
+
await deleteUsers(items.map((i) => i.id));
|
|
95
|
+
return [true, { deselectAll: true }];
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}}
|
|
100
|
+
table={[
|
|
101
|
+
{
|
|
102
|
+
head: <DataKitTable.Head>Name</DataKitTable.Head>,
|
|
103
|
+
body: ({ item }) => <DataKitTable.Cell>{item.name}</DataKitTable.Cell>,
|
|
104
|
+
sortable: { path: "name", default: 0 },
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
head: <DataKitTable.Head>Email</DataKitTable.Head>,
|
|
108
|
+
body: ({ item }) => <DataKitTable.Cell>{item.email}</DataKitTable.Cell>,
|
|
109
|
+
},
|
|
110
|
+
]}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Client-side (DataKit Component - Custom Layout)
|
|
117
|
+
|
|
118
|
+
Use `DataKit` for grids, cards, or any custom layout. It provides toolbar/pagination but lets you render content:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
"use client";
|
|
122
|
+
|
|
123
|
+
import { DataKit } from "next-data-kit/client";
|
|
124
|
+
import { fetchUsers } from "@/actions/users";
|
|
125
|
+
|
|
126
|
+
export function UsersGrid() {
|
|
127
|
+
return (
|
|
128
|
+
<DataKit
|
|
129
|
+
action={fetchUsers}
|
|
130
|
+
limit={{ default: 12 }}
|
|
131
|
+
filters={[{ id: "search", label: "Search", type: "TEXT" }]}
|
|
132
|
+
>
|
|
133
|
+
{(dataKit) => (
|
|
134
|
+
<div className="grid grid-cols-4 gap-4">
|
|
135
|
+
{dataKit.items.map((user) => (
|
|
136
|
+
<div key={user.id} className="rounded-lg border p-4">
|
|
137
|
+
<h3>{user.name}</h3>
|
|
138
|
+
<p>{user.email}</p>
|
|
139
|
+
</div>
|
|
140
|
+
))}
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
</DataKit>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Manual mode** - handle loading/empty states yourself:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<DataKit action={fetchUsers} manual>
|
|
152
|
+
{(dataKit) => (
|
|
153
|
+
<>
|
|
154
|
+
{dataKit.state.isLoading && <Spinner />}
|
|
155
|
+
{dataKit.items.map((user) => <Card key={user.id} user={user} />)}
|
|
156
|
+
</>
|
|
157
|
+
)}
|
|
158
|
+
</DataKit>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Client-side (useDataKit Hook)
|
|
162
|
+
|
|
163
|
+
For fully custom implementations:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
"use client";
|
|
167
|
+
|
|
168
|
+
import { useDataKit } from "next-data-kit/client";
|
|
169
|
+
import { fetchUsers } from "@/actions/users";
|
|
170
|
+
|
|
171
|
+
export function UsersTable() {
|
|
172
|
+
const {
|
|
173
|
+
items,
|
|
174
|
+
page,
|
|
175
|
+
total,
|
|
176
|
+
state: { isLoading },
|
|
177
|
+
actions: { setPage, setFilter, setSort, refresh },
|
|
178
|
+
} = useDataKit({
|
|
179
|
+
action: fetchUsers,
|
|
180
|
+
initial: {
|
|
181
|
+
limit: 10,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<div>
|
|
187
|
+
<input placeholder="Search..." onChange={(e) => setFilter("search", e.target.value)} />
|
|
188
|
+
|
|
189
|
+
{isLoading ? (
|
|
190
|
+
<p>Loading...</p>
|
|
191
|
+
) : (
|
|
192
|
+
<table>
|
|
193
|
+
<thead>
|
|
194
|
+
<tr>
|
|
195
|
+
<th onClick={() => setSort("name", 1)}>Name</th>
|
|
196
|
+
<th onClick={() => setSort("email", 1)}>Email</th>
|
|
197
|
+
</tr>
|
|
198
|
+
</thead>
|
|
199
|
+
<tbody>
|
|
200
|
+
{items.map((user) => (
|
|
201
|
+
<tr key={user.id}>
|
|
202
|
+
<td>{user.name}</td>
|
|
203
|
+
<td>{user.email}</td>
|
|
204
|
+
</tr>
|
|
205
|
+
))}
|
|
206
|
+
</tbody>
|
|
207
|
+
</table>
|
|
208
|
+
)}
|
|
209
|
+
|
|
210
|
+
<button disabled={page === 1} onClick={() => setPage(page - 1)}>
|
|
211
|
+
Previous
|
|
212
|
+
</button>
|
|
213
|
+
<span>Page {page}</span>
|
|
214
|
+
<button onClick={() => setPage(page + 1)}>Next</button>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## API Reference
|
|
221
|
+
|
|
222
|
+
### Server
|
|
223
|
+
|
|
224
|
+
#### `dataKitServerAction(options)`
|
|
225
|
+
|
|
226
|
+
Main server function for handling table data fetching.
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
type TDataKitServerActionOptions<T, R> = {
|
|
230
|
+
input: TDataKitInput<T>;
|
|
231
|
+
model: TMongoModel<T>;
|
|
232
|
+
item: (item: T) => Promise<R> | R;
|
|
233
|
+
filter?: (filterInput?: Record<string, unknown>) => TMongoFilterQuery<T>;
|
|
234
|
+
filterCustom?: TFilterCustomConfigWithFilter<T, TMongoFilterQuery<T>>;
|
|
235
|
+
defaultSort?: TSortOptions<T>;
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### `createSearchFilter(fields)`
|
|
240
|
+
|
|
241
|
+
Create a search filter for multiple fields.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
filterCustom: {
|
|
245
|
+
search: createSearchFilter(["name", "email", "phone"]);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### `escapeRegex(str)`
|
|
250
|
+
|
|
251
|
+
Escape regex special characters in a string.
|
|
252
|
+
|
|
253
|
+
#### Custom Filter Implementation
|
|
254
|
+
|
|
255
|
+
You can implement custom filters manually without using `createSearchFilter`. This gives you full control over the database query.
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { escapeRegex } from "next-data-kit/server";
|
|
259
|
+
|
|
260
|
+
// ... inside dataKitServerAction options
|
|
261
|
+
filterCustom: {
|
|
262
|
+
// Manual search implementation
|
|
263
|
+
search: (value) => {
|
|
264
|
+
if (typeof value !== 'string') return {};
|
|
265
|
+
const term = escapeRegex(value);
|
|
266
|
+
return {
|
|
267
|
+
$or: [
|
|
268
|
+
{ name: { $regex: term, $options: "i" } },
|
|
269
|
+
{ email: { $regex: term, $options: "i" } },
|
|
270
|
+
],
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
// Filter by range
|
|
274
|
+
priceRange: (value: { min: number; max: number }) => ({
|
|
275
|
+
price: { $gte: value.min, $lte: value.max },
|
|
276
|
+
}),
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**Client Usage:**
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
const {
|
|
284
|
+
actions: { setFilter },
|
|
285
|
+
} = useDataKit({
|
|
286
|
+
/* ... */
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Trigger the manual search
|
|
290
|
+
setFilter("search", "query string");
|
|
291
|
+
|
|
292
|
+
// Trigger the range filter
|
|
293
|
+
setFilter("priceRange", { min: 10, max: 100 });
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Client
|
|
297
|
+
|
|
298
|
+
#### `<DataKitTable>` Component
|
|
299
|
+
|
|
300
|
+
Full-featured table component with built-in UI.
|
|
301
|
+
|
|
302
|
+
| Prop | Type | Description |
|
|
303
|
+
|------|------|-------------|
|
|
304
|
+
| `action` | `(input) => Promise<Result>` | Server action function |
|
|
305
|
+
| `table` | `Column[]` | Column definitions |
|
|
306
|
+
| `filters` | `FilterItem[]` | Filter configurations |
|
|
307
|
+
| `selectable` | `{ enabled, actions? }` | Selection & bulk actions |
|
|
308
|
+
| `limit` | `{ default: number }` | Items per page |
|
|
309
|
+
| `controller` | `Ref<Controller>` | External control ref |
|
|
310
|
+
| `className` | `string` | Container class |
|
|
311
|
+
| `bordered` | `boolean \| 'rounded'` | Border style |
|
|
312
|
+
| `refetchInterval` | `number` | Auto-refresh interval (ms) |
|
|
313
|
+
|
|
314
|
+
#### `<DataKit>` Component
|
|
315
|
+
|
|
316
|
+
Headless component for custom layouts (grids, cards, etc).
|
|
317
|
+
|
|
318
|
+
| Prop | Type | Description |
|
|
319
|
+
|------|------|-------------|
|
|
320
|
+
| `action` | `(input) => Promise<Result>` | Server action function |
|
|
321
|
+
| `filters` | `FilterItem[]` | Filter configurations |
|
|
322
|
+
| `limit` | `{ default: number }` | Items per page |
|
|
323
|
+
| `manual` | `boolean` | Skip loading/empty state handling |
|
|
324
|
+
| `children` | `(dataKit) => ReactNode` | Render function |
|
|
325
|
+
|
|
326
|
+
#### `useDataKit(options)`
|
|
327
|
+
|
|
328
|
+
React hook for managing next-data-kit state.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
interface TDataKitControllerOptions<T, R> {
|
|
332
|
+
action: (input: TDataKitInput<T>) => Promise<TDataKitResult<R>>;
|
|
333
|
+
initial?: {
|
|
334
|
+
page?: number;
|
|
335
|
+
limit?: number;
|
|
336
|
+
sorts?: TSortEntry[];
|
|
337
|
+
filter?: Record<string, unknown>;
|
|
338
|
+
query?: Record<string, unknown>;
|
|
339
|
+
};
|
|
340
|
+
filterConfig?: TFilterConfig;
|
|
341
|
+
onSuccess?: (result: TDataKitResult<R>) => void;
|
|
342
|
+
onError?: (error: Error) => void;
|
|
343
|
+
autoFetch?: boolean;
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
|
|
349
|
+
- `items` - Current page items
|
|
350
|
+
- `page` - Current page number
|
|
351
|
+
- `limit` - Items per page
|
|
352
|
+
- `total` - Total document count
|
|
353
|
+
- `sorts` - Current sort configuration
|
|
354
|
+
- `filter` - Current filter values
|
|
355
|
+
- `state`
|
|
356
|
+
- `isLoading` - Loading state
|
|
357
|
+
- `error` - Error state
|
|
358
|
+
- `actions`
|
|
359
|
+
- `setPage(page)` - Go to a specific page
|
|
360
|
+
- `setLimit(limit)` - Set items per page
|
|
361
|
+
- `setSort(path, value)` - Set sort for a column
|
|
362
|
+
- `setFilter(key, value)` - Set a filter value
|
|
363
|
+
- `clearFilters()` - Clear all filters
|
|
364
|
+
- `refresh()` - Refresh the table data
|
|
365
|
+
- `reset()` - Reset to initial state
|
|
366
|
+
|
|
367
|
+
#### `useSelection<T>()`
|
|
368
|
+
|
|
369
|
+
React hook for managing table row selection.
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
const { selectedIds, toggle, selectAll, deselectAll, isSelected, getSelectedArray } = useSelection<string>();
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### `usePagination(options)`
|
|
376
|
+
|
|
377
|
+
React hook for calculating pagination state.
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
const { pages, hasNextPage, hasPrevPage, totalPages } = usePagination({
|
|
381
|
+
page: 1,
|
|
382
|
+
limit: 10,
|
|
383
|
+
total: 100,
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Types
|
|
388
|
+
|
|
389
|
+
### Filter Types (Discriminated Union)
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
// TEXT - text input
|
|
393
|
+
{ id: "name", label: "Name", type: "TEXT", placeholder?: "..." }
|
|
394
|
+
|
|
395
|
+
// SELECT - dropdown (dataset required!)
|
|
396
|
+
{ id: "role", label: "Role", type: "SELECT", dataset: [{ id, name, label }] }
|
|
397
|
+
|
|
398
|
+
// BOOLEAN - toggle switch
|
|
399
|
+
{ id: "active", label: "Active", type: "BOOLEAN" }
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### `TDataKitInput<T>`
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
interface TDataKitInput<T = unknown> {
|
|
406
|
+
action?: "FETCH";
|
|
407
|
+
page?: number;
|
|
408
|
+
limit?: number;
|
|
409
|
+
sort?: TSortOptions<T>;
|
|
410
|
+
sorts?: TSortEntry[];
|
|
411
|
+
query?: Record<string, unknown>;
|
|
412
|
+
filter?: Record<string, unknown>;
|
|
413
|
+
filterConfig?: TFilterConfig;
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### `TDataKitResult<R>`
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
interface TDataKitResult<R> {
|
|
421
|
+
type: "ITEMS";
|
|
422
|
+
items: R[];
|
|
423
|
+
documentTotal: number;
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### `TFilterConfig`
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
interface TFilterConfig {
|
|
431
|
+
[key: string]: {
|
|
432
|
+
type: "regex" | "exact";
|
|
433
|
+
field?: string;
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Working with Custom Models
|
|
439
|
+
|
|
440
|
+
The package provides generic database types that work with any ORM/ODM:
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import type { TModel, TMongoFilterQuery, TQueryBuilder } from "next-data-kit/types";
|
|
444
|
+
|
|
445
|
+
// Your model just needs to implement the Model interface
|
|
446
|
+
interface MyModel extends TModel<MyDocument> {
|
|
447
|
+
countDocuments(filter?: TMongoFilterQuery<MyDocument>): Promise<number>;
|
|
448
|
+
find(filter?: TMongoFilterQuery<MyDocument>): TQueryBuilder<MyDocument[]>;
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## License
|
|
453
|
+
|
|
454
|
+
MIT © muhgholy
|
|
455
|
+
|
|
456
|
+
## Dev Playground (Live Server + Temp MongoDB)
|
|
457
|
+
|
|
458
|
+
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**.
|
|
459
|
+
|
|
460
|
+
- It is **not exported** in `package.json#exports`.
|
|
461
|
+
- It is **not published** to npm because `package.json#files` only includes `dist`.
|
|
462
|
+
|
|
463
|
+
### Run
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
npm run playground:dev
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Then open:
|
|
470
|
+
|
|
471
|
+
- Web UI: http://localhost:5173
|
|
472
|
+
- API health: http://127.0.0.1:8787/api/health
|
|
473
|
+
|
|
474
|
+
### Reset seed data
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
curl -X POST http://127.0.0.1:8787/api/seed/reset
|
|
478
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TableCell, TableHead } from './ui';
|
|
3
|
+
import type { TDataKitInput, TDataKitResult, TDataKitComponentColumn, TDataKitFilterItem, TDataKitBulkAction, TDataKitComponentController, TDataKitSelectableItem, TDataKitStateMode, TExtractDataKitItemType, TFilterConfig } from '../../types';
|
|
4
|
+
export declare const DataKitTable: (<TAction extends (input: TDataKitInput<unknown>) => Promise<TDataKitResult<TDataKitSelectableItem>>, TRowState = unknown>(props: Readonly<{
|
|
5
|
+
action: TAction;
|
|
6
|
+
query?: Record<string, unknown>;
|
|
7
|
+
filterConfig?: TFilterConfig;
|
|
8
|
+
table: TDataKitComponentColumn<TExtractDataKitItemType<TAction>, TRowState>[];
|
|
9
|
+
filters?: TDataKitFilterItem[];
|
|
10
|
+
selectable?: {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
actions?: Record<string, TDataKitBulkAction<TExtractDataKitItemType<TAction>>>;
|
|
13
|
+
};
|
|
14
|
+
initialState?: TRowState;
|
|
15
|
+
limit?: {
|
|
16
|
+
default: number;
|
|
17
|
+
};
|
|
18
|
+
className?: string;
|
|
19
|
+
autoFetch?: boolean;
|
|
20
|
+
debounce?: number;
|
|
21
|
+
bordered?: boolean | "rounded";
|
|
22
|
+
refetchInterval?: number;
|
|
23
|
+
state?: TDataKitStateMode;
|
|
24
|
+
controller?: React.MutableRefObject<TDataKitComponentController<TExtractDataKitItemType<TAction>> | null>;
|
|
25
|
+
}>) => import("react/jsx-runtime").JSX.Element) & {
|
|
26
|
+
Cell: typeof TableCell;
|
|
27
|
+
Head: typeof TableHead;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=data-kit-table.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { TDataKitInput, TDataKitResult, TDataKitFilterItem, TDataKitSelectableItem, TDataKitStateMode, TExtractDataKitItemType, TFilterConfig, TUseDataKitReturn } from '../../types';
|
|
3
|
+
export declare const DataKit: <TAction extends (input: TDataKitInput<unknown>) => Promise<TDataKitResult<TDataKitSelectableItem>>>(props: Readonly<{
|
|
4
|
+
action: TAction;
|
|
5
|
+
query?: Record<string, unknown>;
|
|
6
|
+
filterConfig?: TFilterConfig;
|
|
7
|
+
filters?: TDataKitFilterItem[];
|
|
8
|
+
limit?: {
|
|
9
|
+
default: number;
|
|
10
|
+
};
|
|
11
|
+
className?: string;
|
|
12
|
+
autoFetch?: boolean;
|
|
13
|
+
debounce?: number;
|
|
14
|
+
refetchInterval?: number;
|
|
15
|
+
state?: TDataKitStateMode;
|
|
16
|
+
manual?: boolean;
|
|
17
|
+
children: (dataKit: TUseDataKitReturn<unknown, TExtractDataKitItemType<TAction>>) => React.ReactNode;
|
|
18
|
+
}>) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=data-kit.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
declare const buttonVariants: (props?: ({
|
|
4
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
5
|
+
size?: "default" | "sm" | "lg" | "icon" | "icon-sm" | "icon-lg" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
+
declare const Button: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
8
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
9
|
+
size?: "default" | "sm" | "lg" | "icon" | "icon-sm" | "icon-lg" | null | undefined;
|
|
10
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
11
|
+
asChild?: boolean;
|
|
12
|
+
} & React.RefAttributes<HTMLButtonElement>>;
|
|
13
|
+
export { Button, buttonVariants };
|
|
14
|
+
//# sourceMappingURL=button.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
3
|
+
declare function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export { Checkbox };
|
|
5
|
+
//# sourceMappingURL=checkbox.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
3
|
+
declare function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function DropdownMenuPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function DropdownMenuTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function DropdownMenuContent({ className, sideOffset, container, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Content> & {
|
|
7
|
+
container?: HTMLElement | null;
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
declare function DropdownMenuItem({ className, inset, variant, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
11
|
+
inset?: boolean;
|
|
12
|
+
variant?: "default" | "destructive";
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
declare function DropdownMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
declare function DropdownMenuRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
declare function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
declare function DropdownMenuLabel({ className, inset, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
18
|
+
inset?: boolean;
|
|
19
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
declare function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
declare function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
declare function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
declare function DropdownMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
24
|
+
inset?: boolean;
|
|
25
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
declare function DropdownMenuSubContent({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
|
|
28
|
+
//# sourceMappingURL=dropdown-menu.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption, } from './table';
|
|
2
|
+
export { Checkbox } from './checkbox';
|
|
3
|
+
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from './popover';
|
|
4
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from './dropdown-menu';
|
|
5
|
+
export { Switch } from './switch';
|
|
6
|
+
export { Button, buttonVariants } from './button';
|
|
7
|
+
export { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis, } from './pagination';
|
|
8
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from './select';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type Button } from './button';
|
|
3
|
+
declare function Pagination({ className, ...props }: React.ComponentProps<'nav'>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function PaginationContent({ className, ...props }: React.ComponentProps<'ul'>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function PaginationItem({ ...props }: React.ComponentProps<'li'>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
type PaginationLinkProps = {
|
|
7
|
+
isActive?: boolean;
|
|
8
|
+
} & Pick<React.ComponentProps<typeof Button>, 'size' | 'disabled' | 'onClick' | 'type'> & Omit<React.ComponentProps<'button'>, 'type' | 'onClick' | 'disabled'>;
|
|
9
|
+
declare function PaginationLink({ className, isActive, size, type, ...props }: PaginationLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
declare function PaginationPrevious({ className, ...props }: React.ComponentProps<typeof PaginationLink>): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
declare function PaginationNext({ className, ...props }: React.ComponentProps<typeof PaginationLink>): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
declare function PaginationEllipsis({ className, ...props }: React.ComponentProps<'span'>): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis, };
|
|
14
|
+
//# sourceMappingURL=pagination.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
3
|
+
declare function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function PopoverContent({ className, align, sideOffset, container, ...props }: React.ComponentProps<typeof PopoverPrimitive.Content> & {
|
|
6
|
+
container?: HTMLElement | null;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
|
|
10
|
+
//# sourceMappingURL=popover.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
3
|
+
declare function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function SelectTrigger({ className, size, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
7
|
+
size?: "sm" | "default";
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare function SelectContent({ className, children, position, align, container, ...props }: React.ComponentProps<typeof SelectPrimitive.Content> & {
|
|
10
|
+
container?: HTMLElement | null;
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
declare function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
declare function SelectItem({ className, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Item>): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
declare function SelectSeparator({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Separator>): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
declare function SelectScrollUpButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
declare function SelectScrollDownButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
|
|
18
|
+
//# sourceMappingURL=select.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SwitchPrimitive from '@radix-ui/react-switch';
|
|
3
|
+
declare function Switch({ className, ...props }: React.ComponentProps<typeof SwitchPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export { Switch };
|
|
5
|
+
//# sourceMappingURL=switch.d.ts.map
|