react-live-data-table 1.0.15 → 1.0.16
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/package.json +1 -1
- package/readme.md +1 -37
- package/src/ReactDataTable.jsx +189 -102
package/package.json
CHANGED
package/readme.md
CHANGED
@@ -8,7 +8,7 @@ A highly customizable and efficient data grid table for React. It supports featu
|
|
8
8
|
|
9
9
|
To install the package, run one of the following commands:
|
10
10
|
|
11
|
-
|
11
|
+
bash
|
12
12
|
npm install react-data-grid-table
|
13
13
|
|
14
14
|
# React Data Grid
|
@@ -41,40 +41,4 @@ A flexible React data grid component with support for pagination, row selection,
|
|
41
41
|
| `rowHeights` | `number` | `40` | Height of each row |
|
42
42
|
| `headerProps` | `object` | `{}` | Custom header properties |
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
### Column Definition
|
48
|
-
|
49
|
-
```typescript
|
50
|
-
interface ColumnDefinition {
|
51
|
-
field: string; // Key of the data field to display
|
52
|
-
headerName: string; // Text to show in column header
|
53
|
-
width?: number; // Column width in pixels
|
54
|
-
sortable?: boolean; // Enable column sorting
|
55
|
-
render?: (value: any, rowData: Object) => React.ReactNode; // Custom cell renderer
|
56
|
-
|
57
|
-
|
58
44
|
## Basic Usage
|
59
|
-
|
60
|
-
```jsx
|
61
|
-
import { ReactDataTable } from 'react-data-grid';
|
62
|
-
|
63
|
-
const App = () => {
|
64
|
-
const columns = [
|
65
|
-
{ field: 'id', headerName: 'ID' },
|
66
|
-
{ field: 'name', headerName: 'Name' }
|
67
|
-
];
|
68
|
-
|
69
|
-
const data = [
|
70
|
-
{ id: 1, name: 'John' },
|
71
|
-
{ id: 2, name: 'Jane' }
|
72
|
-
];
|
73
|
-
|
74
|
-
return (
|
75
|
-
<ReactDataTable
|
76
|
-
columns={columns}
|
77
|
-
dataSource={data}
|
78
|
-
/>
|
79
|
-
);
|
80
|
-
};
|
package/src/ReactDataTable.jsx
CHANGED
@@ -15,13 +15,26 @@ function ReactDataTable({
|
|
15
15
|
staticData = null,
|
16
16
|
emptyText,
|
17
17
|
rowHeights = 40,
|
18
|
-
headerProps = {}
|
18
|
+
headerProps = {},
|
19
|
+
selected = {},
|
20
|
+
showSelectAllCheckbox = true,
|
21
|
+
rowStyle = {},
|
22
|
+
rowClassName = ""
|
19
23
|
}) {
|
20
24
|
const tableContainerRef = React.useRef(null);
|
21
25
|
const [data, setData] = React.useState({ pages: [], meta: { totalPages: 1 } });
|
22
26
|
const [isFetching, setIsFetching] = React.useState(false);
|
23
27
|
const [pageParam, setPageParam] = React.useState(1);
|
24
|
-
const [selectedRows, setSelectedRows] = React.useState(
|
28
|
+
const [selectedRows, setSelectedRows] = React.useState(selected);
|
29
|
+
const previousSelected = React.useRef(selected);
|
30
|
+
|
31
|
+
|
32
|
+
useEffect(() => {
|
33
|
+
if (JSON.stringify(previousSelected.current) !== JSON.stringify(selected)) {
|
34
|
+
setSelectedRows({...selected});
|
35
|
+
previousSelected.current = selected;
|
36
|
+
}
|
37
|
+
}, [selected]);
|
25
38
|
|
26
39
|
|
27
40
|
useEffect(() => {
|
@@ -164,117 +177,191 @@ function ReactDataTable({
|
|
164
177
|
size: 50,
|
165
178
|
minWidth: 50,
|
166
179
|
textAlign: "center",
|
167
|
-
header: ({ data }) =>
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
180
|
+
header: ({ data }) => {
|
181
|
+
const allSelected = flatData.length > 0 && flatData.every(row => selectedRows[row.id]);
|
182
|
+
const someSelected = flatData.some(row => selectedRows[row.id]) && !allSelected;
|
183
|
+
|
184
|
+
|
185
|
+
return (
|
186
|
+
<div className="flex items-center justify-center h-[40px]">
|
187
|
+
{showSelectAllCheckbox && (
|
188
|
+
<div className="relative">
|
189
|
+
<input
|
190
|
+
id={data.id}
|
191
|
+
type="checkbox"
|
192
|
+
className='bg-gray-700 rounded-4 border-gray-200 text-blue-400 focus:ring-0 focus:ring-white'
|
193
|
+
checked={allSelected}
|
194
|
+
onChange={(e) => handleSelectAll(e.target.checked, flatData)}
|
195
|
+
/>
|
196
|
+
{allSelected ? (
|
197
|
+
<svg
|
198
|
+
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-3 h-3 pointer-events-none text-white"
|
199
|
+
viewBox="0 0 20 20"
|
200
|
+
fill="currentColor"
|
201
|
+
>
|
202
|
+
<path
|
203
|
+
fillRule="evenodd"
|
204
|
+
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
205
|
+
clipRule="evenodd"
|
206
|
+
/>
|
207
|
+
</svg>
|
208
|
+
) : (
|
209
|
+
someSelected &&
|
210
|
+
<svg
|
211
|
+
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-3 h-3 pointer-events-none "
|
212
|
+
viewBox="0 0 20 20"
|
213
|
+
fill="currentColor"
|
214
|
+
>
|
215
|
+
<path
|
216
|
+
fillRule="evenodd"
|
217
|
+
d="M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
218
|
+
clipRule="evenodd"
|
219
|
+
/>
|
220
|
+
</svg>
|
221
|
+
)}
|
222
|
+
</div>
|
223
|
+
)}
|
224
|
+
</div>
|
225
|
+
);
|
226
|
+
},
|
227
|
+
cell: ({ row }) => {
|
228
|
+
return (
|
229
|
+
<div className="flex items-center justify-center h-[40px]" onClick={(e) => e.stopPropagation()}>
|
230
|
+
<input
|
231
|
+
id={row.id}
|
232
|
+
type="checkbox"
|
233
|
+
className='bg-gray-700 rounded-4 border-gray-200 text-blue-400 focus:ring-0 focus:ring-white'
|
234
|
+
checked={!!selectedRows[row.id]}
|
235
|
+
onClick={(e) => e.stopPropagation()}
|
236
|
+
onChange={(e) => { e.stopPropagation(); handleSelectRow(e.target.checked, row, flatData) }}
|
237
|
+
/>
|
238
|
+
</div>
|
239
|
+
)
|
240
|
+
}
|
185
241
|
};
|
186
242
|
|
187
243
|
const enhancedColumns = showCheckbox ? [checkboxColumn, ...columns] : columns;
|
188
244
|
|
189
245
|
return (
|
190
|
-
<div className="bg-white relative w-full">
|
191
|
-
{loading &&
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
fill="currentColor"
|
203
|
-
/>
|
204
|
-
</svg>
|
205
|
-
</div>
|
206
|
-
</div>}
|
207
|
-
{flatData.length === 0 && !loading ? (
|
208
|
-
<div className="flex items-center justify-center" style={{ height }}>
|
209
|
-
<div className="text-gray-500">
|
210
|
-
{emptyText || 'No data available'}
|
211
|
-
</div>
|
212
|
-
</div>
|
213
|
-
) : (
|
214
|
-
<div className="overflow-hidden">
|
215
|
-
<div
|
216
|
-
ref={tableContainerRef}
|
217
|
-
className="overflow-auto w-full"
|
218
|
-
style={{ maxHeight, height }}
|
219
|
-
onScroll={(e) => handleScroll(e.currentTarget)}
|
246
|
+
<div className="bg-white relative w-full react-live-data-table" >
|
247
|
+
{loading && (
|
248
|
+
<div className="absolute inset-0 bg-white/50 z-20 flex items-center justify-center">
|
249
|
+
<svg
|
250
|
+
style={{
|
251
|
+
animation: 'spin 1s linear infinite',
|
252
|
+
width: '24px',
|
253
|
+
height: '24px'
|
254
|
+
}}
|
255
|
+
viewBox="0 0 24 24"
|
256
|
+
fill="none"
|
257
|
+
xmlns="http://www.w3.org/2000/svg"
|
220
258
|
>
|
221
|
-
<
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
259
|
+
<style>
|
260
|
+
{`@keyframes spin {from {transform: rotate(0deg)} to {transform: rotate(360deg)}}`}
|
261
|
+
</style>
|
262
|
+
<circle
|
263
|
+
style={{ opacity: 0.25 }}
|
264
|
+
cx="12"
|
265
|
+
cy="12"
|
266
|
+
r="10"
|
267
|
+
stroke="currentColor"
|
268
|
+
strokeWidth="4"
|
269
|
+
/>
|
270
|
+
<path
|
271
|
+
style={{ opacity: 0.75 }}
|
272
|
+
fill="currentColor"
|
273
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
274
|
+
/>
|
275
|
+
</svg>
|
276
|
+
|
277
|
+
</div>
|
278
|
+
)}
|
279
|
+
|
280
|
+
{
|
281
|
+
flatData.length === 0 && !loading ? (
|
282
|
+
<div className="flex items-center justify-center" style={{ height }}>
|
283
|
+
<div className="text-gray-500">
|
284
|
+
{emptyText || 'No data available'}
|
285
|
+
</div>
|
286
|
+
</div>
|
287
|
+
) : (
|
288
|
+
<div className="overflow-hidden">
|
289
|
+
<div
|
290
|
+
ref={tableContainerRef}
|
291
|
+
className="overflow-auto w-full"
|
292
|
+
style={{ maxHeight, height }}
|
293
|
+
onScroll={(e) => handleScroll(e.currentTarget)}
|
294
|
+
>
|
295
|
+
<table className="w-full border-collapse">
|
296
|
+
<thead
|
297
|
+
className="sticky top-0 z-1 bg-blue-300"
|
298
|
+
style={{ ...headerProps.style }}
|
299
|
+
>
|
300
|
+
<tr>
|
301
|
+
{enhancedColumns.map((column, columnIndex) => (
|
302
|
+
<th
|
303
|
+
key={column.accessorKey || column.id}
|
304
|
+
className={`text-left font-normal h-[40px] border-b border-t border-solid border-[#e4e3e2] ${
|
305
|
+
columnIndex < enhancedColumns.length - 1 ? 'border-r' : ''
|
306
|
+
}`}
|
307
|
+
style={{
|
308
|
+
width: column.size,
|
309
|
+
minWidth: column.minWidth,
|
310
|
+
textAlign: column.textAlign,
|
311
|
+
}}
|
312
|
+
>
|
313
|
+
{typeof column.header === 'function' ? column.header({ data: flatData }) : column.header}
|
314
|
+
</th>
|
315
|
+
))}
|
316
|
+
</tr>
|
317
|
+
</thead>
|
318
|
+
<tbody>
|
319
|
+
{flatData.length > 0 ? (
|
320
|
+
flatData.map((row, index) => {
|
321
|
+
const isLastRow = index === flatData.length - 1;
|
322
|
+
return (
|
323
|
+
<tr
|
324
|
+
key={row.id}
|
325
|
+
className={`border-t ${isLastRow ? 'border-b' : ''} border-gray-200 hover:bg-[#dee1f2] ${selectedRows[row.id] ? 'bg-[#dee1f2]' : ''} ${rowClassName}`}
|
254
326
|
style={{
|
255
|
-
|
256
|
-
|
257
|
-
...
|
327
|
+
height: `${rowHeights}px`,
|
328
|
+
...rowStyle,
|
329
|
+
...(typeof rowStyle === 'function' ? rowStyle(row, index) : {})
|
258
330
|
}}
|
331
|
+
onClick={() => handleRowClick(row, index, flatData)}
|
259
332
|
>
|
260
|
-
{
|
261
|
-
|
262
|
-
|
333
|
+
{enhancedColumns.map((column, cellIndex) => (
|
334
|
+
<td
|
335
|
+
key={column.accessorKey || column.id}
|
336
|
+
className={`text-left font-normal ${
|
337
|
+
cellIndex < enhancedColumns.length-1 ? 'border-r' : ''
|
338
|
+
} ${column?.cellProps?.className || ''}`}
|
339
|
+
style={{
|
340
|
+
minWidth: `${column.minWidth}px`,
|
341
|
+
textAlign: column?.textAlign,
|
342
|
+
...column?.cellProps?.style,
|
343
|
+
}}
|
344
|
+
>
|
345
|
+
{typeof column.cell === 'function' ? column.cell({ row }) : null}
|
346
|
+
</td>
|
347
|
+
))}
|
348
|
+
</tr>
|
349
|
+
);
|
350
|
+
})
|
351
|
+
) : (
|
352
|
+
<tr>
|
353
|
+
<td colSpan={enhancedColumns.length} className="text-center py-4">
|
354
|
+
{emptyText || 'No data available'}
|
355
|
+
</td>
|
263
356
|
</tr>
|
264
|
-
)
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
{emptyText || 'No data available'}
|
269
|
-
</td>
|
270
|
-
</tr>
|
271
|
-
)}
|
272
|
-
</tbody>
|
273
|
-
</table>
|
357
|
+
)}
|
358
|
+
</tbody>
|
359
|
+
</table>
|
360
|
+
</div>
|
274
361
|
</div>
|
275
|
-
|
276
|
-
|
277
|
-
</div>
|
362
|
+
)
|
363
|
+
}
|
364
|
+
</div >
|
278
365
|
);
|
279
366
|
}
|
280
367
|
|