jattac.libs.web.responsive-table 0.2.3 → 0.2.4
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 +152 -147
- package/dist/Data/IResponsiveTableColumnDefinition.d.ts +4 -0
- package/dist/Plugins/SortPlugin.d.ts +29 -0
- package/dist/UI/ResponsiveTable.d.ts +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +118 -5
- package/dist/index.js.map +1 -1
- package/package.json +12 -6
- package/.eslintrc.json +0 -8
- package/.prettierrc +0 -5
- package/gemini/index.md +0 -1
- package/gemini/project_analysis.md +0 -41
- package/gemini/project_memos.md +0 -3
- package/gemini/release_git_steps.md +0 -80
- package/rollup.config.js +0 -23
- package/set-origin-urls.sh +0 -32
- package/src/Data/IFooterColumnDefinition.ts +0 -21
- package/src/Data/IFooterRowDefinition.ts +0 -5
- package/src/Data/IResponsiveTableColumnDefinition.tsx +0 -12
- package/src/Plugins/FilterPlugin.tsx +0 -71
- package/src/Plugins/IResponsiveTablePlugin.ts +0 -48
- package/src/Plugins/InfiniteScrollPlugin.tsx +0 -73
- package/src/Styles/ResponsiveTable.module.css +0 -260
- package/src/UI/ResponsiveTable.tsx +0 -552
- package/src/index.tsx +0 -10
- package/src/typings.d.ts +0 -14
- package/tsconfig.json +0 -22
- package/update-dependancies.sh +0 -4
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ResponsiveTable: A Modern and Flexible React Table Component
|
|
1
|
+
'''# ResponsiveTable: A Modern and Flexible React Table Component
|
|
2
2
|
|
|
3
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
4
|
|
|
@@ -11,7 +11,7 @@ ResponsiveTable is a powerful, lightweight, and fully responsive React component
|
|
|
11
11
|
- **Interactive Elements**: Easily add click handlers for rows, headers, and footer cells.
|
|
12
12
|
- **Performant**: Built with performance in mind, including debounced resize handling.
|
|
13
13
|
- **Easy to Use**: A simple and intuitive API for quick integration.
|
|
14
|
-
- **Extensible Plugin System**: Easily add new functionalities like filtering,
|
|
14
|
+
- **Extensible Plugin System**: Easily add new functionalities like filtering, sorting, or infinite scrolling.
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -123,7 +123,7 @@ import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
|
123
123
|
|
|
124
124
|
const CustomCells = () => {
|
|
125
125
|
const columns = [
|
|
126
|
-
{ displayLabel:
|
|
126
|
+
{ displayLabel: <strong>User</strong>, cellRenderer: (row) => <strong>{row.user}</strong> },
|
|
127
127
|
{
|
|
128
128
|
displayLabel: 'Status',
|
|
129
129
|
cellRenderer: (row) => (
|
|
@@ -284,8 +284,8 @@ Plugins are passed to the `ResponsiveTable` component via the `plugins` prop, wh
|
|
|
284
284
|
|
|
285
285
|
```jsx
|
|
286
286
|
import React from 'react';
|
|
287
|
-
|
|
288
|
-
import { FilterPlugin } from 'jattac.libs.web.responsive-table
|
|
287
|
+
// Note: All plugins are exported from the main package entry point.
|
|
288
|
+
import ResponsiveTable, { FilterPlugin } from 'jattac.libs.web.responsive-table';
|
|
289
289
|
|
|
290
290
|
const MyTableWithPlugins = () => {
|
|
291
291
|
const columns = [
|
|
@@ -314,6 +314,145 @@ const MyTableWithPlugins = () => {
|
|
|
314
314
|
|
|
315
315
|
### Built-in Plugins
|
|
316
316
|
|
|
317
|
+
#### `SortPlugin`
|
|
318
|
+
|
|
319
|
+
The `SortPlugin` provides powerful, type-safe, and highly customizable column sorting. It adds intuitive UI cues, allowing users to click column headers to sort the data in ascending, descending, or original order.
|
|
320
|
+
|
|
321
|
+
**Enabling the `SortPlugin`:**
|
|
322
|
+
|
|
323
|
+
To use the plugin, you must first import it and provide a generic instance of it to the `plugins` prop. The real power comes from making the plugin instance generic with your data type, which provides type-safety and IDE autocompletion for the sort comparer helpers.
|
|
324
|
+
|
|
325
|
+
```jsx
|
|
326
|
+
import React from 'react';
|
|
327
|
+
import ResponsiveTable, { IResponsiveTableColumnDefinition, SortPlugin } from 'jattac.libs.web.responsive-table';
|
|
328
|
+
|
|
329
|
+
// Define the shape of your data
|
|
330
|
+
interface User {
|
|
331
|
+
id: number;
|
|
332
|
+
name: string;
|
|
333
|
+
signupDate: string;
|
|
334
|
+
logins: number;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 1. Create a single, generic instance of the plugin.
|
|
338
|
+
// This is the ONLY setup step required.
|
|
339
|
+
const sortPlugin = new SortPlugin<User>({
|
|
340
|
+
initialSortColumn: 'logins',
|
|
341
|
+
initialSortDirection: 'desc',
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// 2. Define the columns, using the helpers directly from the plugin instance.
|
|
345
|
+
const columnDefinitions: IResponsiveTableColumnDefinition<User>[] = [
|
|
346
|
+
// ... see examples below
|
|
347
|
+
];
|
|
348
|
+
|
|
349
|
+
const UserTable = ({ users }) => (
|
|
350
|
+
<ResponsiveTable
|
|
351
|
+
columnDefinitions={columnDefinitions}
|
|
352
|
+
data={users}
|
|
353
|
+
// 3. Pass the already-configured plugin to the table.
|
|
354
|
+
plugins={[sortPlugin]}
|
|
355
|
+
/>
|
|
356
|
+
);
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**How to Make Columns Sortable (Opt-In):**
|
|
360
|
+
|
|
361
|
+
A column is made sortable by adding either a `sortComparer` or a `getSortableValue` property to its definition.
|
|
362
|
+
|
|
363
|
+
* `sortComparer`: A function that defines the exact comparison logic. This is the most powerful option and should be used for complex data types or custom logic.
|
|
364
|
+
* `getSortableValue`: A simpler function that just returns the primitive value (string, number, etc.) to be used in a default comparison.
|
|
365
|
+
|
|
366
|
+
**Example 1: Using Type-Safe Comparer Helpers**
|
|
367
|
+
|
|
368
|
+
The `SortPlugin` instance provides a `comparers` object with pre-built, type-safe helper functions to eliminate boilerplate for common sorting scenarios. This is the recommended approach.
|
|
369
|
+
|
|
370
|
+
```jsx
|
|
371
|
+
const columnDefinitions: IResponsiveTableColumnDefinition<User>[] = [
|
|
372
|
+
{
|
|
373
|
+
displayLabel: 'Name',
|
|
374
|
+
dataKey: 'name',
|
|
375
|
+
cellRenderer: (user) => user.name,
|
|
376
|
+
// The plugin instance itself provides the type-safe helpers.
|
|
377
|
+
// The string 'name' is fully type-checked against the User interface.
|
|
378
|
+
sortComparer: sortPlugin.comparers.caseInsensitiveString('name'),
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
displayLabel: 'Signup Date',
|
|
382
|
+
dataKey: 'signupDate',
|
|
383
|
+
cellRenderer: (user) => new Date(user.signupDate).toLocaleDateString(),
|
|
384
|
+
// IDE autocompletion for 'signupDate' works perfectly.
|
|
385
|
+
sortComparer: sortPlugin.comparers.date('signupDate'),
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
displayLabel: 'Logins',
|
|
389
|
+
dataKey: 'logins',
|
|
390
|
+
cellRenderer: (user) => user.logins,
|
|
391
|
+
sortComparer: sortPlugin.comparers.numeric('logins'),
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
displayLabel: 'Actions',
|
|
395
|
+
// This column is NOT sortable because it has no sort-related properties.
|
|
396
|
+
cellRenderer: (user) => <button>View</button>,
|
|
397
|
+
},
|
|
398
|
+
];
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Example 2: Writing a Custom `sortComparer`**
|
|
402
|
+
|
|
403
|
+
For unique requirements, you can write your own comparison function from scratch.
|
|
404
|
+
|
|
405
|
+
```jsx
|
|
406
|
+
const columnDefinitions: IResponsiveTableColumnDefinition<User>[] = [
|
|
407
|
+
{
|
|
408
|
+
displayLabel: 'Name',
|
|
409
|
+
dataKey: 'name',
|
|
410
|
+
cellRenderer: (user) => user.name,
|
|
411
|
+
// Writing custom logic for a case-sensitive sort
|
|
412
|
+
sortComparer: (a, b, direction) => {
|
|
413
|
+
const nameA = a.name; // No .toLowerCase()
|
|
414
|
+
const nameB = b.name;
|
|
415
|
+
if (nameA < nameB) return direction === 'asc' ? -1 : 1;
|
|
416
|
+
if (nameA > nameB) return direction === 'asc' ? 1 : -1;
|
|
417
|
+
return 0;
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
];
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Example 3: Using `getSortableValue` for Simple Cases**
|
|
424
|
+
|
|
425
|
+
If you don't need special logic, `getSortableValue` is a concise way to enable default sorting on a property.
|
|
426
|
+
|
|
427
|
+
```jsx
|
|
428
|
+
const columnDefinitions: IResponsiveTableColumnDefinition<User>[] = [
|
|
429
|
+
{
|
|
430
|
+
displayLabel: 'Logins',
|
|
431
|
+
dataKey: 'logins',
|
|
432
|
+
cellRenderer: (user) => user.logins,
|
|
433
|
+
// This enables a simple, default numerical sort on the 'logins' property.
|
|
434
|
+
getSortableValue: (user) => user.logins,
|
|
435
|
+
},
|
|
436
|
+
];
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Plugin Options (via `new SortPlugin(options)`):**
|
|
440
|
+
|
|
441
|
+
| Prop | Type (`keyof TData`) | Description |
|
|
442
|
+
| --------------------- | -------------------- | --------------------------------------------------------------------------- |
|
|
443
|
+
| `initialSortColumn` | `string` | The `dataKey` of the column to sort by initially. |
|
|
444
|
+
| `initialSortDirection`| `'asc' \| 'desc'` | The direction for the initial sort. |
|
|
445
|
+
|
|
446
|
+
**`SortPlugin.comparers` API:**
|
|
447
|
+
|
|
448
|
+
The `comparers` object on your `SortPlugin` instance provides the following helper methods. Each method is a factory that takes a `dataKey` (which is type-checked against your data model) and returns a `sortComparer` function.
|
|
449
|
+
|
|
450
|
+
| Method | Description |
|
|
451
|
+
| ----------------------- | --------------------------------------------------------------------------- |
|
|
452
|
+
| `numeric(dataKey)` | Performs a standard numerical sort. |
|
|
453
|
+
| `caseInsensitiveString(dataKey)` | Performs a case-insensitive alphabetical sort. |
|
|
454
|
+
| `date(dataKey)` | Correctly sorts dates, assuming the data is a valid date string or timestamp. |
|
|
455
|
+
|
|
317
456
|
#### `FilterPlugin`
|
|
318
457
|
|
|
319
458
|
Provides a search input to filter table data. It can be enabled by setting `filterProps.showFilter` to `true` on the `ResponsiveTable` component. For columns to be filterable, you must provide a `getFilterableValue` function in their `IResponsiveTableColumnDefinition`.
|
|
@@ -328,7 +467,7 @@ Provides a search input to filter table data. It can be enabled by setting `filt
|
|
|
328
467
|
**Example with `FilterPlugin`:**
|
|
329
468
|
|
|
330
469
|
```jsx
|
|
331
|
-
import React
|
|
470
|
+
import React from 'react';
|
|
332
471
|
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
333
472
|
|
|
334
473
|
const FilterableTable = () => {
|
|
@@ -437,143 +576,6 @@ const InfiniteScrollTable = () => {
|
|
|
437
576
|
};
|
|
438
577
|
```
|
|
439
578
|
|
|
440
|
-
### Extending Functionality with Custom Plugins
|
|
441
|
-
|
|
442
|
-
Developers can create their own custom plugins to add unique features to the `ResponsiveTable`. This is achieved by implementing the `IResponsiveTablePlugin` interface.
|
|
443
|
-
|
|
444
|
-
**`IResponsiveTablePlugin<TData>` Interface:**
|
|
445
|
-
|
|
446
|
-
| Property | Type | Description |
|
|
447
|
-
| -------------- | ------------------------------------ | --------------------------------------------------------------------------- |
|
|
448
|
-
| `id` | `string` | A unique identifier for the plugin. |
|
|
449
|
-
| `renderHeader?`| `() => ReactNode` | Optional. A function that returns a React component to be rendered above the table. |
|
|
450
|
-
| `renderFooter?`| `() => ReactNode` | Optional. A function that returns a React component to be rendered below the table. |
|
|
451
|
-
| `processData?` | `(data: TData[]) => TData[]` | Optional. A function that processes the table data before it is rendered. Useful for sorting, filtering, or transforming data. |
|
|
452
|
-
| `onPluginInit?`| `(api: IPluginAPI<TData>) => void` | Optional. A callback function that provides the plugin with an API to interact with the `ResponsiveTable` component. |
|
|
453
|
-
|
|
454
|
-
**`IPluginAPI<TData>` Interface:**
|
|
455
|
-
|
|
456
|
-
This interface provides methods and properties for plugins to interact with the `ResponsiveTable` component.
|
|
457
|
-
|
|
458
|
-
| Property | Type | Description |
|
|
459
|
-
| -------------------- | ------------------------------------ | --------------------------------------------------------------------------- |
|
|
460
|
-
| `getData` | `() => TData[]` | Returns the current raw data array being used by the table. |
|
|
461
|
-
| `forceUpdate` | `() => void` | Forces the `ResponsiveTable` component to re-render. Useful after a plugin modifies internal state that affects rendering. |
|
|
462
|
-
| `columnDefinitions` | `ColumnDefinition<TData>[]` | Provides access to the table's column definitions. |
|
|
463
|
-
| `getScrollableElement?`| `() => HTMLElement | null` | Optional. Returns the HTML element that is scrollable, if `maxHeight` is set. Useful for implementing scroll-based features. |
|
|
464
|
-
| `infiniteScrollProps?`| `object` | Optional. Provides access to the `infiniteScrollProps` passed to the `ResponsiveTable`. |
|
|
465
|
-
| `filterProps?` | `object` | Optional. Provides access to the `filterProps` passed to the `ResponsiveTable`. |
|
|
466
|
-
|
|
467
|
-
**Example: Custom Sorting Plugin**
|
|
468
|
-
|
|
469
|
-
This example demonstrates a simple sorting plugin that allows sorting by a specified column.
|
|
470
|
-
|
|
471
|
-
```typescript
|
|
472
|
-
// src/Plugins/SortPlugin.ts
|
|
473
|
-
import React from 'react';
|
|
474
|
-
import { IResponsiveTablePlugin, IPluginAPI } from './IResponsiveTablePlugin';
|
|
475
|
-
import IResponsiveTableColumnDefinition from '../Data/IResponsiveTableColumnDefinition';
|
|
476
|
-
|
|
477
|
-
export class SortPlugin<TData> implements IResponsiveTablePlugin<TData> {
|
|
478
|
-
public id = 'sort';
|
|
479
|
-
private api!: IPluginAPI<TData>;
|
|
480
|
-
private sortColumn: string | null = null;
|
|
481
|
-
private sortDirection: 'asc' | 'desc' = 'asc';
|
|
482
|
-
|
|
483
|
-
public onPluginInit = (api: IPluginAPI<TData>) => {
|
|
484
|
-
this.api = api;
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
public renderHeader = () => {
|
|
488
|
-
return (
|
|
489
|
-
<div style={{ marginBottom: '1rem' }}>
|
|
490
|
-
Sort by:
|
|
491
|
-
<select onChange={this.handleColumnChange} style={{ marginLeft: '0.5rem' }}>
|
|
492
|
-
<option value="">None</option>
|
|
493
|
-
{this.api.columnDefinitions.map((colDef) => {
|
|
494
|
-
const rawColDef = colDef as IResponsiveTableColumnDefinition<TData>;
|
|
495
|
-
if (rawColDef.dataKey) {
|
|
496
|
-
return <option key={rawColDef.dataKey} value={rawColDef.dataKey}>{rawColDef.displayLabel}</option>;
|
|
497
|
-
}
|
|
498
|
-
return null;
|
|
499
|
-
})}
|
|
500
|
-
</select>
|
|
501
|
-
{this.sortColumn && (
|
|
502
|
-
<button onClick={this.toggleSortDirection} style={{ marginLeft: '0.5rem' }}>
|
|
503
|
-
{this.sortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
|
504
|
-
</button>
|
|
505
|
-
)}
|
|
506
|
-
</div>
|
|
507
|
-
);
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
public processData = (data: TData[]): TData[] => {
|
|
511
|
-
if (!this.sortColumn) {
|
|
512
|
-
return data;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
const sortedData = [...data].sort((a, b) => {
|
|
516
|
-
const aValue = a[this.sortColumn as keyof TData];
|
|
517
|
-
const bValue = b[this.sortColumn as keyof TData];
|
|
518
|
-
|
|
519
|
-
if (aValue < bValue) return this.sortDirection === 'asc' ? -1 : 1;
|
|
520
|
-
if (aValue > bValue) return this.sortDirection === 'asc' ? 1 : -1;
|
|
521
|
-
return 0;
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
return sortedData;
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
private handleColumnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
528
|
-
this.sortColumn = e.target.value || null;
|
|
529
|
-
this.api.forceUpdate();
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
private toggleSortDirection = () => {
|
|
533
|
-
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
|
|
534
|
-
this.api.forceUpdate();
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Usage in your component:
|
|
539
|
-
```jsx
|
|
540
|
-
import React from 'react';
|
|
541
|
-
import ResponsiveTable from 'jattac.libs.web.responsive-table';
|
|
542
|
-
import { SortPlugin } from './SortPlugin'; // Assuming SortPlugin.ts is in the same directory
|
|
543
|
-
|
|
544
|
-
const SortableTable = () => {
|
|
545
|
-
const data = [
|
|
546
|
-
{ id: 1, name: 'Alice', age: 30 },
|
|
547
|
-
{ id: 2, name: 'Bob', age: 25 },
|
|
548
|
-
{ id: 3, name: 'Charlie', age: 35 },
|
|
549
|
-
];
|
|
550
|
-
|
|
551
|
-
const columns = [
|
|
552
|
-
{ displayLabel: 'ID', dataKey: 'id', cellRenderer: (row) => row.id },
|
|
553
|
-
{ displayLabel: 'Name', dataKey: 'name', cellRenderer: (row) => row.name },
|
|
554
|
-
{ displayLabel: 'Age', dataKey: 'age', cellRenderer: (row) => row.age },
|
|
555
|
-
];
|
|
556
|
-
|
|
557
|
-
return (
|
|
558
|
-
<ResponsiveTable
|
|
559
|
-
columnDefinitions={columns}
|
|
560
|
-
data={data}
|
|
561
|
-
plugins={[new SortPlugin()]}
|
|
562
|
-
/>
|
|
563
|
-
);
|
|
564
|
-
};
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
**Other Plugin Ideas (beyond Filtering and Infinite Scroll):**
|
|
568
|
-
|
|
569
|
-
- **Column Resizing Plugin:** Allows users to drag column headers to resize columns.
|
|
570
|
-
- **Row Selection Plugin:** Adds checkboxes to rows for multi-row selection.
|
|
571
|
-
- **Export Data Plugin:** Provides buttons to export table data to CSV, Excel, or PDF.
|
|
572
|
-
- **Drag-and-Drop Reordering Plugin:** Enables reordering of rows or columns via drag and drop.
|
|
573
|
-
- **Column Visibility Toggle Plugin:** Allows users to show/hide specific columns.
|
|
574
|
-
|
|
575
|
-
---
|
|
576
|
-
|
|
577
579
|
---
|
|
578
580
|
|
|
579
581
|
## API Reference
|
|
@@ -595,15 +597,17 @@ const SortableTable = () => {
|
|
|
595
597
|
| `filterProps` | `object` | No | Configuration for the built-in filter plugin. |
|
|
596
598
|
| `animationProps` | `object` | No | Configuration for animations, including `isLoading` and `animateOnLoad`. |
|
|
597
599
|
|
|
598
|
-
### `IResponsiveTableColumnDefinition
|
|
600
|
+
### `IResponsiveTableColumnDefinition<TData>`
|
|
599
601
|
|
|
600
602
|
| Property | Type | Required | Description |
|
|
601
603
|
| --------------- | --------------------------- | -------- | ------------------------------------------------------------------------------ |
|
|
602
|
-
| `displayLabel` | `
|
|
604
|
+
| `displayLabel` | `ReactNode` | Yes | The label displayed in the table header (can be a string or any React component). |
|
|
603
605
|
| `cellRenderer` | `(row: TData) => ReactNode` | Yes | A function that returns the content to be rendered in the cell. |
|
|
604
|
-
| `dataKey` | `string` | No | A key to match the column to a property in the data object (
|
|
606
|
+
| `dataKey` | `string` | No | A key to match the column to a property in the data object (required for sorting). |
|
|
605
607
|
| `interactivity` | `object` | No | An object to define header interactivity (`onHeaderClick`, `id`, `className`). |
|
|
606
|
-
| `getFilterableValue`| `(row: TData) => string`
|
|
608
|
+
| `getFilterableValue`| `(row: TData) => string \| number` | No | A function that returns the string or number value to be used for filtering this column. |
|
|
609
|
+
| `getSortableValue`| `(row: TData) => any` | No | A function that returns a primitive value from a row to be used for default sorting. |
|
|
610
|
+
| `sortComparer` | `(a: TData, b: TData, direction: 'asc' \| 'desc') => number` | No | A function that provides the precise comparison logic for sorting a column. |
|
|
607
611
|
|
|
608
612
|
### `IFooterRowDefinition`
|
|
609
613
|
|
|
@@ -624,3 +628,4 @@ const SortableTable = () => {
|
|
|
624
628
|
## License
|
|
625
629
|
|
|
626
630
|
This project is licensed under the MIT License.
|
|
631
|
+
''
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
export type SortDirection = 'asc' | 'desc';
|
|
2
3
|
export default interface IResponsiveTableColumnDefinition<TData> {
|
|
3
4
|
displayLabel: ReactNode;
|
|
4
5
|
cellRenderer: (data: TData) => ReactNode;
|
|
6
|
+
dataKey?: string;
|
|
5
7
|
interactivity?: {
|
|
6
8
|
id: string;
|
|
7
9
|
onHeaderClick?: (id: string) => void;
|
|
8
10
|
className?: string;
|
|
9
11
|
};
|
|
10
12
|
getFilterableValue?: (data: TData) => string | number;
|
|
13
|
+
getSortableValue?: (row: TData) => any;
|
|
14
|
+
sortComparer?: (a: TData, b: TData, direction: SortDirection) => number;
|
|
11
15
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { IResponsiveTablePlugin, IPluginAPI } from './IResponsiveTablePlugin';
|
|
2
|
+
import IResponsiveTableColumnDefinition, { SortDirection } from '../Data/IResponsiveTableColumnDefinition';
|
|
3
|
+
export interface ISortPluginOptions<TData> {
|
|
4
|
+
initialSortColumn?: keyof TData;
|
|
5
|
+
initialSortDirection?: SortDirection;
|
|
6
|
+
}
|
|
7
|
+
export declare class SortPlugin<TData> implements IResponsiveTablePlugin<TData> {
|
|
8
|
+
id: string;
|
|
9
|
+
private api;
|
|
10
|
+
private sortColumn;
|
|
11
|
+
private sortDirection;
|
|
12
|
+
readonly comparers: {
|
|
13
|
+
numeric: (key: keyof TData) => (a: TData, b: TData, direction: SortDirection) => number;
|
|
14
|
+
caseInsensitiveString: (key: keyof TData) => (a: TData, b: TData, direction: SortDirection) => 0 | 1 | -1;
|
|
15
|
+
date: (key: keyof TData) => (a: TData, b: TData, direction: SortDirection) => number;
|
|
16
|
+
};
|
|
17
|
+
constructor(options?: ISortPluginOptions<TData>);
|
|
18
|
+
onPluginInit: (api: IPluginAPI<TData>) => void;
|
|
19
|
+
processData: (data: TData[]) => TData[];
|
|
20
|
+
getHeaderProps: (columnDef: IResponsiveTableColumnDefinition<TData>) => {
|
|
21
|
+
onClick?: undefined;
|
|
22
|
+
className?: undefined;
|
|
23
|
+
'aria-sort'?: undefined;
|
|
24
|
+
} | {
|
|
25
|
+
onClick: () => void;
|
|
26
|
+
className: string;
|
|
27
|
+
'aria-sort': string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -57,6 +57,7 @@ declare class ResponsiveTable<TData> extends Component<IProps<TData>, IState<TDa
|
|
|
57
57
|
private getRawColumnDefinition;
|
|
58
58
|
private onHeaderClickCallback;
|
|
59
59
|
private getClickableHeaderClassName;
|
|
60
|
+
private getHeaderProps;
|
|
60
61
|
private get rowClickFunction();
|
|
61
62
|
private get rowClickStyle();
|
|
62
63
|
private get tableFooter();
|
package/dist/index.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ import ResponsiveTable, { ColumnDefinition } from './UI/ResponsiveTable';
|
|
|
5
5
|
import { FilterPlugin } from './Plugins/FilterPlugin';
|
|
6
6
|
import { InfiniteScrollPlugin } from './Plugins/InfiniteScrollPlugin';
|
|
7
7
|
import { IResponsiveTablePlugin } from './Plugins/IResponsiveTablePlugin';
|
|
8
|
-
|
|
8
|
+
import { SortPlugin } from './Plugins/SortPlugin';
|
|
9
|
+
export { IResponsiveTableColumnDefinition, ColumnDefinition, IFooterColumnDefinition, IFooterRowDefinition, FilterPlugin, InfiniteScrollPlugin, IResponsiveTablePlugin, SortPlugin };
|
|
9
10
|
export default ResponsiveTable;
|
package/dist/index.js
CHANGED
|
@@ -31,8 +31,8 @@ function styleInject(css, ref) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
var css_248z = "/* Using CSS variables for a more maintainable and themeable design */\r\n.ResponsiveTable-module_responsiveTable__4y-Od {\r\n --table-border-color: #e0e0e0;\r\n --table-header-bg: #f8f9fa;\r\n --table-row-hover-bg: #e9ecef;\r\n --table-row-stripe-bg: #f2f2f2;\r\n --card-bg: #ffffff;\r\n --card-border-color: #e0e0e0;\r\n --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\r\n --text-color: #212529;\r\n --text-color-muted: #6c757d;\r\n --interactive-color: #0056b3;\r\n}\r\n\r\n/* Mobile Card View */\r\n.ResponsiveTable-module_card__b-U2v {\r\n background-color: var(--card-bg);\r\n border: 1px solid var(--card-border-color);\r\n margin-bottom: 1rem;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: var(--card-shadow);\r\n transition: box-shadow 0.2s ease-in-out;\r\n}\r\n\r\n.ResponsiveTable-module_card__b-U2v:hover {\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n/* This is not used in the component, but keeping it styled in case it's added back */\r\n.ResponsiveTable-module_card-header__Ttk51 {\r\n background-color: var(--table-header-bg);\r\n padding: 0.75rem 1rem;\r\n font-weight: 600; /* Bolder */\r\n border-bottom: 1px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_card-body__XIy0h {\r\n padding: 1rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.ResponsiveTable-module_card-body__XIy0h p {\r\n margin: 0 0 0.75rem;\r\n display: flex;\r\n justify-content: space-between;\r\n}\r\n\r\n.ResponsiveTable-module_card-body__XIy0h p:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.ResponsiveTable-module_card-label__v9L71 {\r\n font-weight: 600;\r\n color: var(--text-color);\r\n margin-right: 0.5rem;\r\n}\r\n\r\n/* Desktop Table View */\r\n.ResponsiveTable-module_responsiveTable__4y-Od {\r\n width: 100%;\r\n border-collapse: collapse;\r\n color: var(--text-color);\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od thead th {\r\n background-color: var(--table-header-bg);\r\n position: sticky;\r\n top: 0;\r\n z-index: 1;\r\n font-weight: 600;\r\n text-align: left;\r\n padding: 1rem;\r\n border-bottom: 2px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od td {\r\n padding: 1rem;\r\n border-bottom: 1px solid var(--table-border-color);\r\n text-align: left;\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od tr {\r\n background-color: var(--card-bg);\r\n transition: background-color 0.2s ease-in-out;\r\n}\r\n\r\n/* Subtle striping for better readability */\r\n.ResponsiveTable-module_responsiveTable__4y-Od tr:nth-child(even) {\r\n background-color: var(--table-row-stripe-bg);\r\n}\r\n\r\n/* Modern hover effect */\r\n.ResponsiveTable-module_responsiveTable__4y-Od tr:hover {\r\n background-color: var(--table-row-hover-bg);\r\n}\r\n\r\n/* Clickable Header Style */\r\n.ResponsiveTable-module_clickableHeader__xHQhF {\r\n cursor: pointer;\r\n color: var(--interactive-color);\r\n}\r\n\r\n.ResponsiveTable-module_clickableHeader__xHQhF:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od tfoot {\r\n background-color: var(--table-header-bg);\r\n border-top: 2px solid var(--table-border-color);\r\n font-weight: 600;\r\n}\r\n\r\n.ResponsiveTable-module_footerCell__8H-uG {\r\n padding: 1rem;\r\n text-align: right;\r\n font-weight: 600;\r\n}\r\n\r\n.ResponsiveTable-module_clickableFooterCell__WB9Ss {\r\n cursor: pointer;\r\n color: var(--interactive-color);\r\n}\r\n\r\n.ResponsiveTable-module_clickableFooterCell__WB9Ss:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.ResponsiveTable-module_footerCard__-NE2M {\r\n background-color: var(--table-header-bg);\r\n border: 1px solid var(--card-border-color);\r\n border-top: 2px solid var(--table-border-color);\r\n margin-bottom: 1rem;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: none;\r\n}\r\n\r\n.ResponsiveTable-module_footer-card-body__CtBMv {\r\n padding: 1rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.ResponsiveTable-module_footer-card-row__Vv6Ur {\r\n margin: 0 0 0.75rem;\r\n display: flex;\r\n justify-content: space-between;\r\n font-weight: 600;\r\n}\r\n\r\n/* No Data State */\r\n\r\n/* Staggered Entrance Animation */\r\n.ResponsiveTable-module_animatedRow__SFjrJ {\r\n animation: ResponsiveTable-module_fadeInUp__jMCS7 0.5s ease-out forwards;\r\n opacity: 0;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_fadeInUp__jMCS7 {\r\n from {\r\n opacity: 0;\r\n transform: translateY(20px);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* Skeleton Loader */\r\n.ResponsiveTable-module_skeleton__XxsXW {\r\n background-color: #e0e0e0;\r\n border-radius: 4px;\r\n position: relative;\r\n overflow: hidden;\r\n}\r\n\r\n.ResponsiveTable-module_skeleton__XxsXW::after {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: -150%;\r\n width: 150%;\r\n height: 100%;\r\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);\r\n animation: ResponsiveTable-module_shimmer__H8PhC 1.5s infinite;\r\n}\r\n\r\n.ResponsiveTable-module_skeletonText__T-Lgq {\r\n height: 1.2em;\r\n width: 100%;\r\n}\r\n\r\n.ResponsiveTable-module_skeletonCard__AYVwL {\r\n background-color: var(--card-bg);\r\n border: 1px solid var(--card-border-color);\r\n margin-bottom: 1rem;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n padding: 1rem;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_shimmer__H8PhC {\r\n 0% {\r\n transform: translateX(0);\r\n }\r\n 100% {\r\n transform: translateX(100%);\r\n }\r\n}\r\n.ResponsiveTable-module_noDataWrapper__Rj-k3 {\r\n color: var(--text-color-muted); /* Softer color */\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n height: 100%;\r\n gap: 10px;\r\n padding: 40px;\r\n background-color: var(--table-header-bg);\r\n border: 1px dashed var(--table-border-color);\r\n border-radius: 8px;\r\n}\r\n\r\n.ResponsiveTable-module_noData__IpwNq {\r\n text-align: center;\r\n font-weight: 500; /* Less aggressive than bold */\r\n font-size: 1rem;\r\n}\r\n\r\n.ResponsiveTable-module_row-exit__EVX6T {\r\n opacity: 0;\r\n transform: scaleY(0);\r\n transition: transform 0.3s ease-out, opacity 0.3s ease-out;\r\n}\r\n\r\n.ResponsiveTable-module_row-enter__YKgI4 {\r\n opacity: 0;\r\n transform: translateY(20px);\r\n transition: transform 0.5s ease-out, opacity 0.5s ease-out;\r\n}\r\n\r\n.ResponsiveTable-module_row-flash__a4NOm {\r\n animation: ResponsiveTable-module_flash__nxeAX 0.5s ease-out;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_flash__nxeAX {\r\n 0% {\r\n background-color: var(--table-row-hover-bg);\r\n }\r\n 100% {\r\n background-color: transparent;\r\n }\r\n}\r\n\r\n.ResponsiveTable-module_stickyHeader__-jjN- {\r\n position: sticky;\r\n top: 0;\r\n z-index: 1;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\r\n}\r\n";
|
|
35
|
-
var styles = {"responsiveTable":"ResponsiveTable-module_responsiveTable__4y-Od","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","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","stickyHeader":"ResponsiveTable-module_stickyHeader__-jjN-"};
|
|
34
|
+
var css_248z = "/* Using CSS variables for a more maintainable and themeable design */\r\n.ResponsiveTable-module_responsiveTable__4y-Od {\r\n --table-border-color: #e0e0e0;\r\n --table-header-bg: #f8f9fa;\r\n --table-row-hover-bg: #e9ecef;\r\n --table-row-stripe-bg: #f2f2f2;\r\n --card-bg: #ffffff;\r\n --card-border-color: #e0e0e0;\r\n --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\r\n --text-color: #212529;\r\n --text-color-muted: #6c757d;\r\n --interactive-color: #0056b3;\r\n}\r\n\r\n/* Mobile Card View */\r\n.ResponsiveTable-module_card__b-U2v {\r\n background-color: var(--card-bg);\r\n border: 1px solid var(--card-border-color);\r\n margin-bottom: 1rem;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: var(--card-shadow);\r\n transition: box-shadow 0.2s ease-in-out;\r\n}\r\n\r\n.ResponsiveTable-module_card__b-U2v:hover {\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n/* This is not used in the component, but keeping it styled in case it's added back */\r\n.ResponsiveTable-module_card-header__Ttk51 {\r\n background-color: var(--table-header-bg);\r\n padding: 0.75rem 1rem;\r\n font-weight: 600; /* Bolder */\r\n border-bottom: 1px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_card-body__XIy0h {\r\n padding: 1rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.ResponsiveTable-module_card-body__XIy0h p {\r\n margin: 0 0 0.75rem;\r\n display: flex;\r\n justify-content: space-between;\r\n}\r\n\r\n.ResponsiveTable-module_card-body__XIy0h p:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.ResponsiveTable-module_card-label__v9L71 {\r\n font-weight: 600;\r\n color: var(--text-color);\r\n margin-right: 0.5rem;\r\n}\r\n\r\n/* Desktop Table View */\r\n.ResponsiveTable-module_responsiveTable__4y-Od {\r\n width: 100%;\r\n border-collapse: collapse;\r\n color: var(--text-color);\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od thead th {\r\n background-color: var(--table-header-bg);\r\n position: sticky;\r\n top: 0;\r\n z-index: 1;\r\n font-weight: 600;\r\n text-align: left;\r\n padding: 1rem;\r\n border-bottom: 2px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od td {\r\n padding: 1rem;\r\n border-bottom: 1px solid var(--table-border-color);\r\n text-align: left;\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od tr {\r\n background-color: var(--card-bg);\r\n transition: background-color 0.2s ease-in-out;\r\n}\r\n\r\n/* Subtle striping for better readability */\r\n.ResponsiveTable-module_responsiveTable__4y-Od tr:nth-child(even) {\r\n background-color: var(--table-row-stripe-bg);\r\n}\r\n\r\n/* Modern hover effect */\r\n.ResponsiveTable-module_responsiveTable__4y-Od tr:hover {\r\n background-color: var(--table-row-hover-bg);\r\n}\r\n\r\n/* Clickable Header Style */\r\n.ResponsiveTable-module_clickableHeader__xHQhF {\r\n cursor: pointer;\r\n color: var(--interactive-color);\r\n}\r\n\r\n.ResponsiveTable-module_clickableHeader__xHQhF:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n/* Sortable Header Styles */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 {\r\n cursor: pointer;\r\n position: relative;\r\n padding-right: 2rem; /* Space for the icon */\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th .ResponsiveTable-module_sortIcon__A9WtD {\r\n position: absolute;\r\n right: 0.5rem;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n width: 1rem;\r\n height: 1rem;\r\n opacity: 0.4;\r\n transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60:hover .ResponsiveTable-module_sortIcon__A9WtD {\r\n opacity: 1;\r\n}\r\n\r\n/* Default unsorted icon (up and down arrows) */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 .ResponsiveTable-module_sortIcon__A9WtD::before,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 .ResponsiveTable-module_sortIcon__A9WtD::after {\r\n content: '';\r\n position: absolute;\r\n display: block;\r\n width: 0;\r\n height: 0;\r\n border-left: 0.4em solid transparent;\r\n border-right: 0.4em solid transparent;\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 .ResponsiveTable-module_sortIcon__A9WtD::before {\r\n border-bottom: 0.4em solid var(--text-color);\r\n top: 0.1em;\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 .ResponsiveTable-module_sortIcon__A9WtD::after {\r\n border-top: 0.4em solid var(--text-color);\r\n bottom: 0.1em;\r\n}\r\n\r\n/* Sorted Ascending Icon */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-asc__jzOIa .ResponsiveTable-module_sortIcon__A9WtD,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-desc__7WCFK .ResponsiveTable-module_sortIcon__A9WtD {\r\n opacity: 1;\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-asc__jzOIa .ResponsiveTable-module_sortIcon__A9WtD::before {\r\n border-bottom-color: var(--interactive-color);\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-asc__jzOIa .ResponsiveTable-module_sortIcon__A9WtD::after {\r\n opacity: 0.4;\r\n}\r\n\r\n/* Sorted Descending Icon */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-desc__7WCFK .ResponsiveTable-module_sortIcon__A9WtD::after {\r\n border-top-color: var(--interactive-color);\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-desc__7WCFK .ResponsiveTable-module_sortIcon__A9WtD::before {\r\n opacity: 0.4;\r\n}\r\n\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od tfoot {\r\n background-color: var(--table-header-bg);\r\n border-top: 2px solid var(--table-border-color);\r\n font-weight: 600;\r\n}\r\n\r\n.ResponsiveTable-module_footerCell__8H-uG {\r\n padding: 1rem;\r\n text-align: right;\r\n font-weight: 600;\r\n}\r\n\r\n.ResponsiveTable-module_clickableFooterCell__WB9Ss {\r\n cursor: pointer;\r\n color: var(--interactive-color);\r\n}\r\n\r\n.ResponsiveTable-module_clickableFooterCell__WB9Ss:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.ResponsiveTable-module_footerCard__-NE2M {\r\n background-color: var(--table-header-bg);\r\n border: 1px solid var(--card-border-color);\r\n border-top: 2px solid var(--table-border-color);\r\n margin-bottom: 1rem;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n box-shadow: none;\r\n}\r\n\r\n.ResponsiveTable-module_footer-card-body__CtBMv {\r\n padding: 1rem;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.ResponsiveTable-module_footer-card-row__Vv6Ur {\r\n margin: 0 0 0.75rem;\r\n display: flex;\r\n justify-content: space-between;\r\n font-weight: 600;\r\n}\r\n\r\n/* No Data State */\r\n\r\n/* Staggered Entrance Animation */\r\n.ResponsiveTable-module_animatedRow__SFjrJ {\r\n animation: ResponsiveTable-module_fadeInUp__jMCS7 0.5s ease-out forwards;\r\n opacity: 0;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_fadeInUp__jMCS7 {\r\n from {\r\n opacity: 0;\r\n transform: translateY(20px);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* Skeleton Loader */\r\n.ResponsiveTable-module_skeleton__XxsXW {\r\n background-color: #e0e0e0;\r\n border-radius: 4px;\r\n position: relative;\r\n overflow: hidden;\r\n}\r\n\r\n.ResponsiveTable-module_skeleton__XxsXW::after {\r\n content: '';\r\n position: absolute;\r\n top: 0;\r\n left: -150%;\r\n width: 150%;\r\n height: 100%;\r\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);\r\n animation: ResponsiveTable-module_shimmer__H8PhC 1.5s infinite;\r\n}\r\n\r\n.ResponsiveTable-module_skeletonText__T-Lgq {\r\n height: 1.2em;\r\n width: 100%;\r\n}\r\n\r\n.ResponsiveTable-module_skeletonCard__AYVwL {\r\n background-color: var(--card-bg);\r\n border: 1px solid var(--card-border-color);\r\n margin-bottom: 1rem;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n padding: 1rem;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_shimmer__H8PhC {\r\n 0% {\r\n transform: translateX(0);\r\n }\r\n 100% {\r\n transform: translateX(100%);\r\n }\r\n}\r\n.ResponsiveTable-module_noDataWrapper__Rj-k3 {\r\n color: var(--text-color-muted); /* Softer color */\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n height: 100%;\r\n gap: 10px;\r\n padding: 40px;\r\n background-color: var(--table-header-bg);\r\n border: 1px dashed var(--table-border-color);\r\n border-radius: 8px;\r\n}\r\n\r\n.ResponsiveTable-module_noData__IpwNq {\r\n text-align: center;\r\n font-weight: 500; /* Less aggressive than bold */\r\n font-size: 1rem;\r\n}\r\n\r\n.ResponsiveTable-module_row-exit__EVX6T {\r\n opacity: 0;\r\n transform: scaleY(0);\r\n transition: transform 0.3s ease-out, opacity 0.3s ease-out;\r\n}\r\n\r\n.ResponsiveTable-module_row-enter__YKgI4 {\r\n opacity: 0;\r\n transform: translateY(20px);\r\n transition: transform 0.5s ease-out, opacity 0.5s ease-out;\r\n}\r\n\r\n.ResponsiveTable-module_row-flash__a4NOm {\r\n animation: ResponsiveTable-module_flash__nxeAX 0.5s ease-out;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_flash__nxeAX {\r\n 0% {\r\n background-color: var(--table-row-hover-bg);\r\n }\r\n 100% {\r\n background-color: transparent;\r\n }\r\n}\r\n\r\n.ResponsiveTable-module_stickyHeader__-jjN- {\r\n position: sticky;\r\n top: 0;\r\n z-index: 1;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\r\n}\r\n";
|
|
35
|
+
var styles = {"responsiveTable":"ResponsiveTable-module_responsiveTable__4y-Od","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","clickableHeader":"ResponsiveTable-module_clickableHeader__xHQhF","sortIcon":"ResponsiveTable-module_sortIcon__A9WtD","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","stickyHeader":"ResponsiveTable-module_stickyHeader__-jjN-"};
|
|
36
36
|
styleInject(css_248z);
|
|
37
37
|
|
|
38
38
|
class FilterPlugin {
|
|
@@ -1126,6 +1126,17 @@ class ResponsiveTable extends React.Component {
|
|
|
1126
1126
|
: '';
|
|
1127
1127
|
return clickableHeaderClassName;
|
|
1128
1128
|
}
|
|
1129
|
+
getHeaderProps(columnDefinition) {
|
|
1130
|
+
let headerProps = {};
|
|
1131
|
+
if (this.props.plugins) {
|
|
1132
|
+
this.props.plugins.forEach((plugin) => {
|
|
1133
|
+
if (plugin.getHeaderProps) {
|
|
1134
|
+
Object.assign(headerProps, plugin.getHeaderProps(this.getRawColumnDefinition(columnDefinition)));
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
return headerProps;
|
|
1139
|
+
}
|
|
1129
1140
|
get rowClickFunction() {
|
|
1130
1141
|
if (this.props.onRowClick) {
|
|
1131
1142
|
return this.props.onRowClick;
|
|
@@ -1225,9 +1236,10 @@ class ResponsiveTable extends React.Component {
|
|
|
1225
1236
|
React.createElement("tr", null, this.props.columnDefinitions.map((columnDefinition, colIndex) => {
|
|
1226
1237
|
const onHeaderClickCallback = this.onHeaderClickCallback(columnDefinition);
|
|
1227
1238
|
const clickableHeaderClassName = this.getClickableHeaderClassName(onHeaderClickCallback, columnDefinition);
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1239
|
+
const headerProps = this.getHeaderProps(columnDefinition);
|
|
1240
|
+
return (React.createElement("th", Object.assign({ key: colIndex, className: `${clickableHeaderClassName}` }, headerProps),
|
|
1241
|
+
this.getColumnDefinition(columnDefinition, 0).displayLabel,
|
|
1242
|
+
React.createElement("span", { className: styles.sortIcon })));
|
|
1231
1243
|
}))),
|
|
1232
1244
|
React.createElement("tbody", null, ((_a = this.props.infiniteScrollProps) === null || _a === void 0 ? void 0 : _a.enableInfiniteScroll) ? (React.createElement(FixedSizeList, { height: fixedHeadersStyle.maxHeight ? (typeof fixedHeadersStyle.maxHeight === 'string' ? parseFloat(fixedHeadersStyle.maxHeight) : fixedHeadersStyle.maxHeight) : 500, itemCount: this.data.length, itemSize: 50, width: '100%', outerRef: this.tableContainerRef }, Row)) : (this.data.map((row, rowIndex) => {
|
|
1233
1245
|
var _a;
|
|
@@ -1243,6 +1255,10 @@ class ResponsiveTable extends React.Component {
|
|
|
1243
1255
|
}
|
|
1244
1256
|
return this.props.plugins.map((plugin) => {
|
|
1245
1257
|
if (plugin.renderHeader) {
|
|
1258
|
+
// For sort plugin, only render header in mobile view
|
|
1259
|
+
if (plugin.id === 'sort' && !this.state.isMobile) {
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1246
1262
|
return React.createElement("div", { key: plugin.id }, plugin.renderHeader());
|
|
1247
1263
|
}
|
|
1248
1264
|
return null;
|
|
@@ -1272,7 +1288,104 @@ class ResponsiveTable extends React.Component {
|
|
|
1272
1288
|
}
|
|
1273
1289
|
}
|
|
1274
1290
|
|
|
1291
|
+
// Type-safe sort comparer helpers
|
|
1292
|
+
const createSortComparers = () => ({
|
|
1293
|
+
numeric: (key) => (a, b, direction) => {
|
|
1294
|
+
const valA = a[key];
|
|
1295
|
+
const valB = b[key];
|
|
1296
|
+
return direction === 'asc' ? valA - valB : valB - valA;
|
|
1297
|
+
},
|
|
1298
|
+
caseInsensitiveString: (key) => (a, b, direction) => {
|
|
1299
|
+
const valA = a[key].toLowerCase();
|
|
1300
|
+
const valB = b[key].toLowerCase();
|
|
1301
|
+
if (valA < valB)
|
|
1302
|
+
return direction === 'asc' ? -1 : 1;
|
|
1303
|
+
if (valA > valB)
|
|
1304
|
+
return direction === 'asc' ? 1 : -1;
|
|
1305
|
+
return 0;
|
|
1306
|
+
},
|
|
1307
|
+
date: (key) => (a, b, direction) => {
|
|
1308
|
+
const dateA = new Date(a[key]).getTime();
|
|
1309
|
+
const dateB = new Date(b[key]).getTime();
|
|
1310
|
+
return direction === 'asc' ? dateA - dateB : dateB - dateA;
|
|
1311
|
+
},
|
|
1312
|
+
});
|
|
1313
|
+
class SortPlugin {
|
|
1314
|
+
constructor(options) {
|
|
1315
|
+
var _a, _b;
|
|
1316
|
+
this.id = 'sort';
|
|
1317
|
+
this.comparers = createSortComparers();
|
|
1318
|
+
this.onPluginInit = (api) => {
|
|
1319
|
+
this.api = api;
|
|
1320
|
+
};
|
|
1321
|
+
this.processData = (data) => {
|
|
1322
|
+
if (!this.sortColumn || !this.sortDirection) {
|
|
1323
|
+
return data;
|
|
1324
|
+
}
|
|
1325
|
+
const columnDef = this.api.columnDefinitions.find((col) => col.dataKey === this.sortColumn);
|
|
1326
|
+
if (!columnDef) {
|
|
1327
|
+
return data;
|
|
1328
|
+
}
|
|
1329
|
+
const sortedData = [...data].sort((a, b) => {
|
|
1330
|
+
if (columnDef.sortComparer) {
|
|
1331
|
+
return columnDef.sortComparer(a, b, this.sortDirection);
|
|
1332
|
+
}
|
|
1333
|
+
let aValue, bValue;
|
|
1334
|
+
if (columnDef.getSortableValue) {
|
|
1335
|
+
aValue = columnDef.getSortableValue(a);
|
|
1336
|
+
bValue = columnDef.getSortableValue(b);
|
|
1337
|
+
}
|
|
1338
|
+
else {
|
|
1339
|
+
aValue = a[this.sortColumn];
|
|
1340
|
+
bValue = b[this.sortColumn];
|
|
1341
|
+
}
|
|
1342
|
+
if (aValue < bValue)
|
|
1343
|
+
return this.sortDirection === 'asc' ? -1 : 1;
|
|
1344
|
+
if (aValue > bValue)
|
|
1345
|
+
return this.sortDirection === 'asc' ? 1 : -1;
|
|
1346
|
+
return 0;
|
|
1347
|
+
});
|
|
1348
|
+
return sortedData;
|
|
1349
|
+
};
|
|
1350
|
+
this.getHeaderProps = (columnDef) => {
|
|
1351
|
+
const { dataKey, sortComparer, getSortableValue } = columnDef;
|
|
1352
|
+
const isSortable = !!(sortComparer || getSortableValue);
|
|
1353
|
+
if (!isSortable || !dataKey) {
|
|
1354
|
+
return {};
|
|
1355
|
+
}
|
|
1356
|
+
const onHeaderClick = () => {
|
|
1357
|
+
if (this.sortColumn === dataKey) {
|
|
1358
|
+
if (this.sortDirection === 'desc') {
|
|
1359
|
+
this.sortColumn = null;
|
|
1360
|
+
this.sortDirection = null;
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
this.sortDirection = 'desc';
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
this.sortColumn = dataKey;
|
|
1368
|
+
this.sortDirection = 'asc';
|
|
1369
|
+
}
|
|
1370
|
+
this.api.forceUpdate();
|
|
1371
|
+
};
|
|
1372
|
+
let sortClassName = 'sortable';
|
|
1373
|
+
if (this.sortColumn === dataKey) {
|
|
1374
|
+
sortClassName = `sorted-${this.sortDirection}`;
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
onClick: onHeaderClick,
|
|
1378
|
+
className: sortClassName,
|
|
1379
|
+
'aria-sort': this.sortColumn === dataKey ? (this.sortDirection === 'asc' ? 'ascending' : 'descending') : 'none',
|
|
1380
|
+
};
|
|
1381
|
+
};
|
|
1382
|
+
this.sortColumn = (_a = options === null || options === void 0 ? void 0 : options.initialSortColumn) !== null && _a !== void 0 ? _a : null;
|
|
1383
|
+
this.sortDirection = (_b = options === null || options === void 0 ? void 0 : options.initialSortDirection) !== null && _b !== void 0 ? _b : null;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1275
1387
|
exports.FilterPlugin = FilterPlugin;
|
|
1276
1388
|
exports.InfiniteScrollPlugin = InfiniteScrollPlugin;
|
|
1389
|
+
exports.SortPlugin = SortPlugin;
|
|
1277
1390
|
exports.default = ResponsiveTable;
|
|
1278
1391
|
//# sourceMappingURL=index.js.map
|