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 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, infinite scrolling, or custom behaviors.
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: 'User', cellRenderer: (row) => <strong>{row.user}</strong> },
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
- import ResponsiveTable from 'jattac.libs.web.responsive-table';
288
- import { FilterPlugin } from 'jattac.libs.web.responsive-table/dist/Plugins/FilterPlugin'; // Adjust path as needed
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, { useState } from '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` | `string` | Yes | The label displayed in the table header. |
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 (optional). |
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` | No | A function that returns the string value to be used for filtering this column. Required for `FilterPlugin`. |
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
- export { IResponsiveTableColumnDefinition, ColumnDefinition, IFooterColumnDefinition, IFooterRowDefinition, FilterPlugin, InfiniteScrollPlugin, IResponsiveTablePlugin };
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
- return (React.createElement("th", { key: colIndex, className: `${clickableHeaderClassName}`, onClick: onHeaderClickCallback
1229
- ? () => onHeaderClickCallback(this.getColumnDefinition(columnDefinition, 0).interactivity.id)
1230
- : undefined }, this.getColumnDefinition(columnDefinition, 0).displayLabel));
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