react-live-data-table 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "react-live-data-table",
3
+ "version": "1.0.1",
4
+ "description": "Your React component package with Tailwind",
5
+ "main": "src/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "files": [
8
+ "src"
9
+ ],
10
+ "type": "module",
11
+ "scripts": {
12
+ "build": "rollup -c --bundleConfigAsCjs",
13
+ "prepare": "npm run build"
14
+ },
15
+ "peerDependencies": {
16
+ "react": "^18.0.0",
17
+ "react-dom": "^18.0.0",
18
+ "tailwindcss": ">=3.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "@babel/core": "^7.26.7",
22
+ "@babel/preset-env": "^7.26.7",
23
+ "@babel/preset-react": "^7.26.3",
24
+ "@rollup/plugin-babel": "^6.0.4",
25
+ "@rollup/plugin-commonjs": "^25.0.8",
26
+ "@rollup/plugin-node-resolve": "^15.3.1",
27
+ "@rollup/plugin-terser": "^0.4.4",
28
+ "autoprefixer": "^10.4.20",
29
+ "babel-loader": "^9.2.1",
30
+ "postcss": "^8.5.1",
31
+ "rollup": "^3.29.5",
32
+ "rollup-plugin-peer-deps-external": "^2.2.4",
33
+ "rollup-plugin-postcss": "^4.0.2",
34
+ "tailwindcss": "^3.4.17"
35
+ },
36
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
37
+ }
package/readme.md ADDED
@@ -0,0 +1,80 @@
1
+ # React Data Grid Table
2
+
3
+ A highly customizable and efficient data grid table for React. It supports features like **live pagination**, **checkbox selection**, **dynamic data loading**, **scrollable body**, and more. This component is designed to make handling tabular data easier and more interactive.
4
+
5
+ ---
6
+
7
+ ## 🚀 Installation
8
+
9
+ To install the package, run one of the following commands:
10
+
11
+ ```bash
12
+ npm install react-data-grid-table
13
+
14
+ # React Data Grid
15
+
16
+ A flexible React data grid component with support for pagination, row selection, and dynamic data handling.
17
+
18
+ ## Props
19
+
20
+ ### Required Props
21
+
22
+ | Prop | Type | Description |
23
+ |------|------|-------------|
24
+ | `dataSource` | `Array \| Function` | The data to display in the grid. Can be an array for static data or a function for server-side pagination |
25
+ | `columns` | `Array` | Defines the columns configuration |
26
+
27
+ ### Optional Props
28
+
29
+ | Prop | Type | Default | Description |
30
+ |------|------|---------|-------------|
31
+ | `loading` | `boolean` | `false` | Shows loading state |
32
+ | `maxHeight` | `string` | `"600px"` | Maximum height of the grid |
33
+ | `height` | `string` | `"600px"` | Fixed height of the grid |
34
+ | `showCheckbox` | `boolean` | `false` | Enable row selection |
35
+ | `onSelectionChange` | `Function` | - | Callback when row selection changes |
36
+ | `defaultLimit` | `number` | `50` | Rows per page |
37
+ | `onRowClick` | `Function` | - | Callback when row is clicked |
38
+ | `livePagination` | `boolean` | `false` | Enable server-side pagination |
39
+ | `staticData` | `Array` | `null` | Static data array |
40
+ | `emptyText` | `string` | - | Text shown when no data |
41
+ | `rowHeights` | `number` | `40` | Height of each row |
42
+ | `headerProps` | `object` | `{}` | Custom header properties |
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
+ ## 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
+ };
@@ -0,0 +1,281 @@
1
+ import React, { useEffect } from 'react';
2
+ import "./index.css";
3
+
4
+ function ReactDataTable({
5
+ dataSource,
6
+ columns,
7
+ loading = false,
8
+ maxHeight = "600px",
9
+ height = "600px",
10
+ showCheckbox = false,
11
+ onSelectionChange,
12
+ defaultLimit = 50,
13
+ onRowClick,
14
+ livePagination = false,
15
+ staticData = null,
16
+ emptyText,
17
+ rowHeights = 40,
18
+ headerProps = {} // Added default value
19
+ }) {
20
+ const tableContainerRef = React.useRef(null);
21
+ const [data, setData] = React.useState({ pages: [], meta: { totalPages: 1 } });
22
+ const [isFetching, setIsFetching] = React.useState(false);
23
+ const [pageParam, setPageParam] = React.useState(1);
24
+ const [selectedRows, setSelectedRows] = React.useState({});
25
+
26
+
27
+ useEffect(() => {
28
+ setData({ pages: [], meta: { totalPages: 1 } });
29
+ setPageParam(1);
30
+
31
+ if (staticData) {
32
+ setData({
33
+ pages: [{
34
+ data: staticData,
35
+ meta: {
36
+ totalPages: 1,
37
+ totalCount: staticData.length
38
+ }
39
+ }],
40
+ meta: {
41
+ totalPages: 1,
42
+ totalCount: staticData.length
43
+ }
44
+ });
45
+ } else {
46
+ loadInitialData();
47
+ }
48
+ }, [dataSource, staticData]);
49
+
50
+ const loadInitialData = async () => {
51
+ if (!dataSource) return;
52
+
53
+ try {
54
+ const initialData = await dataSource({ skip: 0, limit: defaultLimit });
55
+ setData({
56
+ pages: [initialData],
57
+ meta: initialData.meta
58
+ });
59
+ setPageParam(1);
60
+ } catch (error) {
61
+ console.error('Error loading initial data:', error);
62
+ }
63
+ };
64
+
65
+ const fetchNextPage = async () => {
66
+ if (isFetching || !livePagination || staticData) return;
67
+
68
+ setIsFetching(true);
69
+ try {
70
+ const nextData = await dataSource({
71
+ skip: pageParam * defaultLimit,
72
+ limit: defaultLimit
73
+ });
74
+ setData(prev => ({
75
+ pages: [...prev.pages, nextData],
76
+ meta: nextData.meta
77
+ }));
78
+ setPageParam(prev => prev + 1);
79
+ } catch (error) {
80
+ console.error('Error fetching next page:', error);
81
+ } finally {
82
+ setIsFetching(false);
83
+ }
84
+ };
85
+
86
+ const handleSelectAll = (checked, flatData) => {
87
+ if (checked) {
88
+ const newSelected = {};
89
+ flatData.forEach(row => {
90
+ newSelected[row.id] = { ...row };
91
+ });
92
+ setSelectedRows(newSelected);
93
+ onSelectionChange?.({
94
+ data: flatData,
95
+ selected: true,
96
+ originalData: flatData,
97
+ unselected: null
98
+ });
99
+ } else {
100
+ setSelectedRows({});
101
+ onSelectionChange?.({
102
+ data: [],
103
+ selected: {},
104
+ originalData: data.pages.data
105
+ });
106
+ }
107
+ };
108
+
109
+ const handleSelectRow = (checked, row, flatData) => {
110
+ const rowId = row.id;
111
+ if (checked) {
112
+ setSelectedRows(prev => ({
113
+ ...prev,
114
+ [rowId]: { ...row }
115
+ }));
116
+ onSelectionChange?.({
117
+ data: { ...row },
118
+ selected: { ...selectedRows, [rowId]: { ...row } },
119
+ [rowId]: { ...row },
120
+ originalData: [...flatData]
121
+ });
122
+ } else {
123
+ const updatedSelectedRows = { ...selectedRows };
124
+ delete updatedSelectedRows[rowId];
125
+
126
+ setSelectedRows(updatedSelectedRows);
127
+ onSelectionChange?.({
128
+ data: { ...row },
129
+ selected: { ...updatedSelectedRows },
130
+ originalData: [...flatData]
131
+ });
132
+ }
133
+ };
134
+
135
+ const handleRowClick = (row, index, flatData) => {
136
+ onRowClick?.({
137
+ data: { ...row },
138
+ dataSourceArray: flatData,
139
+ rowIndex: index
140
+ });
141
+ };
142
+
143
+ const handleScroll = (containerRefElement) => {
144
+ if (containerRefElement) {
145
+ const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
146
+ if (
147
+ scrollHeight - scrollTop - clientHeight < 500 &&
148
+ !isFetching &&
149
+ pageParam < data.meta.totalPages
150
+ ) {
151
+ fetchNextPage();
152
+ }
153
+ }
154
+ };
155
+
156
+ useEffect(() => {
157
+ handleScroll(tableContainerRef.current);
158
+ }, [data]);
159
+
160
+ const flatData = data.pages.flatMap(page => page.data);
161
+
162
+ const checkboxColumn = {
163
+ id: 'select',
164
+ size: 50,
165
+ minWidth: 50,
166
+ textAlign: "center",
167
+ header: ({ data }) => (
168
+ <div className="flex items-center justify-center h-[40px]">
169
+ <input
170
+ type="checkbox"
171
+ checked={Object.keys(selectedRows).length > 0 && data.every(row => selectedRows[row.id])}
172
+ onChange={(e) => handleSelectAll(e.target.checked, flatData)}
173
+ />
174
+ </div>
175
+ ),
176
+ cell: ({ row }) => (
177
+ <div className="flex items-center justify-center h-[40px]">
178
+ <input
179
+ type="checkbox"
180
+ checked={!!selectedRows[row.id]}
181
+ onChange={(e) => handleSelectRow(e.target.checked, row, flatData)}
182
+ />
183
+ </div>
184
+ ),
185
+ };
186
+
187
+ const enhancedColumns = showCheckbox ? [checkboxColumn, ...columns] : columns;
188
+
189
+ return (
190
+ <div className="bg-white relative w-full">
191
+ {loading && <div className="absolute inset-0 bg-white/50 z-20 flex items-center justify-center">
192
+ <div role="status" className="p-2">
193
+ <svg
194
+ aria-hidden="true"
195
+ className="inline w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-green-500"
196
+ viewBox="0 0 100 101"
197
+ fill="none"
198
+ xmlns="http://www.w3.org/2000/svg"
199
+ >
200
+ <path
201
+ d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908Z"
202
+ fill="#555e68"
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)}
220
+ >
221
+ <table className="w-full border-collapse">
222
+ <thead
223
+ className="sticky top-0 z-1 bg-blue-300"
224
+ style={{ ...headerProps.style }}
225
+ >
226
+ <tr>
227
+ {enhancedColumns.map(column => (
228
+ <th
229
+ key={column.accessorKey || column.id}
230
+ className="text-left font-normal h-[40px]"
231
+ style={{
232
+ width: column.size,
233
+ minWidth: column.minWidth,
234
+ textAlign: column.textAlign,
235
+ }}
236
+ >
237
+ {typeof column.header === 'function' ? column.header({ data: flatData }) : column.header}
238
+ </th>
239
+ ))}
240
+ </tr>
241
+ </thead>
242
+ <tbody>
243
+ {flatData.length > 0 ? (
244
+ flatData.map((row, index) => (
245
+ <tr
246
+ key={row.id}
247
+ className={`border-t border-x border-gray-200 hover:bg-[#dee1f2] h-[${rowHeights}px] ${selectedRows[row.id] ? 'bg-[#dee1f2]' : ''}`}
248
+ onClick={() => handleRowClick(row, index, flatData)}
249
+ >
250
+ {enhancedColumns.map(column => (
251
+ <td
252
+ key={column.accessorKey || column.id}
253
+ className={`text-left font-normal border-r ${column?.cellProps?.className || ''}`}
254
+ style={{
255
+ minWidth: `${column.minWidth}px`,
256
+ textAlign: column?.textAlign,
257
+ ...column?.cellProps?.style,
258
+ }}
259
+ >
260
+ {typeof column.cell === 'function' ? column.cell({ row }) : null}
261
+ </td>
262
+ ))}
263
+ </tr>
264
+ ))
265
+ ) : (
266
+ <tr>
267
+ <td colSpan={enhancedColumns.length} className="text-center py-4">
268
+ {emptyText || 'No data available'}
269
+ </td>
270
+ </tr>
271
+ )}
272
+ </tbody>
273
+ </table>
274
+ </div>
275
+ </div>
276
+ )}
277
+ </div>
278
+ );
279
+ }
280
+
281
+ export default ReactDataTable;
package/src/index.css ADDED
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ export { default as ReactDataTable } from './ReactDataTable.jsx';