react-live-data-table 1.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/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';