jattac.libs.web.responsive-table 0.1.4 → 0.1.6
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 +275 -275
- package/dist/index.js +18 -8
- package/dist/index.js.map +1 -1
- package/gemini/project_analysis.md +41 -41
- package/gemini/release_git_steps.md +64 -0
- package/package.json +58 -58
- package/src/Data/IFooterColumnDefinition.ts +21 -21
- package/src/Styles/ResponsiveTable.module.css +12 -5
- package/src/UI/ResponsiveTable.tsx +381 -375
package/README.md
CHANGED
|
@@ -1,275 +1,275 @@
|
|
|
1
|
-
# ResponsiveTable: A Modern and Flexible React Table Component
|
|
2
|
-
|
|
3
|
-
ResponsiveTable is a powerful, lightweight, and fully responsive React component for creating beautiful and functional tables. It’s designed to look great on any device, adapting from a traditional table layout on desktops to a clean, card-based view on mobile screens.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Mobile-First Design**: Automatically switches to a card layout on smaller screens for optimal readability.
|
|
8
|
-
- **Highly Customizable**: Tailor the look and feel of columns, headers, and footers.
|
|
9
|
-
- **Dynamic Data Handling**: Define columns and footers based on your data or application state.
|
|
10
|
-
- **Delightful Animations**: Includes an optional skeleton loader and staggered row entrance animations.
|
|
11
|
-
- **Interactive Elements**: Easily add click handlers for rows, headers, and footer cells.
|
|
12
|
-
- **Performant**: Built with performance in mind, including debounced resize handling.
|
|
13
|
-
- **Easy to Use**: A simple and intuitive API for quick integration.
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
To get started, install the package from npm:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install jattac.libs.web.responsive-table
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Getting Started
|
|
24
|
-
|
|
25
|
-
Here’s a basic example to get you up and running in minutes.
|
|
26
|
-
|
|
27
|
-
```jsx
|
|
28
|
-
import React from 'react';
|
|
29
|
-
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
30
|
-
|
|
31
|
-
const GettingStarted = () => {
|
|
32
|
-
const columns = [
|
|
33
|
-
{ displayLabel: 'Name', dataKey: 'name', cellRenderer: (row) => row.name },
|
|
34
|
-
{ displayLabel: 'Age', dataKey: 'age', cellRenderer: (row) => row.age },
|
|
35
|
-
{ displayLabel: 'City', dataKey: 'city', cellRenderer: (row) => row.city },
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
const data = [
|
|
39
|
-
{ name: 'Alice', age: 32, city: 'New York' },
|
|
40
|
-
{ name: 'Bob', age: 28, city: 'Los Angeles' },
|
|
41
|
-
{ name: 'Charlie', age: 45, city: 'Chicago' },
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
return <ResponsiveTable columnDefinitions={columns} data={data} />;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export default GettingStarted;
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
This will render a table that automatically adapts to the screen size. On a desktop, it will look like a standard table, and on mobile, it will switch to a card-based layout.
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## Comprehensive Examples
|
|
55
|
-
|
|
56
|
-
### Example 1: Loading State and Animations
|
|
57
|
-
|
|
58
|
-
You can provide a seamless user experience by showing a skeleton loader while your data is being fetched, and then animating the rows in when the data is ready.
|
|
59
|
-
|
|
60
|
-
```jsx
|
|
61
|
-
import React, { useState, useEffect } from 'react';
|
|
62
|
-
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
63
|
-
|
|
64
|
-
const AnimatedTable = () => {
|
|
65
|
-
const [data, setData] = useState([]);
|
|
66
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
67
|
-
|
|
68
|
-
const columns = [
|
|
69
|
-
{ displayLabel: 'User', cellRenderer: (row) => row.name },
|
|
70
|
-
{ displayLabel: 'Email', cellRenderer: (row) => row.email },
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
// Simulate a network request
|
|
75
|
-
setTimeout(() => {
|
|
76
|
-
setData([
|
|
77
|
-
{ name: 'Grace', email: 'grace@example.com' },
|
|
78
|
-
{ name: 'Henry', email: 'henry@example.com' },
|
|
79
|
-
]);
|
|
80
|
-
setIsLoading(false);
|
|
81
|
-
}, 2000);
|
|
82
|
-
}, []);
|
|
83
|
-
|
|
84
|
-
return <ResponsiveTable columnDefinitions={columns} data={data} isLoading={isLoading} animateOnLoad={true} />;
|
|
85
|
-
};
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Example 2: Adding a Clickable Row Action
|
|
89
|
-
|
|
90
|
-
You can make rows clickable to perform actions, such as navigating to a details page or opening a modal.
|
|
91
|
-
|
|
92
|
-
```jsx
|
|
93
|
-
import React from 'react';
|
|
94
|
-
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
95
|
-
|
|
96
|
-
const ClickableRows = () => {
|
|
97
|
-
const columns = [
|
|
98
|
-
{ displayLabel: 'Product', cellRenderer: (row) => row.product },
|
|
99
|
-
{ displayLabel: 'Price', cellRenderer: (row) => `${row.price.toFixed(2)}` },
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
const data = [
|
|
103
|
-
{ id: 1, product: 'Laptop', price: 1200 },
|
|
104
|
-
{ id: 2, product: 'Keyboard', price: 75 },
|
|
105
|
-
];
|
|
106
|
-
|
|
107
|
-
const handleRowClick = (item) => {
|
|
108
|
-
alert(`You clicked on product ID: ${item.id}`);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
return <ResponsiveTable columnDefinitions={columns} data={data} onRowClick={handleRowClick} />;
|
|
112
|
-
};
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Example 3: Custom Cell Rendering
|
|
116
|
-
|
|
117
|
-
You can render any React component inside a cell, allowing for rich content like buttons, links, or status badges.
|
|
118
|
-
|
|
119
|
-
```jsx
|
|
120
|
-
import React from 'react';
|
|
121
|
-
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
122
|
-
|
|
123
|
-
const CustomCells = () => {
|
|
124
|
-
const columns = [
|
|
125
|
-
{ displayLabel: 'User', cellRenderer: (row) => <strong>{row.user}</strong> },
|
|
126
|
-
{
|
|
127
|
-
displayLabel: 'Status',
|
|
128
|
-
cellRenderer: (row) => (
|
|
129
|
-
<span
|
|
130
|
-
style={{
|
|
131
|
-
color: row.status === 'Active' ? 'green' : 'red',
|
|
132
|
-
fontWeight: 'bold',
|
|
133
|
-
}}
|
|
134
|
-
>
|
|
135
|
-
{row.status}
|
|
136
|
-
</span>
|
|
137
|
-
),
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
displayLabel: 'Action',
|
|
141
|
-
cellRenderer: (row) => <button onClick={() => alert(`Editing ${row.user}`)}>Edit</button>,
|
|
142
|
-
},
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
const data = [
|
|
146
|
-
{ user: 'Eve', status: 'Active' },
|
|
147
|
-
{ user: 'Frank', status: 'Inactive' },
|
|
148
|
-
];
|
|
149
|
-
|
|
150
|
-
return <ResponsiveTable columnDefinitions={columns} data={data} />;
|
|
151
|
-
};
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Example 4: Dynamic and Conditional Columns
|
|
155
|
-
|
|
156
|
-
Columns can be generated dynamically based on your data or application state. This is useful for creating flexible tables that adapt to different datasets.
|
|
157
|
-
|
|
158
|
-
```jsx
|
|
159
|
-
import React from 'react';
|
|
160
|
-
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
161
|
-
|
|
162
|
-
const DynamicColumns = ({ isAdmin }) => {
|
|
163
|
-
// Base columns for all users
|
|
164
|
-
const columns = [
|
|
165
|
-
{ displayLabel: 'File Name', cellRenderer: (row) => row.fileName },
|
|
166
|
-
{ displayLabel: 'Size', cellRenderer: (row) => `${row.size} KB` },
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
// Add an admin-only column conditionally
|
|
170
|
-
if (isAdmin) {
|
|
171
|
-
columns.push({
|
|
172
|
-
displayLabel: 'Admin Actions',
|
|
173
|
-
cellRenderer: (row) => <button onClick={() => alert(`Deleting ${row.fileName}`)}>Delete</button>,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const data = [
|
|
178
|
-
{ fileName: 'document.pdf', size: 1024 },
|
|
179
|
-
{ fileName: 'image.jpg', size: 512 },
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
return <ResponsiveTable columnDefinitions={columns} data={data} />;
|
|
183
|
-
};
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Example 5: Advanced Footer with Labels and Interactivity
|
|
187
|
-
|
|
188
|
-
You can add a footer to display summary information, such as totals or averages. The footer is also responsive and will appear correctly in both desktop and mobile views. With the enhanced footer functionality, you can provide explicit labels for mobile view and add click handlers to footer cells.
|
|
189
|
-
|
|
190
|
-
```jsx
|
|
191
|
-
import React from 'react';
|
|
192
|
-
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
193
|
-
|
|
194
|
-
const TableWithFooter = () => {
|
|
195
|
-
const columns = [
|
|
196
|
-
{ displayLabel: 'Item', cellRenderer: (row) => row.item },
|
|
197
|
-
{ displayLabel: 'Quantity', cellRenderer: (row) => row.quantity },
|
|
198
|
-
{ displayLabel: 'Price', cellRenderer: (row) => `${row.price.toFixed(2)}` },
|
|
199
|
-
];
|
|
200
|
-
|
|
201
|
-
const data = [
|
|
202
|
-
{ item: 'Apples', quantity: 10, price: 1.5 },
|
|
203
|
-
{ item: 'Oranges', quantity: 5, price: 2.0 },
|
|
204
|
-
{ item: 'Bananas', quantity: 15, price: 0.5 },
|
|
205
|
-
];
|
|
206
|
-
|
|
207
|
-
const total = data.reduce((sum, row) => sum + row.quantity * row.price, 0);
|
|
208
|
-
|
|
209
|
-
const footerRows = [
|
|
210
|
-
{
|
|
211
|
-
columns: [
|
|
212
|
-
{
|
|
213
|
-
colSpan: 2,
|
|
214
|
-
cellRenderer: () => <strong>Total:</strong>,
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
colSpan: 1,
|
|
218
|
-
displayLabel: 'Total',
|
|
219
|
-
cellRenderer: () => <strong>${total.toFixed(2)}</strong>,
|
|
220
|
-
onCellClick: () => alert('Total clicked!'),
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
},
|
|
224
|
-
];
|
|
225
|
-
|
|
226
|
-
return <ResponsiveTable columnDefinitions={columns} data={data} footerRows={footerRows} />;
|
|
227
|
-
};
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
---
|
|
231
|
-
|
|
232
|
-
## API Reference
|
|
233
|
-
|
|
234
|
-
### `ResponsiveTable` Props
|
|
235
|
-
|
|
236
|
-
| Prop | Type | Required | Description |
|
|
237
|
-
| ------------------- | ------------------------------------ | -------- | ----------------------------------------------------------------------------------- |
|
|
238
|
-
| `columnDefinitions` | `IResponsiveTableColumnDefinition[]` | Yes | An array of objects defining the table columns. |
|
|
239
|
-
| `data` | `TData[]` | Yes | An array of data objects to populate the table rows. |
|
|
240
|
-
| `isLoading` | `boolean` | No | If `true`, displays a skeleton loader. Defaults to `false`. |
|
|
241
|
-
| `animateOnLoad` | `boolean` | No | If `true`, animates the rows with a staggered entrance effect. Defaults to `false`. |
|
|
242
|
-
| `footerRows` | `IFooterRowDefinition[]` | No | An array of objects defining the table footer. |
|
|
243
|
-
| `onRowClick` | `(item: TData) => void` | No | A callback function that is triggered when a row is clicked. |
|
|
244
|
-
| `noDataComponent` | `ReactNode` | No | A custom component to display when there is no data. |
|
|
245
|
-
| `maxHeight` | `string` | No | Sets a maximum height for the table body, making it scrollable. |
|
|
246
|
-
| `mobileBreakpoint` | `number` | No | The pixel width at which the table switches to the mobile view. Defaults to `600`. |
|
|
247
|
-
|
|
248
|
-
### `IResponsiveTableColumnDefinition`
|
|
249
|
-
|
|
250
|
-
| Property | Type | Required | Description |
|
|
251
|
-
| --------------- | --------------------------- | -------- | ------------------------------------------------------------------------------ |
|
|
252
|
-
| `displayLabel` | `string` | Yes | The label displayed in the table header. |
|
|
253
|
-
| `cellRenderer` | `(row: TData) => ReactNode` | Yes | A function that returns the content to be rendered in the cell. |
|
|
254
|
-
| `dataKey` | `string` | No | A key to match the column to a property in the data object (optional). |
|
|
255
|
-
| `interactivity` | `object` | No | An object to define header interactivity (`onHeaderClick`, `id`, `className`). |
|
|
256
|
-
|
|
257
|
-
### `IFooterRowDefinition`
|
|
258
|
-
|
|
259
|
-
| Property | Type | Required | Description |
|
|
260
|
-
| --------- | --------------------------- | -------- | -------------------------------------------------- |
|
|
261
|
-
| `columns` | `IFooterColumnDefinition[]` | Yes | An array of column definitions for the footer row. |
|
|
262
|
-
|
|
263
|
-
### `IFooterColumnDefinition`
|
|
264
|
-
|
|
265
|
-
| Property | Type | Required | Description |
|
|
266
|
-
| -------------- | ----------------- | -------- | ------------------------------------------------------------------------------ |
|
|
267
|
-
| `colSpan` | `number` | Yes | The number of columns the footer cell should span. |
|
|
268
|
-
| `cellRenderer` | `() => ReactNode` | Yes | A function that returns the content for the footer cell. |
|
|
269
|
-
| `displayLabel` | `ReactNode` | No | An optional, explicit label for the footer cell,
|
|
270
|
-
| `onCellClick` | `() => void` | No | An optional click handler for the footer cell. |
|
|
271
|
-
| `className` | `string` | No | Optional class name for custom styling of the footer cell. |
|
|
272
|
-
|
|
273
|
-
## License
|
|
274
|
-
|
|
275
|
-
This project is licensed under the MIT License.
|
|
1
|
+
# ResponsiveTable: A Modern and Flexible React Table Component
|
|
2
|
+
|
|
3
|
+
ResponsiveTable is a powerful, lightweight, and fully responsive React component for creating beautiful and functional tables. It’s designed to look great on any device, adapting from a traditional table layout on desktops to a clean, card-based view on mobile screens.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Mobile-First Design**: Automatically switches to a card layout on smaller screens for optimal readability.
|
|
8
|
+
- **Highly Customizable**: Tailor the look and feel of columns, headers, and footers.
|
|
9
|
+
- **Dynamic Data Handling**: Define columns and footers based on your data or application state.
|
|
10
|
+
- **Delightful Animations**: Includes an optional skeleton loader and staggered row entrance animations.
|
|
11
|
+
- **Interactive Elements**: Easily add click handlers for rows, headers, and footer cells.
|
|
12
|
+
- **Performant**: Built with performance in mind, including debounced resize handling.
|
|
13
|
+
- **Easy to Use**: A simple and intuitive API for quick integration.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
To get started, install the package from npm:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install jattac.libs.web.responsive-table
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Getting Started
|
|
24
|
+
|
|
25
|
+
Here’s a basic example to get you up and running in minutes.
|
|
26
|
+
|
|
27
|
+
```jsx
|
|
28
|
+
import React from 'react';
|
|
29
|
+
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
30
|
+
|
|
31
|
+
const GettingStarted = () => {
|
|
32
|
+
const columns = [
|
|
33
|
+
{ displayLabel: 'Name', dataKey: 'name', cellRenderer: (row) => row.name },
|
|
34
|
+
{ displayLabel: 'Age', dataKey: 'age', cellRenderer: (row) => row.age },
|
|
35
|
+
{ displayLabel: 'City', dataKey: 'city', cellRenderer: (row) => row.city },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const data = [
|
|
39
|
+
{ name: 'Alice', age: 32, city: 'New York' },
|
|
40
|
+
{ name: 'Bob', age: 28, city: 'Los Angeles' },
|
|
41
|
+
{ name: 'Charlie', age: 45, city: 'Chicago' },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
return <ResponsiveTable columnDefinitions={columns} data={data} />;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default GettingStarted;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This will render a table that automatically adapts to the screen size. On a desktop, it will look like a standard table, and on mobile, it will switch to a card-based layout.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Comprehensive Examples
|
|
55
|
+
|
|
56
|
+
### Example 1: Loading State and Animations
|
|
57
|
+
|
|
58
|
+
You can provide a seamless user experience by showing a skeleton loader while your data is being fetched, and then animating the rows in when the data is ready.
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
import React, { useState, useEffect } from 'react';
|
|
62
|
+
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
63
|
+
|
|
64
|
+
const AnimatedTable = () => {
|
|
65
|
+
const [data, setData] = useState([]);
|
|
66
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
67
|
+
|
|
68
|
+
const columns = [
|
|
69
|
+
{ displayLabel: 'User', cellRenderer: (row) => row.name },
|
|
70
|
+
{ displayLabel: 'Email', cellRenderer: (row) => row.email },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
// Simulate a network request
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
setData([
|
|
77
|
+
{ name: 'Grace', email: 'grace@example.com' },
|
|
78
|
+
{ name: 'Henry', email: 'henry@example.com' },
|
|
79
|
+
]);
|
|
80
|
+
setIsLoading(false);
|
|
81
|
+
}, 2000);
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
return <ResponsiveTable columnDefinitions={columns} data={data} isLoading={isLoading} animateOnLoad={true} />;
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Example 2: Adding a Clickable Row Action
|
|
89
|
+
|
|
90
|
+
You can make rows clickable to perform actions, such as navigating to a details page or opening a modal.
|
|
91
|
+
|
|
92
|
+
```jsx
|
|
93
|
+
import React from 'react';
|
|
94
|
+
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
95
|
+
|
|
96
|
+
const ClickableRows = () => {
|
|
97
|
+
const columns = [
|
|
98
|
+
{ displayLabel: 'Product', cellRenderer: (row) => row.product },
|
|
99
|
+
{ displayLabel: 'Price', cellRenderer: (row) => `${row.price.toFixed(2)}` },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const data = [
|
|
103
|
+
{ id: 1, product: 'Laptop', price: 1200 },
|
|
104
|
+
{ id: 2, product: 'Keyboard', price: 75 },
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const handleRowClick = (item) => {
|
|
108
|
+
alert(`You clicked on product ID: ${item.id}`);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return <ResponsiveTable columnDefinitions={columns} data={data} onRowClick={handleRowClick} />;
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Example 3: Custom Cell Rendering
|
|
116
|
+
|
|
117
|
+
You can render any React component inside a cell, allowing for rich content like buttons, links, or status badges.
|
|
118
|
+
|
|
119
|
+
```jsx
|
|
120
|
+
import React from 'react';
|
|
121
|
+
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
122
|
+
|
|
123
|
+
const CustomCells = () => {
|
|
124
|
+
const columns = [
|
|
125
|
+
{ displayLabel: 'User', cellRenderer: (row) => <strong>{row.user}</strong> },
|
|
126
|
+
{
|
|
127
|
+
displayLabel: 'Status',
|
|
128
|
+
cellRenderer: (row) => (
|
|
129
|
+
<span
|
|
130
|
+
style={{
|
|
131
|
+
color: row.status === 'Active' ? 'green' : 'red',
|
|
132
|
+
fontWeight: 'bold',
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
{row.status}
|
|
136
|
+
</span>
|
|
137
|
+
),
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
displayLabel: 'Action',
|
|
141
|
+
cellRenderer: (row) => <button onClick={() => alert(`Editing ${row.user}`)}>Edit</button>,
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const data = [
|
|
146
|
+
{ user: 'Eve', status: 'Active' },
|
|
147
|
+
{ user: 'Frank', status: 'Inactive' },
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
return <ResponsiveTable columnDefinitions={columns} data={data} />;
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Example 4: Dynamic and Conditional Columns
|
|
155
|
+
|
|
156
|
+
Columns can be generated dynamically based on your data or application state. This is useful for creating flexible tables that adapt to different datasets.
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
import React from 'react';
|
|
160
|
+
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
161
|
+
|
|
162
|
+
const DynamicColumns = ({ isAdmin }) => {
|
|
163
|
+
// Base columns for all users
|
|
164
|
+
const columns = [
|
|
165
|
+
{ displayLabel: 'File Name', cellRenderer: (row) => row.fileName },
|
|
166
|
+
{ displayLabel: 'Size', cellRenderer: (row) => `${row.size} KB` },
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
// Add an admin-only column conditionally
|
|
170
|
+
if (isAdmin) {
|
|
171
|
+
columns.push({
|
|
172
|
+
displayLabel: 'Admin Actions',
|
|
173
|
+
cellRenderer: (row) => <button onClick={() => alert(`Deleting ${row.fileName}`)}>Delete</button>,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const data = [
|
|
178
|
+
{ fileName: 'document.pdf', size: 1024 },
|
|
179
|
+
{ fileName: 'image.jpg', size: 512 },
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
return <ResponsiveTable columnDefinitions={columns} data={data} />;
|
|
183
|
+
};
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Example 5: Advanced Footer with Labels and Interactivity
|
|
187
|
+
|
|
188
|
+
You can add a footer to display summary information, such as totals or averages. The footer is also responsive and will appear correctly in both desktop and mobile views. With the enhanced footer functionality, you can provide explicit labels for mobile view and add click handlers to footer cells.
|
|
189
|
+
|
|
190
|
+
```jsx
|
|
191
|
+
import React from 'react';
|
|
192
|
+
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
193
|
+
|
|
194
|
+
const TableWithFooter = () => {
|
|
195
|
+
const columns = [
|
|
196
|
+
{ displayLabel: 'Item', cellRenderer: (row) => row.item },
|
|
197
|
+
{ displayLabel: 'Quantity', cellRenderer: (row) => row.quantity },
|
|
198
|
+
{ displayLabel: 'Price', cellRenderer: (row) => `${row.price.toFixed(2)}` },
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
const data = [
|
|
202
|
+
{ item: 'Apples', quantity: 10, price: 1.5 },
|
|
203
|
+
{ item: 'Oranges', quantity: 5, price: 2.0 },
|
|
204
|
+
{ item: 'Bananas', quantity: 15, price: 0.5 },
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
const total = data.reduce((sum, row) => sum + row.quantity * row.price, 0);
|
|
208
|
+
|
|
209
|
+
const footerRows = [
|
|
210
|
+
{
|
|
211
|
+
columns: [
|
|
212
|
+
{
|
|
213
|
+
colSpan: 2,
|
|
214
|
+
cellRenderer: () => <strong>Total:</strong>,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
colSpan: 1,
|
|
218
|
+
displayLabel: 'Total',
|
|
219
|
+
cellRenderer: () => <strong>${total.toFixed(2)}</strong>,
|
|
220
|
+
onCellClick: () => alert('Total clicked!'),
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
return <ResponsiveTable columnDefinitions={columns} data={data} footerRows={footerRows} />;
|
|
227
|
+
};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## API Reference
|
|
233
|
+
|
|
234
|
+
### `ResponsiveTable` Props
|
|
235
|
+
|
|
236
|
+
| Prop | Type | Required | Description |
|
|
237
|
+
| ------------------- | ------------------------------------ | -------- | ----------------------------------------------------------------------------------- |
|
|
238
|
+
| `columnDefinitions` | `IResponsiveTableColumnDefinition[]` | Yes | An array of objects defining the table columns. |
|
|
239
|
+
| `data` | `TData[]` | Yes | An array of data objects to populate the table rows. |
|
|
240
|
+
| `isLoading` | `boolean` | No | If `true`, displays a skeleton loader. Defaults to `false`. |
|
|
241
|
+
| `animateOnLoad` | `boolean` | No | If `true`, animates the rows with a staggered entrance effect. Defaults to `false`. |
|
|
242
|
+
| `footerRows` | `IFooterRowDefinition[]` | No | An array of objects defining the table footer. |
|
|
243
|
+
| `onRowClick` | `(item: TData) => void` | No | A callback function that is triggered when a row is clicked. |
|
|
244
|
+
| `noDataComponent` | `ReactNode` | No | A custom component to display when there is no data. |
|
|
245
|
+
| `maxHeight` | `string` | No | Sets a maximum height for the table body, making it scrollable. |
|
|
246
|
+
| `mobileBreakpoint` | `number` | No | The pixel width at which the table switches to the mobile view. Defaults to `600`. |
|
|
247
|
+
|
|
248
|
+
### `IResponsiveTableColumnDefinition`
|
|
249
|
+
|
|
250
|
+
| Property | Type | Required | Description |
|
|
251
|
+
| --------------- | --------------------------- | -------- | ------------------------------------------------------------------------------ |
|
|
252
|
+
| `displayLabel` | `string` | Yes | The label displayed in the table header. |
|
|
253
|
+
| `cellRenderer` | `(row: TData) => ReactNode` | Yes | A function that returns the content to be rendered in the cell. |
|
|
254
|
+
| `dataKey` | `string` | No | A key to match the column to a property in the data object (optional). |
|
|
255
|
+
| `interactivity` | `object` | No | An object to define header interactivity (`onHeaderClick`, `id`, `className`). |
|
|
256
|
+
|
|
257
|
+
### `IFooterRowDefinition`
|
|
258
|
+
|
|
259
|
+
| Property | Type | Required | Description |
|
|
260
|
+
| --------- | --------------------------- | -------- | -------------------------------------------------- |
|
|
261
|
+
| `columns` | `IFooterColumnDefinition[]` | Yes | An array of column definitions for the footer row. |
|
|
262
|
+
|
|
263
|
+
### `IFooterColumnDefinition`
|
|
264
|
+
|
|
265
|
+
| Property | Type | Required | Description |
|
|
266
|
+
| -------------- | ----------------- | -------- | ------------------------------------------------------------------------------ |
|
|
267
|
+
| `colSpan` | `number` | Yes | The number of columns the footer cell should span. |
|
|
268
|
+
| `cellRenderer` | `() => ReactNode` | Yes | A function that returns the content for the footer cell. |
|
|
269
|
+
| `displayLabel` | `ReactNode` | No | An optional, explicit label for the footer cell. In mobile view, if `colSpan` is 1 and this is not provided, the corresponding column header will be used as a fallback. This is required for `colSpan` > 1 if you want a label to be displayed. |
|
|
270
|
+
| `onCellClick` | `() => void` | No | An optional click handler for the footer cell. |
|
|
271
|
+
| `className` | `string` | No | Optional class name for custom styling of the footer cell. |
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
This project is licensed under the MIT License.
|
package/dist/index.js
CHANGED
|
@@ -29,8 +29,8 @@ function styleInject(css, ref) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
var css_248z = "/* Using CSS variables for a more maintainable and themeable design */\n:root {\n --table-border-color: #e0e0e0;\n --table-header-bg: #f8f9fa;\n --table-row-hover-bg: #
|
|
33
|
-
var styles = {"card":"ResponsiveTable-module_card__b-U2v","card-header":"ResponsiveTable-module_card-header__Ttk51","card-body":"ResponsiveTable-module_card-body__XIy0h","responsiveTable":"ResponsiveTable-module_responsiveTable__4y-Od","clickableHeader":"ResponsiveTable-module_clickableHeader__xHQhF","footerCell":"ResponsiveTable-module_footerCell__8H-uG","clickableFooterCell":"ResponsiveTable-module_clickableFooterCell__WB9Ss","footerCard":"ResponsiveTable-module_footerCard__-NE2M","footer-card-body":"ResponsiveTable-module_footer-card-body__CtBMv","footer-card-row":"ResponsiveTable-module_footer-card-row__Vv6Ur","animatedRow":"ResponsiveTable-module_animatedRow__SFjrJ","skeleton":"ResponsiveTable-module_skeleton__XxsXW","skeletonText":"ResponsiveTable-module_skeletonText__T-Lgq","skeletonCard":"ResponsiveTable-module_skeletonCard__AYVwL","noDataWrapper":"ResponsiveTable-module_noDataWrapper__Rj-k3","noData":"ResponsiveTable-module_noData__IpwNq"};
|
|
32
|
+
var css_248z = "/* Using CSS variables for a more maintainable and themeable design */\n:root {\n --table-border-color: #e0e0e0;\n --table-header-bg: #f8f9fa;\n --table-row-hover-bg: #e9ecef;\n --table-row-stripe-bg: #f2f2f2;\n --card-bg: #ffffff;\n --card-border-color: #e0e0e0;\n --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n --text-color: #212529;\n --text-color-muted: #6c757d;\n --interactive-color: #0056b3;\n}\n\n/* Mobile Card View */\n.ResponsiveTable-module_card__b-U2v {\n background-color: var(--card-bg);\n border: 1px solid var(--card-border-color);\n margin-bottom: 1rem;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: var(--card-shadow);\n transition: box-shadow 0.2s ease-in-out;\n}\n\n.ResponsiveTable-module_card__b-U2v:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* This is not used in the component, but keeping it styled in case it's added back */\n.ResponsiveTable-module_card-header__Ttk51 {\n background-color: var(--table-header-bg);\n padding: 0.75rem 1rem;\n font-weight: 600; /* Bolder */\n border-bottom: 1px solid var(--table-border-color);\n}\n\n.ResponsiveTable-module_card-body__XIy0h {\n padding: 1rem;\n font-size: 0.9rem;\n}\n\n.ResponsiveTable-module_card-body__XIy0h p {\n margin: 0 0 0.75rem;\n display: flex;\n justify-content: space-between;\n}\n\n.ResponsiveTable-module_card-body__XIy0h p:last-child {\n margin-bottom: 0;\n}\n\n.ResponsiveTable-module_card-label__v9L71 {\n font-weight: 600;\n color: var(--text-color);\n margin-right: 0.5rem;\n}\n\n/* Desktop Table View */\n.ResponsiveTable-module_responsiveTable__4y-Od {\n width: 100%;\n border-collapse: collapse;\n color: var(--text-color);\n}\n\n.ResponsiveTable-module_responsiveTable__4y-Od thead th {\n background-color: var(--table-header-bg);\n position: sticky;\n top: 0;\n z-index: 2;\n font-weight: 600;\n text-align: left;\n padding: 1rem;\n border-bottom: 2px solid var(--table-border-color);\n}\n\n.ResponsiveTable-module_responsiveTable__4y-Od td {\n padding: 1rem;\n border-bottom: 1px solid var(--table-border-color);\n text-align: left;\n}\n\n.ResponsiveTable-module_responsiveTable__4y-Od tr {\n background-color: var(--card-bg);\n transition: background-color 0.2s ease-in-out;\n}\n\n/* Subtle striping for better readability */\n.ResponsiveTable-module_responsiveTable__4y-Od tr:nth-child(even) {\n background-color: var(--table-row-stripe-bg);\n}\n\n/* Modern hover effect */\n.ResponsiveTable-module_responsiveTable__4y-Od tr:hover {\n background-color: var(--table-row-hover-bg);\n}\n\n/* Clickable Header Style */\n.ResponsiveTable-module_clickableHeader__xHQhF {\n cursor: pointer;\n color: var(--interactive-color);\n}\n\n.ResponsiveTable-module_clickableHeader__xHQhF:hover {\n text-decoration: underline;\n}\n\n.ResponsiveTable-module_responsiveTable__4y-Od tfoot {\n background-color: var(--table-header-bg);\n border-top: 2px solid var(--table-border-color);\n font-weight: 600;\n}\n\n.ResponsiveTable-module_footerCell__8H-uG {\n padding: 1rem;\n text-align: right;\n font-weight: 600;\n}\n\n.ResponsiveTable-module_clickableFooterCell__WB9Ss {\n cursor: pointer;\n color: var(--interactive-color);\n}\n\n.ResponsiveTable-module_clickableFooterCell__WB9Ss:hover {\n text-decoration: underline;\n}\n\n.ResponsiveTable-module_footerCard__-NE2M {\n background-color: var(--table-header-bg);\n border: 1px solid var(--card-border-color);\n border-top: 2px solid var(--table-border-color);\n margin-bottom: 1rem;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: none;\n}\n\n.ResponsiveTable-module_footer-card-body__CtBMv {\n padding: 1rem;\n font-size: 0.9rem;\n}\n\n.ResponsiveTable-module_footer-card-row__Vv6Ur {\n margin: 0 0 0.75rem;\n display: flex;\n justify-content: space-between;\n font-weight: 600;\n}\n\n/* No Data State */\n\n/* Staggered Entrance Animation */\n.ResponsiveTable-module_animatedRow__SFjrJ {\n animation: ResponsiveTable-module_fadeInUp__jMCS7 0.5s ease-out forwards;\n opacity: 0;\n}\n\n@keyframes ResponsiveTable-module_fadeInUp__jMCS7 {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Skeleton Loader */\n.ResponsiveTable-module_skeleton__XxsXW {\n background-color: #e0e0e0;\n border-radius: 4px;\n position: relative;\n overflow: hidden;\n}\n\n.ResponsiveTable-module_skeleton__XxsXW::after {\n content: '';\n position: absolute;\n top: 0;\n left: -150%;\n width: 150%;\n height: 100%;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);\n animation: ResponsiveTable-module_shimmer__H8PhC 1.5s infinite;\n}\n\n.ResponsiveTable-module_skeletonText__T-Lgq {\n height: 1.2em;\n width: 100%;\n}\n\n.ResponsiveTable-module_skeletonCard__AYVwL {\n background-color: var(--card-bg);\n border: 1px solid var(--card-border-color);\n margin-bottom: 1rem;\n border-radius: 8px;\n overflow: hidden;\n padding: 1rem;\n}\n\n@keyframes ResponsiveTable-module_shimmer__H8PhC {\n 0% {\n transform: translateX(0);\n }\n 100% {\n transform: translateX(100%);\n }\n}\n.ResponsiveTable-module_noDataWrapper__Rj-k3 {\n color: var(--text-color-muted); /* Softer color */\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n gap: 10px;\n padding: 40px;\n background-color: var(--table-header-bg);\n border: 1px dashed var(--table-border-color);\n border-radius: 8px;\n}\n\n.ResponsiveTable-module_noData__IpwNq {\n text-align: center;\n font-weight: 500; /* Less aggressive than bold */\n font-size: 1rem;\n}\n";
|
|
33
|
+
var styles = {"card":"ResponsiveTable-module_card__b-U2v","card-header":"ResponsiveTable-module_card-header__Ttk51","card-body":"ResponsiveTable-module_card-body__XIy0h","card-label":"ResponsiveTable-module_card-label__v9L71","responsiveTable":"ResponsiveTable-module_responsiveTable__4y-Od","clickableHeader":"ResponsiveTable-module_clickableHeader__xHQhF","footerCell":"ResponsiveTable-module_footerCell__8H-uG","clickableFooterCell":"ResponsiveTable-module_clickableFooterCell__WB9Ss","footerCard":"ResponsiveTable-module_footerCard__-NE2M","footer-card-body":"ResponsiveTable-module_footer-card-body__CtBMv","footer-card-row":"ResponsiveTable-module_footer-card-row__Vv6Ur","animatedRow":"ResponsiveTable-module_animatedRow__SFjrJ","skeleton":"ResponsiveTable-module_skeleton__XxsXW","skeletonText":"ResponsiveTable-module_skeletonText__T-Lgq","skeletonCard":"ResponsiveTable-module_skeletonCard__AYVwL","noDataWrapper":"ResponsiveTable-module_noDataWrapper__Rj-k3","noData":"ResponsiveTable-module_noData__IpwNq"};
|
|
34
34
|
styleInject(css_248z);
|
|
35
35
|
|
|
36
36
|
// Class component
|
|
@@ -143,12 +143,22 @@ class ResponsiveTable extends React.Component {
|
|
|
143
143
|
return null;
|
|
144
144
|
}
|
|
145
145
|
return (React.createElement("div", { className: styles.footerCard },
|
|
146
|
-
React.createElement("div", { className: styles['footer-card-body'] }, this.props.footerRows.map((row, rowIndex) =>
|
|
147
|
-
|
|
148
|
-
return (React.createElement("
|
|
149
|
-
label
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
React.createElement("div", { className: styles['footer-card-body'] }, this.props.footerRows.map((row, rowIndex) => {
|
|
147
|
+
let currentColumnIndex = 0;
|
|
148
|
+
return (React.createElement("div", { key: rowIndex }, row.columns.map((col, colIndex) => {
|
|
149
|
+
let label = col.displayLabel;
|
|
150
|
+
if (!label && col.colSpan === 1) {
|
|
151
|
+
const header = this.props.columnDefinitions[currentColumnIndex];
|
|
152
|
+
if (header) {
|
|
153
|
+
label = this.getRawColumnDefinition(header).displayLabel;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
currentColumnIndex += col.colSpan;
|
|
157
|
+
return (React.createElement("p", { key: colIndex, className: `${styles['footer-card-row']} ${col.className || ''} ${col.onCellClick ? styles.clickableFooterCell : ''}`, onClick: col.onCellClick },
|
|
158
|
+
label && React.createElement("span", { className: styles['card-label'] }, label),
|
|
159
|
+
React.createElement("span", { className: styles['card-value'] }, col.cellRenderer())));
|
|
160
|
+
})));
|
|
161
|
+
}))));
|
|
152
162
|
}
|
|
153
163
|
get skeletonView() {
|
|
154
164
|
const skeletonRowCount = 5; // Or make this configurable
|