yk-grid 0.1.1 → 0.1.3
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 +118 -116
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# yk-grid
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
A production-ready React DataGrid component with built-in sorting, filtering, grouping, pagination, selection, column management, CSV export, virtual scrolling, inline cell editing, and optional AI-assisted natural-language query input.
|
|
4
6
|
|
|
5
7
|
Dual-format library (ESM + CJS) with full TypeScript generics. Zero runtime dependencies beyond React, Zod, and @tanstack/react-virtual.
|
|
@@ -40,7 +42,7 @@ npm install react react-dom zod
|
|
|
40
42
|
Import the library stylesheet once in your app entry point:
|
|
41
43
|
|
|
42
44
|
```tsx
|
|
43
|
-
import 'yk-grid/dist/yk-grid.css'
|
|
45
|
+
import 'yk-grid/dist/yk-grid.css';
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
---
|
|
@@ -48,24 +50,24 @@ import 'yk-grid/dist/yk-grid.css'
|
|
|
48
50
|
## Quick start
|
|
49
51
|
|
|
50
52
|
```tsx
|
|
51
|
-
import 'yk-grid/dist/yk-grid.css'
|
|
52
|
-
import { DataGrid } from 'yk-grid'
|
|
53
|
-
import type { ColumnDef } from 'yk-grid'
|
|
53
|
+
import 'yk-grid/dist/yk-grid.css';
|
|
54
|
+
import { DataGrid } from 'yk-grid';
|
|
55
|
+
import type { ColumnDef } from 'yk-grid';
|
|
54
56
|
|
|
55
57
|
interface User {
|
|
56
|
-
id: string
|
|
57
|
-
name: string
|
|
58
|
-
email: string
|
|
59
|
-
age: number
|
|
60
|
-
status: 'active' | 'inactive'
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
email: string;
|
|
61
|
+
age: number;
|
|
62
|
+
status: 'active' | 'inactive';
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
const columns: ColumnDef<User>[] = [
|
|
64
|
-
{ id: 'name',
|
|
65
|
-
{ id: 'email',
|
|
66
|
-
{ id: 'age',
|
|
66
|
+
{ id: 'name', header: 'Name', accessor: r => r.name, sortable: true, filterable: true },
|
|
67
|
+
{ id: 'email', header: 'Email', accessor: r => r.email, sortable: true, filterable: true },
|
|
68
|
+
{ id: 'age', header: 'Age', accessor: r => r.age, sortable: true, filterType: 'number', editable: true },
|
|
67
69
|
{ id: 'status', header: 'Status', accessor: r => r.status, sortable: true, filterType: 'select' },
|
|
68
|
-
]
|
|
70
|
+
];
|
|
69
71
|
|
|
70
72
|
export default function App() {
|
|
71
73
|
return (
|
|
@@ -81,7 +83,7 @@ export default function App() {
|
|
|
81
83
|
enableColumnVisibility
|
|
82
84
|
onCellEdit={(value, row, col) => console.log(col.id, row.id, '→', value)}
|
|
83
85
|
/>
|
|
84
|
-
)
|
|
86
|
+
);
|
|
85
87
|
}
|
|
86
88
|
```
|
|
87
89
|
|
|
@@ -91,25 +93,25 @@ export default function App() {
|
|
|
91
93
|
|
|
92
94
|
Every column is defined with a `ColumnDef<T>` object.
|
|
93
95
|
|
|
94
|
-
| Property
|
|
95
|
-
|
|
96
|
-
| `id`
|
|
97
|
-
| `header`
|
|
98
|
-
| `accessor`
|
|
99
|
-
| `cell`
|
|
100
|
-
| `exportValue`
|
|
101
|
-
| `sortable`
|
|
102
|
-
| `filterable`
|
|
103
|
-
| `filterType`
|
|
104
|
-
| `filterOptions` | `string[]`
|
|
105
|
-
| `groupable`
|
|
106
|
-
| `aggregation`
|
|
107
|
-
| `editable`
|
|
108
|
-
| `width`
|
|
109
|
-
| `minWidth`
|
|
110
|
-
| `resizable`
|
|
111
|
-
| `hideable`
|
|
112
|
-
| `defaultHidden` | `boolean`
|
|
96
|
+
| Property | Type | Description |
|
|
97
|
+
| --------------- | ---------------------------------------------- | ------------------------------------------------------------------- |
|
|
98
|
+
| `id` | `string` | Unique column identifier |
|
|
99
|
+
| `header` | `string` | Column header label |
|
|
100
|
+
| `accessor` | `(row: T) => string \| number \| Date \| null` | Extracts the raw cell value from the row |
|
|
101
|
+
| `cell` | `(value, row: T) => ReactNode` | Optional custom cell renderer |
|
|
102
|
+
| `exportValue` | `(row: T) => string \| number` | Override the value used in CSV export |
|
|
103
|
+
| `sortable` | `boolean` | Enable sort on this column |
|
|
104
|
+
| `filterable` | `boolean` | Show the funnel filter button for this column |
|
|
105
|
+
| `filterType` | `'text' \| 'number' \| 'select' \| 'date'` | Filter panel variant. Defaults to `'text'` |
|
|
106
|
+
| `filterOptions` | `string[]` | Static options for `filterType: 'select'` |
|
|
107
|
+
| `groupable` | `boolean` | Allow this column to be used as a grouping key |
|
|
108
|
+
| `aggregation` | `'sum' \| 'avg' \| 'count' \| 'min' \| 'max'` | Aggregation shown in group header rows |
|
|
109
|
+
| `editable` | `boolean` | Double-click to edit the cell inline |
|
|
110
|
+
| `width` | `number` | Default column width in pixels |
|
|
111
|
+
| `minWidth` | `number` | Minimum width when resizing (default: 50) |
|
|
112
|
+
| `resizable` | `boolean` | Override per-column resize (default: inherits `enableColumnResize`) |
|
|
113
|
+
| `hideable` | `boolean` | Whether this column can be hidden in the visibility picker |
|
|
114
|
+
| `defaultHidden` | `boolean` | Start the column hidden |
|
|
113
115
|
|
|
114
116
|
---
|
|
115
117
|
|
|
@@ -117,91 +119,91 @@ Every column is defined with a `ColumnDef<T>` object.
|
|
|
117
119
|
|
|
118
120
|
### Required
|
|
119
121
|
|
|
120
|
-
| Prop
|
|
121
|
-
|
|
122
|
-
| `data`
|
|
123
|
-
| `columns`
|
|
122
|
+
| Prop | Type | Description |
|
|
123
|
+
| ---------- | -------------------- | ------------------------------ |
|
|
124
|
+
| `data` | `T[]` | Row data |
|
|
125
|
+
| `columns` | `ColumnDef<T>[]` | Column definitions |
|
|
124
126
|
| `getRowId` | `(row: T) => string` | Unique row identifier function |
|
|
125
127
|
|
|
126
128
|
### Data & loading
|
|
127
129
|
|
|
128
|
-
| Prop
|
|
129
|
-
|
|
130
|
-
| `dataMode`
|
|
131
|
-
| `pageSize`
|
|
132
|
-
| `rowCount`
|
|
133
|
-
| `loading`
|
|
134
|
-
| `onStateChange` | `(state: GridState) => void` | —
|
|
135
|
-
| `initialState`
|
|
130
|
+
| Prop | Type | Default | Description |
|
|
131
|
+
| --------------- | ---------------------------- | ---------- | ------------------------------------------------------------------------------------------ |
|
|
132
|
+
| `dataMode` | `'client' \| 'server'` | `'server'` | `client` handles sort/filter/page locally; `server` fires `onStateChange` for you to fetch |
|
|
133
|
+
| `pageSize` | `number` | `20` | Initial rows per page |
|
|
134
|
+
| `rowCount` | `number` | — | Total row count for server-side pagination |
|
|
135
|
+
| `loading` | `boolean` | `false` | Shows a loading overlay |
|
|
136
|
+
| `onStateChange` | `(state: GridState) => void` | — | Fires when sorts, filters, grouping, or pagination change |
|
|
137
|
+
| `initialState` | `Partial<GridState>` | — | Seed initial sorts, filters, grouping, pagination, etc. |
|
|
136
138
|
|
|
137
139
|
### Selection
|
|
138
140
|
|
|
139
|
-
| Prop
|
|
140
|
-
|
|
141
|
-
| `selectionMode`
|
|
142
|
-
| `selectAllScope`
|
|
143
|
-
| `onSelectionChange` | `(rows: T[], ids: string[]) => void` | —
|
|
141
|
+
| Prop | Type | Default | Description |
|
|
142
|
+
| ------------------- | ------------------------------------ | -------- | ------------------------------------ |
|
|
143
|
+
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | Row selection behaviour |
|
|
144
|
+
| `selectAllScope` | `'page' \| 'filtered'` | `'page'` | What the select-all checkbox targets |
|
|
145
|
+
| `onSelectionChange` | `(rows: T[], ids: string[]) => void` | — | Fires whenever the selection changes |
|
|
144
146
|
|
|
145
147
|
### Click handlers
|
|
146
148
|
|
|
147
|
-
| Prop
|
|
148
|
-
|
|
149
|
-
| `onRowClick`
|
|
149
|
+
| Prop | Type | Description |
|
|
150
|
+
| ------------- | -------------------------------------------------------------- | ----------------------------------------- |
|
|
151
|
+
| `onRowClick` | `(row: T, index: number, e: MouseEvent) => void` | Called when a data row is clicked |
|
|
150
152
|
| `onCellClick` | `(value, row: T, column: ColumnDef<T>, e: MouseEvent) => void` | Called when an individual cell is clicked |
|
|
151
153
|
|
|
152
154
|
### Column features
|
|
153
155
|
|
|
154
|
-
| Prop
|
|
155
|
-
|
|
156
|
-
| `enableColumnResize`
|
|
156
|
+
| Prop | Type | Default | Description |
|
|
157
|
+
| ------------------------ | --------- | ------- | --------------------------------------- |
|
|
158
|
+
| `enableColumnResize` | `boolean` | `false` | Drag-to-resize column widths |
|
|
157
159
|
| `enableColumnVisibility` | `boolean` | `false` | Show/hide column picker in header menus |
|
|
158
160
|
|
|
159
161
|
### Filtering
|
|
160
162
|
|
|
161
|
-
| Prop
|
|
162
|
-
|
|
163
|
+
| Prop | Type | Description |
|
|
164
|
+
| -------------------- | ----------------------------------------- | ------------------------------------------------------------- |
|
|
163
165
|
| `fetchFilterOptions` | `(columnId: string) => Promise<string[]>` | Server mode: fetch options for `filterType: 'select'` columns |
|
|
164
166
|
|
|
165
167
|
### Virtual scrolling
|
|
166
168
|
|
|
167
|
-
| Prop
|
|
168
|
-
|
|
169
|
-
| `height`
|
|
170
|
-
| `estimatedRowHeight` | `number`
|
|
169
|
+
| Prop | Type | Default | Description |
|
|
170
|
+
| -------------------- | ------------------ | ------- | ----------------------------------------------------------------- |
|
|
171
|
+
| `height` | `number \| string` | — | Fixed height activates virtual scrolling (e.g. `600` or `'80vh'`) |
|
|
172
|
+
| `estimatedRowHeight` | `number` | `41` | Row height hint for the virtualiser; affects scroll accuracy |
|
|
171
173
|
|
|
172
174
|
### Inline editing
|
|
173
175
|
|
|
174
|
-
| Prop
|
|
175
|
-
|
|
176
|
+
| Prop | Type | Description |
|
|
177
|
+
| ------------ | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
176
178
|
| `onCellEdit` | `(newValue: string \| number, row: T, column: ColumnDef<T>) => void` | Called when a cell edit is committed. Set `editable: true` on columns you want editable. |
|
|
177
179
|
|
|
178
180
|
### Export
|
|
179
181
|
|
|
180
|
-
| Prop
|
|
181
|
-
|
|
182
|
-
| `enableCsvExport` | `boolean` | `false`
|
|
183
|
-
| `csvFilename`
|
|
182
|
+
| Prop | Type | Default | Description |
|
|
183
|
+
| ----------------- | --------- | -------------- | --------------------------------- |
|
|
184
|
+
| `enableCsvExport` | `boolean` | `false` | Adds CSV export button to toolbar |
|
|
185
|
+
| `csvFilename` | `string` | `'export.csv'` | Downloaded file name |
|
|
184
186
|
|
|
185
187
|
### Toolbar
|
|
186
188
|
|
|
187
|
-
| Prop
|
|
188
|
-
|
|
189
|
+
| Prop | Type | Description |
|
|
190
|
+
| ---------------- | ----------------------------------- | ------------------------------------- |
|
|
189
191
|
| `toolbarActions` | `(ctx: ToolbarCtx<T>) => ReactNode` | Render prop injected into the toolbar |
|
|
190
192
|
|
|
191
193
|
`ToolbarCtx<T>` contains: `selectedRows`, `selectedIds`, `processedRows`, `gridState`, `clearSelection`.
|
|
192
194
|
|
|
193
195
|
### AI
|
|
194
196
|
|
|
195
|
-
| Prop | Type
|
|
196
|
-
|
|
197
|
+
| Prop | Type | Description |
|
|
198
|
+
| ---- | -------------------------------------------- | ---------------------------------------- |
|
|
197
199
|
| `ai` | `{ endpoint: string; placeholder?: string }` | Enables the natural-language command bar |
|
|
198
200
|
|
|
199
201
|
### Display
|
|
200
202
|
|
|
201
|
-
| Prop
|
|
202
|
-
|
|
203
|
-
| `emptyState` | `ReactNode` | Custom content when there are no rows
|
|
204
|
-
| `className`
|
|
203
|
+
| Prop | Type | Description |
|
|
204
|
+
| ------------ | ----------- | ----------------------------------------- |
|
|
205
|
+
| `emptyState` | `ReactNode` | Custom content when there are no rows |
|
|
206
|
+
| `className` | `string` | Class applied to the root wrapper element |
|
|
205
207
|
|
|
206
208
|
---
|
|
207
209
|
|
|
@@ -218,14 +220,14 @@ const gridRef = useRef<GridRef<User>>(null)
|
|
|
218
220
|
<DataGrid ref={gridRef} ... />
|
|
219
221
|
```
|
|
220
222
|
|
|
221
|
-
| Method
|
|
222
|
-
|
|
223
|
-
| `getSelectedRows()`
|
|
224
|
-
| `getProcessedRows()` | `T[]`
|
|
225
|
-
| `getGridState()`
|
|
226
|
-
| `clearSelection()`
|
|
227
|
-
| `exportCsv(opts?)`
|
|
228
|
-
| `setState(partial)`
|
|
223
|
+
| Method | Returns | Description |
|
|
224
|
+
| -------------------- | ----------- | ---------------------------------------------------------------- |
|
|
225
|
+
| `getSelectedRows()` | `T[]` | Currently selected row objects |
|
|
226
|
+
| `getProcessedRows()` | `T[]` | All rows after filtering (ignores pagination) |
|
|
227
|
+
| `getGridState()` | `GridState` | Full current grid state snapshot |
|
|
228
|
+
| `clearSelection()` | `void` | Clear all selected rows |
|
|
229
|
+
| `exportCsv(opts?)` | `void` | Trigger CSV download. `opts.selectedOnly` exports selection only |
|
|
230
|
+
| `setState(partial)` | `void` | Programmatically set sorts, filters, or grouping |
|
|
229
231
|
|
|
230
232
|
---
|
|
231
233
|
|
|
@@ -233,20 +235,20 @@ const gridRef = useRef<GridRef<User>>(null)
|
|
|
233
235
|
|
|
234
236
|
All visual properties are controlled via CSS custom properties on `:root` (or any ancestor element):
|
|
235
237
|
|
|
236
|
-
| Variable
|
|
237
|
-
|
|
238
|
-
| `--grid-font-size`
|
|
239
|
-
| `--grid-border-colour`
|
|
240
|
-
| `--grid-header-bg`
|
|
241
|
-
| `--grid-row-hover-bg`
|
|
242
|
-
| `--grid-selected-bg`
|
|
243
|
-
| `--grid-accent`
|
|
244
|
-
| `--grid-focus-ring`
|
|
245
|
-
| `--grid-radius`
|
|
246
|
-
| `--grid-cell-padding`
|
|
247
|
-
| `--grid-cell-padding-y` | `0.625rem`
|
|
248
|
-
| `--grid-cell-padding-x` | `0.875rem`
|
|
249
|
-
| `--grid-toolbar-gap`
|
|
238
|
+
| Variable | Default | Description |
|
|
239
|
+
| ----------------------- | ------------------- | ------------------------------------------------------------ |
|
|
240
|
+
| `--grid-font-size` | `0.875rem` | Base font size |
|
|
241
|
+
| `--grid-border-colour` | `#e2e8f0` | Cell/row border colour |
|
|
242
|
+
| `--grid-header-bg` | `#f1f5f9` | Header and toolbar background |
|
|
243
|
+
| `--grid-row-hover-bg` | `#f8fafc` | Row hover background |
|
|
244
|
+
| `--grid-selected-bg` | `#eef2ff` | Selected row background |
|
|
245
|
+
| `--grid-accent` | `#6366f1` | Accent colour (focus rings, active filters, sort indicators) |
|
|
246
|
+
| `--grid-focus-ring` | `0 0 0 2px #6366f1` | Focus ring box-shadow |
|
|
247
|
+
| `--grid-radius` | `0.5rem` | Border radius of the outer wrapper |
|
|
248
|
+
| `--grid-cell-padding` | `0.625rem 0.875rem` | Cell padding (shorthand) |
|
|
249
|
+
| `--grid-cell-padding-y` | `0.625rem` | Vertical cell padding |
|
|
250
|
+
| `--grid-cell-padding-x` | `0.875rem` | Horizontal cell padding |
|
|
251
|
+
| `--grid-toolbar-gap` | `0.5rem` | Toolbar item gap |
|
|
250
252
|
|
|
251
253
|
Example — dark theme:
|
|
252
254
|
|
|
@@ -267,20 +269,20 @@ Example — dark theme:
|
|
|
267
269
|
In `dataMode="server"`, the grid fires `onStateChange` whenever the user sorts, filters, groups, or paginates. Use this to fetch from your API:
|
|
268
270
|
|
|
269
271
|
```tsx
|
|
270
|
-
const [data, setData] = useState<User[]>([])
|
|
271
|
-
const [rowCount, setRowCount] = useState(0)
|
|
272
|
-
const [loading, setLoading] = useState(false)
|
|
272
|
+
const [data, setData] = useState<User[]>([]);
|
|
273
|
+
const [rowCount, setRowCount] = useState(0);
|
|
274
|
+
const [loading, setLoading] = useState(false);
|
|
273
275
|
|
|
274
276
|
async function fetchData(state: GridState) {
|
|
275
|
-
setLoading(true)
|
|
277
|
+
setLoading(true);
|
|
276
278
|
const res = await fetch('/api/users', {
|
|
277
279
|
method: 'POST',
|
|
278
280
|
body: JSON.stringify(state),
|
|
279
|
-
})
|
|
280
|
-
const json = await res.json()
|
|
281
|
-
setData(json.rows)
|
|
282
|
-
setRowCount(json.total)
|
|
283
|
-
setLoading(false)
|
|
281
|
+
});
|
|
282
|
+
const json = await res.json();
|
|
283
|
+
setData(json.rows);
|
|
284
|
+
setRowCount(json.total);
|
|
285
|
+
setLoading(false);
|
|
284
286
|
}
|
|
285
287
|
|
|
286
288
|
<DataGrid<User>
|
|
@@ -291,7 +293,7 @@ async function fetchData(state: GridState) {
|
|
|
291
293
|
rowCount={rowCount}
|
|
292
294
|
loading={loading}
|
|
293
295
|
onStateChange={fetchData}
|
|
294
|
-
|
|
296
|
+
/>;
|
|
295
297
|
```
|
|
296
298
|
|
|
297
299
|
---
|
|
@@ -314,20 +316,20 @@ Wire up the server handler (Express or Next.js App Router):
|
|
|
314
316
|
|
|
315
317
|
```ts
|
|
316
318
|
// Express
|
|
317
|
-
import { handleGridAiRequest } from 'yk-grid/server'
|
|
319
|
+
import { handleGridAiRequest } from 'yk-grid/server';
|
|
318
320
|
|
|
319
321
|
app.post('/api/grid-ai', async (req, res) => {
|
|
320
|
-
const result = await handleGridAiRequest(req.body)
|
|
321
|
-
res.json(result)
|
|
322
|
-
})
|
|
322
|
+
const result = await handleGridAiRequest(req.body);
|
|
323
|
+
res.json(result);
|
|
324
|
+
});
|
|
323
325
|
|
|
324
326
|
// Next.js App Router
|
|
325
|
-
import { handleGridAiRequest } from 'yk-grid/server'
|
|
327
|
+
import { handleGridAiRequest } from 'yk-grid/server';
|
|
326
328
|
|
|
327
329
|
export async function POST(req: Request) {
|
|
328
|
-
const body = await req.json()
|
|
329
|
-
const result = await handleGridAiRequest(body)
|
|
330
|
-
return Response.json(result)
|
|
330
|
+
const body = await req.json();
|
|
331
|
+
const result = await handleGridAiRequest(body);
|
|
332
|
+
return Response.json(result);
|
|
331
333
|
}
|
|
332
334
|
```
|
|
333
335
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yk-grid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Production-ready AI-assisted React DataGrid component with sorting, filtering, grouping, pagination, virtual scrolling, and optional natural-language query input",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -62,7 +62,10 @@
|
|
|
62
62
|
"test": "vitest run",
|
|
63
63
|
"test:watch": "vitest",
|
|
64
64
|
"test:e2e": "playwright test",
|
|
65
|
-
"lint": "tsc --noEmit"
|
|
65
|
+
"lint": "tsc --noEmit",
|
|
66
|
+
"docs:dev": "vitepress dev docs",
|
|
67
|
+
"docs:build": "vitepress build docs",
|
|
68
|
+
"docs:preview": "vitepress preview docs"
|
|
66
69
|
},
|
|
67
70
|
"peerDependencies": {
|
|
68
71
|
"@tanstack/react-virtual": ">=3",
|
|
@@ -85,6 +88,7 @@
|
|
|
85
88
|
"react-dom": "^19.2.7",
|
|
86
89
|
"typescript": "^6.0.3",
|
|
87
90
|
"vite": "^8.0.16",
|
|
91
|
+
"vitepress": "^1.6.4",
|
|
88
92
|
"vitest": "^3.2.6",
|
|
89
93
|
"zod": "^4.4.3"
|
|
90
94
|
}
|