jattac.libs.web.responsive-table 0.3.0 → 0.3.2
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 +660 -638
- package/dist/Data/IResponsiveTableColumnDefinition.d.ts +5 -1
- package/dist/Plugins/FilterPlugin.d.ts +1 -0
- package/dist/Plugins/SortPlugin.d.ts +3 -3
- package/dist/UI/InfiniteTable.d.ts +1 -2
- package/dist/UI/ResponsiveTable.d.ts +0 -1
- package/dist/index.js +155 -132
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,13 +3,17 @@ export type SortDirection = 'asc' | 'desc';
|
|
|
3
3
|
export type IResponsiveTableColumnDefinition<TData> = {
|
|
4
4
|
displayLabel: ReactNode;
|
|
5
5
|
cellRenderer: (data: TData) => ReactNode;
|
|
6
|
+
/**
|
|
7
|
+
* A unique identifier for the column. Required for sorting.
|
|
8
|
+
*/
|
|
9
|
+
columnId?: string;
|
|
6
10
|
interactivity?: {
|
|
7
11
|
id: string;
|
|
8
12
|
onHeaderClick?: (id: string) => void;
|
|
9
13
|
className?: string;
|
|
10
14
|
};
|
|
11
15
|
} & ({
|
|
12
|
-
dataKey
|
|
16
|
+
dataKey?: keyof TData;
|
|
13
17
|
getFilterableValue?: (data: TData) => string | number;
|
|
14
18
|
getSortableValue?: (row: TData) => string | number;
|
|
15
19
|
sortComparer?: (a: TData, b: TData, direction: SortDirection) => number;
|
|
@@ -4,6 +4,7 @@ export declare class FilterPlugin<TData> implements IResponsiveTablePlugin<TData
|
|
|
4
4
|
id: string;
|
|
5
5
|
private filterText;
|
|
6
6
|
private api;
|
|
7
|
+
private debounceTimeout;
|
|
7
8
|
constructor();
|
|
8
9
|
onPluginInit: (api: IPluginAPI<TData>) => void;
|
|
9
10
|
renderHeader: () => React.JSX.Element | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IResponsiveTablePlugin, IPluginAPI } from './IResponsiveTablePlugin';
|
|
2
2
|
import { IResponsiveTableColumnDefinition, SortDirection } from '../Data/IResponsiveTableColumnDefinition';
|
|
3
|
-
export interface ISortPluginOptions
|
|
4
|
-
initialSortColumn?:
|
|
3
|
+
export interface ISortPluginOptions {
|
|
4
|
+
initialSortColumn?: string;
|
|
5
5
|
initialSortDirection?: SortDirection;
|
|
6
6
|
}
|
|
7
7
|
export declare class SortPlugin<TData> implements IResponsiveTablePlugin<TData> {
|
|
@@ -14,7 +14,7 @@ export declare class SortPlugin<TData> implements IResponsiveTablePlugin<TData>
|
|
|
14
14
|
caseInsensitiveString: (key: keyof TData) => (a: TData, b: TData, direction: SortDirection) => 0 | 1 | -1;
|
|
15
15
|
date: (key: keyof TData) => (a: TData, b: TData, direction: SortDirection) => number;
|
|
16
16
|
};
|
|
17
|
-
constructor(options?: ISortPluginOptions
|
|
17
|
+
constructor(options?: ISortPluginOptions);
|
|
18
18
|
onPluginInit: (api: IPluginAPI<TData>) => void;
|
|
19
19
|
processData: (data: TData[]) => TData[];
|
|
20
20
|
getHeaderProps: (columnDef: IResponsiveTableColumnDefinition<TData>) => {
|
|
@@ -50,13 +50,12 @@ declare class InfiniteTable<TData> extends Component<IProps<TData>, IState<TData
|
|
|
50
50
|
private debouncedResize;
|
|
51
51
|
private tableContainerRef;
|
|
52
52
|
private headerRef;
|
|
53
|
-
private
|
|
53
|
+
private debouncedScrollHandler;
|
|
54
54
|
constructor(props: IProps<TData>);
|
|
55
55
|
componentDidUpdate(prevProps: IProps<TData>): void;
|
|
56
56
|
componentDidMount(): void;
|
|
57
57
|
componentWillUnmount(): void;
|
|
58
58
|
private debounce;
|
|
59
|
-
private throttle;
|
|
60
59
|
handleResize: () => void;
|
|
61
60
|
private handleScroll;
|
|
62
61
|
private loadMoreData;
|
|
@@ -59,7 +59,6 @@ declare class ResponsiveTable<TData> extends Component<IProps<TData>, IState<TDa
|
|
|
59
59
|
componentDidUpdate(prevProps: IProps<TData>): void;
|
|
60
60
|
private handleScroll;
|
|
61
61
|
private initializePlugins;
|
|
62
|
-
private processData;
|
|
63
62
|
handleResize: () => void;
|
|
64
63
|
private getColumnDefinition;
|
|
65
64
|
private getRawColumnDefinition;
|
package/dist/index.js
CHANGED
|
@@ -75,14 +75,15 @@ function styleInject(css, ref) {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
var css_248z$2 = "/* 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; /* Keep padding for the th itself */\r\n border-bottom: 2px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_headerInnerWrapper__3VAhD {\r\n display: flex; /* Use flexbox for alignment within the th */\r\n align-items: center; /* Vertically center content */\r\n justify-content: space-between; /* Push icon to the right */\r\n width: 100%; /* Ensure it takes full width of th */\r\n}\r\n\r\n.ResponsiveTable-module_headerContent__ODMzS {\r\n flex-grow: 1; /* Allow content to take available space */\r\n overflow: hidden; /* Hide overflowing content */\r\n text-overflow: ellipsis; /* Show ellipsis for truncated text */\r\n white-space: nowrap; /* Prevent text from wrapping */\r\n padding-right: 0.5rem; /* Subtle space between text and icon */\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.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-asc__jzOIa,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-desc__7WCFK {\r\n cursor: pointer;\r\n position: relative;\r\n padding-right: 2.5rem; /* Space for the icon (1rem icon + 1rem margin + 0.5rem right offset) */\r\n transition: transform 0.2s ease-in-out; /* Add transition for smooth scaling */\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60:hover,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-asc__jzOIa:hover,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-desc__7WCFK:hover {\r\n transform: scale(1.01); /* Subtle zoom effect on hover */\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 background-size: contain;\r\n background-repeat: no-repeat;\r\n background-position: center;\r\n opacity: 0.4; /* Ghosted by default */\r\n transition: opacity 0.2s ease-in-out;\r\n}\r\n\r\n/* Default unsorted icon (funnel) */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 .ResponsiveTable-module_sortIcon__A9WtD {\r\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%236c757d'%3E%3Cpath d='M10 18h4v-2h-4v2zm-6-8v2h16V8H4zm3-6h10v2H7V2z'/%3E%3C/svg%3E\");\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/* Ensure cursor applies to content within sortable header */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 > * {\r\n cursor: inherit;\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 opacity: 1; /* Always prominent when sorted */\r\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%230056b3'%3E%3Cpath d='M7 14l5-5 5 5z'/%3E%3C/svg%3E\");\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 {\r\n opacity: 1; /* Always prominent when sorted */\r\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%230056b3'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E\");\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/* Spinner for Loading More */\r\n.ResponsiveTable-module_spinner__Pn-3D {\r\n border: 4px solid rgba(0, 0, 0, 0.1);\r\n border-left-color: var(--interactive-color); /* Use theme color */\r\n border-radius: 50%;\r\n width: 24px;\r\n height: 24px;\r\n animation: ResponsiveTable-module_spin__i3NHn 1s linear infinite;\r\n display: inline-block;\r\n vertical-align: middle;\r\n margin-right: 8px;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_spin__i3NHn {\r\n 0% { transform: rotate(0deg); }\r\n 100% { transform: rotate(360deg); }\r\n}\r\n\r\n/* Loading/No More Data Container */\r\n.ResponsiveTable-module_infoContainer__b9IF5 {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n padding: 20px;\r\n color: var(--text-color-muted);\r\n font-size: 0.9rem;\r\n gap: 8px;\r\n background-color: var(--table-header-bg);\r\n border-top: 1px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_infoContainer__b9IF5.ResponsiveTable-module_noMoreData__he1rZ {\r\n font-weight: 500;\r\n}";
|
|
79
|
-
var styles$2 = {"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","headerInnerWrapper":"ResponsiveTable-module_headerInnerWrapper__3VAhD","headerContent":"ResponsiveTable-module_headerContent__ODMzS","clickableHeader":"ResponsiveTable-module_clickableHeader__xHQhF","sortable":"ResponsiveTable-module_sortable__yvA60","sorted-asc":"ResponsiveTable-module_sorted-asc__jzOIa","sorted-desc":"ResponsiveTable-module_sorted-desc__7WCFK","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","fadeInUp":"ResponsiveTable-module_fadeInUp__jMCS7","skeleton":"ResponsiveTable-module_skeleton__XxsXW","shimmer":"ResponsiveTable-module_shimmer__H8PhC","skeletonText":"ResponsiveTable-module_skeletonText__T-Lgq","skeletonCard":"ResponsiveTable-module_skeletonCard__AYVwL","noDataWrapper":"ResponsiveTable-module_noDataWrapper__Rj-k3","noData":"ResponsiveTable-module_noData__IpwNq","row-exit":"ResponsiveTable-module_row-exit__EVX6T","row-enter":"ResponsiveTable-module_row-enter__YKgI4","row-flash":"ResponsiveTable-module_row-flash__a4NOm","flash":"ResponsiveTable-module_flash__nxeAX","stickyHeader":"ResponsiveTable-module_stickyHeader__-jjN-","spinner":"ResponsiveTable-module_spinner__Pn-3D","spin":"ResponsiveTable-module_spin__i3NHn","infoContainer":"ResponsiveTable-module_infoContainer__b9IF5","noMoreData":"ResponsiveTable-module_noMoreData__he1rZ"};
|
|
78
|
+
var css_248z$2 = "/* 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; <-- REMOVED to prevent conflict with transform */\r\n /* top: 0; <-- REMOVED */\r\n z-index: 1;\r\n font-weight: 600;\r\n text-align: left;\r\n padding: 1rem; /* Keep padding for the th itself */\r\n border-bottom: 2px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_headerInnerWrapper__3VAhD {\r\n display: flex; /* Use flexbox for alignment within the th */\r\n align-items: center; /* Vertically center content */\r\n justify-content: space-between; /* Push icon to the right */\r\n width: 100%; /* Ensure it takes full width of th */\r\n}\r\n\r\n.ResponsiveTable-module_headerContent__ODMzS {\r\n flex-grow: 1; /* Allow content to take available space */\r\n overflow: hidden; /* Hide overflowing content */\r\n text-overflow: ellipsis; /* Show ellipsis for truncated text */\r\n white-space: nowrap; /* Prevent text from wrapping */\r\n padding-right: 0.5rem; /* Subtle space between text and icon */\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.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-asc__jzOIa,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-desc__7WCFK {\r\n cursor: pointer;\r\n position: relative;\r\n padding-right: 2.5rem; /* Space for the icon (1rem icon + 1rem margin + 0.5rem right offset) */\r\n transition: transform 0.2s ease-in-out; /* Add transition for smooth scaling */\r\n}\r\n\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60:hover,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-asc__jzOIa:hover,\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sorted-desc__7WCFK:hover {\r\n transform: scale(1.01); /* Subtle zoom effect on hover */\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 background-size: contain;\r\n background-repeat: no-repeat;\r\n background-position: center;\r\n opacity: 0.4; /* Ghosted by default */\r\n transition: opacity 0.2s ease-in-out;\r\n}\r\n\r\n/* Default unsorted icon (funnel) */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 .ResponsiveTable-module_sortIcon__A9WtD {\r\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%236c757d'%3E%3Cpath d='M10 18h4v-2h-4v2zm-6-8v2h16V8H4zm3-6h10v2H7V2z'/%3E%3C/svg%3E\");\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/* Ensure cursor applies to content within sortable header */\r\n.ResponsiveTable-module_responsiveTable__4y-Od th.ResponsiveTable-module_sortable__yvA60 > * {\r\n cursor: inherit;\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 opacity: 1; /* Always prominent when sorted */\r\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%230056b3'%3E%3Cpath d='M7 14l5-5 5 5z'/%3E%3C/svg%3E\");\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 {\r\n opacity: 1; /* Always prominent when sorted */\r\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%230056b3'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E\");\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- th {\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\r\n.ResponsiveTable-module_internalStickyHeader__idiJY th {\r\n position: sticky;\r\n top: 0;\r\n z-index: 1;\r\n}\r\n/* Spinner for Loading More */\r\n.ResponsiveTable-module_spinner__Pn-3D {\r\n border: 4px solid rgba(0, 0, 0, 0.1);\r\n border-left-color: var(--interactive-color); /* Use theme color */\r\n border-radius: 50%;\r\n width: 24px;\r\n height: 24px;\r\n animation: ResponsiveTable-module_spin__i3NHn 1s linear infinite;\r\n display: inline-block;\r\n vertical-align: middle;\r\n margin-right: 8px;\r\n}\r\n\r\n@keyframes ResponsiveTable-module_spin__i3NHn {\r\n 0% { transform: rotate(0deg); }\r\n 100% { transform: rotate(360deg); }\r\n}\r\n\r\n/* Loading/No More Data Container */\r\n.ResponsiveTable-module_infoContainer__b9IF5 {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n padding: 20px;\r\n color: var(--text-color-muted);\r\n font-size: 0.9rem;\r\n gap: 8px;\r\n background-color: var(--table-header-bg);\r\n border-top: 1px solid var(--table-border-color);\r\n}\r\n\r\n.ResponsiveTable-module_infoContainer__b9IF5.ResponsiveTable-module_noMoreData__he1rZ {\r\n font-weight: 500;\r\n}";
|
|
79
|
+
var styles$2 = {"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","headerInnerWrapper":"ResponsiveTable-module_headerInnerWrapper__3VAhD","headerContent":"ResponsiveTable-module_headerContent__ODMzS","clickableHeader":"ResponsiveTable-module_clickableHeader__xHQhF","sortable":"ResponsiveTable-module_sortable__yvA60","sorted-asc":"ResponsiveTable-module_sorted-asc__jzOIa","sorted-desc":"ResponsiveTable-module_sorted-desc__7WCFK","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","fadeInUp":"ResponsiveTable-module_fadeInUp__jMCS7","skeleton":"ResponsiveTable-module_skeleton__XxsXW","shimmer":"ResponsiveTable-module_shimmer__H8PhC","skeletonText":"ResponsiveTable-module_skeletonText__T-Lgq","skeletonCard":"ResponsiveTable-module_skeletonCard__AYVwL","noDataWrapper":"ResponsiveTable-module_noDataWrapper__Rj-k3","noData":"ResponsiveTable-module_noData__IpwNq","row-exit":"ResponsiveTable-module_row-exit__EVX6T","row-enter":"ResponsiveTable-module_row-enter__YKgI4","row-flash":"ResponsiveTable-module_row-flash__a4NOm","flash":"ResponsiveTable-module_flash__nxeAX","stickyHeader":"ResponsiveTable-module_stickyHeader__-jjN-","internalStickyHeader":"ResponsiveTable-module_internalStickyHeader__idiJY","spinner":"ResponsiveTable-module_spinner__Pn-3D","spin":"ResponsiveTable-module_spin__i3NHn","infoContainer":"ResponsiveTable-module_infoContainer__b9IF5","noMoreData":"ResponsiveTable-module_noMoreData__he1rZ"};
|
|
80
80
|
styleInject(css_248z$2);
|
|
81
81
|
|
|
82
82
|
class FilterPlugin {
|
|
83
83
|
constructor() {
|
|
84
84
|
this.id = 'filter';
|
|
85
85
|
this.filterText = '';
|
|
86
|
+
this.debounceTimeout = null;
|
|
86
87
|
this.onPluginInit = (api) => {
|
|
87
88
|
this.api = api;
|
|
88
89
|
};
|
|
@@ -121,68 +122,18 @@ class FilterPlugin {
|
|
|
121
122
|
});
|
|
122
123
|
};
|
|
123
124
|
this.handleFilterChange = (e) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.
|
|
125
|
+
const currentFilterText = e.target.value;
|
|
126
|
+
if (this.debounceTimeout) {
|
|
127
|
+
clearTimeout(this.debounceTimeout);
|
|
128
|
+
}
|
|
129
|
+
this.debounceTimeout = setTimeout(() => {
|
|
130
|
+
this.filterText = currentFilterText;
|
|
127
131
|
this.api.forceUpdate();
|
|
128
132
|
}, 300);
|
|
129
133
|
};
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
|
|
133
|
-
class InfiniteScrollPlugin {
|
|
134
|
-
constructor() {
|
|
135
|
-
this.id = 'infinite-scroll';
|
|
136
|
-
this.isLoadingMore = false;
|
|
137
|
-
this.onPluginInit = (api) => {
|
|
138
|
-
this.api = api;
|
|
139
|
-
this.attachScrollListener();
|
|
140
|
-
};
|
|
141
|
-
this.attachScrollListener = () => {
|
|
142
|
-
var _a, _b;
|
|
143
|
-
const scrollableElement = (_b = (_a = this.api).getScrollableElement) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
144
|
-
if (scrollableElement) {
|
|
145
|
-
scrollableElement.addEventListener('scroll', this.handleScroll);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
this.handleScroll = () => __awaiter(this, void 0, void 0, function* () {
|
|
149
|
-
var _a, _b, _c, _d, _e;
|
|
150
|
-
const scrollableElement = (_b = (_a = this.api).getScrollableElement) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
151
|
-
if (!scrollableElement || !((_c = this.api.infiniteScrollProps) === null || _c === void 0 ? void 0 : _c.enableInfiniteScroll)) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const { scrollTop, scrollHeight, clientHeight } = scrollableElement;
|
|
155
|
-
const scrollThreshold = 200; // Load more data when 200px from the bottom
|
|
156
|
-
if (scrollHeight - scrollTop - clientHeight < scrollThreshold &&
|
|
157
|
-
this.api.infiniteScrollProps.hasMore &&
|
|
158
|
-
!this.isLoadingMore) {
|
|
159
|
-
this.isLoadingMore = true;
|
|
160
|
-
this.api.forceUpdate(); // Trigger re-render to show loading component
|
|
161
|
-
yield ((_e = (_d = this.api.infiniteScrollProps).onLoadMore) === null || _e === void 0 ? void 0 : _e.call(_d, this.api.getData()));
|
|
162
|
-
this.isLoadingMore = false;
|
|
163
|
-
this.api.forceUpdate(); // Trigger re-render to hide loading component
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
this.processData = (data) => {
|
|
167
|
-
// This plugin doesn't modify the data directly, but rather triggers loading more.
|
|
168
|
-
// The main component's data prop should be updated by the consumer of the table.
|
|
169
|
-
return data;
|
|
170
|
-
};
|
|
171
|
-
this.renderFooter = () => {
|
|
172
|
-
if (!this.api.infiniteScrollProps) {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
if (this.isLoadingMore) {
|
|
176
|
-
return this.api.infiniteScrollProps.loadingMoreComponent || React.createElement("div", null, "Loading more...");
|
|
177
|
-
}
|
|
178
|
-
else if (!this.api.infiniteScrollProps.hasMore) {
|
|
179
|
-
return this.api.infiniteScrollProps.noMoreDataComponent || React.createElement("div", null, "No more data.");
|
|
180
|
-
}
|
|
181
|
-
return null;
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
137
|
var css_248z$1 = "/* Spinner for Loading More */\r\n.LoadingSpinner-module_spinner__F9V3x {\r\n border: 4px solid rgba(0, 0, 0, 0.1);\r\n border-left-color: var(--interactive-color); /* Use theme color */\r\n border-radius: 50%;\r\n width: 24px;\r\n height: 24px;\r\n animation: LoadingSpinner-module_spin__VkBDO 1s linear infinite;\r\n display: inline-block;\r\n vertical-align: middle;\r\n margin-right: 8px;\r\n}\r\n\r\n@keyframes LoadingSpinner-module_spin__VkBDO {\r\n 0% { transform: rotate(0deg); }\r\n 100% { transform: rotate(360deg); }\r\n}";
|
|
187
138
|
var styles$1 = {"spinner":"LoadingSpinner-module_spinner__F9V3x"};
|
|
188
139
|
styleInject(css_248z$1);
|
|
@@ -207,16 +158,6 @@ class InfiniteTable extends React.Component {
|
|
|
207
158
|
super(props);
|
|
208
159
|
this.tableContainerRef = React.createRef();
|
|
209
160
|
this.headerRef = React.createRef();
|
|
210
|
-
this.throttle = (func, limit) => {
|
|
211
|
-
let inThrottle;
|
|
212
|
-
return (event) => {
|
|
213
|
-
if (!inThrottle) {
|
|
214
|
-
func(event);
|
|
215
|
-
inThrottle = true;
|
|
216
|
-
setTimeout(() => (inThrottle = false), limit);
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
};
|
|
220
161
|
this.handleResize = () => {
|
|
221
162
|
this.setState({
|
|
222
163
|
isMobile: window.innerWidth <= (this.props.mobileBreakpoint || 600),
|
|
@@ -247,19 +188,27 @@ class InfiniteTable extends React.Component {
|
|
|
247
188
|
return;
|
|
248
189
|
this.setState({ isLoadingMore: true }, () => __awaiter(this, void 0, void 0, function* () {
|
|
249
190
|
var _a, _b;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
|
|
191
|
+
try {
|
|
192
|
+
const newItems = yield ((_a = infiniteScrollProps === null || infiniteScrollProps === void 0 ? void 0 : infiniteScrollProps.onLoadMore) === null || _a === void 0 ? void 0 : _a.call(infiniteScrollProps, this.state.internalData));
|
|
193
|
+
if (((_b = this.props.infiniteScrollProps) === null || _b === void 0 ? void 0 : _b.hasMore) === undefined) {
|
|
194
|
+
if (!newItems || newItems.length === 0) {
|
|
195
|
+
this.setState({ internalHasMore: false });
|
|
196
|
+
}
|
|
254
197
|
}
|
|
198
|
+
if (newItems && newItems.length > 0) {
|
|
199
|
+
const newInternalData = [...this.state.internalData, ...newItems];
|
|
200
|
+
this.setState({ internalData: newInternalData }, () => {
|
|
201
|
+
this.processData(); // Re-process data after new items are added
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error("Failed to load more items for infinite scroll:", error);
|
|
207
|
+
// Optionally, you could add a state to show an error message to the user
|
|
255
208
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.setState({ internalData: newInternalData }, () => {
|
|
259
|
-
this.processData(); // Re-process data after new items are added
|
|
260
|
-
});
|
|
209
|
+
finally {
|
|
210
|
+
this.setState({ isLoadingMore: false });
|
|
261
211
|
}
|
|
262
|
-
this.setState({ isLoadingMore: false });
|
|
263
212
|
}));
|
|
264
213
|
};
|
|
265
214
|
this.processData = () => {
|
|
@@ -312,11 +261,18 @@ class InfiniteTable extends React.Component {
|
|
|
312
261
|
activePlugins: [],
|
|
313
262
|
};
|
|
314
263
|
this.debouncedResize = this.debounce(this.handleResize, 200);
|
|
315
|
-
this.
|
|
264
|
+
this.debouncedScrollHandler = this.debounce(this.handleScroll, 200);
|
|
316
265
|
}
|
|
317
266
|
componentDidUpdate(prevProps) {
|
|
267
|
+
const propsHaveChanged = prevProps.plugins !== this.props.plugins ||
|
|
268
|
+
prevProps.filterProps !== this.props.filterProps;
|
|
318
269
|
if (prevProps.data !== this.props.data) {
|
|
319
|
-
|
|
270
|
+
// If initial data prop changes, reset internal state and re-initialize plugins
|
|
271
|
+
this.setState({ internalData: this.props.data }, () => this.initializePlugins());
|
|
272
|
+
}
|
|
273
|
+
else if (propsHaveChanged) {
|
|
274
|
+
// If only plugins or filter props change, just re-initialize them
|
|
275
|
+
this.initializePlugins();
|
|
320
276
|
}
|
|
321
277
|
}
|
|
322
278
|
componentDidMount() {
|
|
@@ -332,9 +288,11 @@ class InfiniteTable extends React.Component {
|
|
|
332
288
|
}
|
|
333
289
|
debounce(func, delay) {
|
|
334
290
|
let timeout;
|
|
335
|
-
return () => {
|
|
291
|
+
return (...args) => {
|
|
336
292
|
clearTimeout(timeout);
|
|
337
|
-
timeout = setTimeout(() =>
|
|
293
|
+
timeout = setTimeout(() => {
|
|
294
|
+
func(...args);
|
|
295
|
+
}, delay);
|
|
338
296
|
};
|
|
339
297
|
}
|
|
340
298
|
initializePlugins() {
|
|
@@ -399,7 +357,7 @@ class InfiniteTable extends React.Component {
|
|
|
399
357
|
const { infiniteScrollProps } = this.props;
|
|
400
358
|
const { isLoadingMore } = this.state;
|
|
401
359
|
const hasMore = ((_a = this.props.infiniteScrollProps) === null || _a === void 0 ? void 0 : _a.hasMore) !== undefined ? this.props.infiniteScrollProps.hasMore : this.state.internalHasMore;
|
|
402
|
-
return (React.createElement("div", { ref: this.tableContainerRef, onScroll: this.
|
|
360
|
+
return (React.createElement("div", { ref: this.tableContainerRef, onScroll: this.debouncedScrollHandler, style: { maxHeight: this.props.maxHeight, overflowY: 'auto' } },
|
|
403
361
|
React.createElement("table", { className: styles$2['responsiveTable'] },
|
|
404
362
|
React.createElement("thead", { ref: this.headerRef, className: this.state.isHeaderSticky ? styles$2.stickyHeader : '' },
|
|
405
363
|
React.createElement("tr", null, this.props.columnDefinitions.map((colDef, colIndex) => {
|
|
@@ -505,12 +463,15 @@ class ResponsiveTable extends React.Component {
|
|
|
505
463
|
}
|
|
506
464
|
}
|
|
507
465
|
componentDidUpdate(prevProps) {
|
|
508
|
-
if
|
|
509
|
-
|
|
466
|
+
// Re-initialize if data or plugin configuration changes.
|
|
467
|
+
if (prevProps.data !== this.props.data ||
|
|
468
|
+
prevProps.plugins !== this.props.plugins ||
|
|
469
|
+
prevProps.filterProps !== this.props.filterProps) {
|
|
470
|
+
this.initializePlugins();
|
|
510
471
|
}
|
|
511
472
|
}
|
|
512
473
|
initializePlugins() {
|
|
513
|
-
var _a
|
|
474
|
+
var _a;
|
|
514
475
|
const activePlugins = [];
|
|
515
476
|
// Add explicitly provided plugins first
|
|
516
477
|
if (this.props.plugins) {
|
|
@@ -520,15 +481,11 @@ class ResponsiveTable extends React.Component {
|
|
|
520
481
|
if (((_a = this.props.filterProps) === null || _a === void 0 ? void 0 : _a.showFilter) && !activePlugins.some(p => p.id === 'filter')) {
|
|
521
482
|
activePlugins.push(new FilterPlugin());
|
|
522
483
|
}
|
|
523
|
-
// Automatically add InfiniteScrollPlugin if infiniteScrollProps are provided and not already present
|
|
524
|
-
if (((_b = this.props.infiniteScrollProps) === null || _b === void 0 ? void 0 : _b.enableInfiniteScroll) && !activePlugins.some(p => p.id === 'infinite-scroll')) {
|
|
525
|
-
activePlugins.push(new InfiniteScrollPlugin());
|
|
526
|
-
}
|
|
527
484
|
activePlugins.forEach((plugin) => {
|
|
528
485
|
if (plugin.onPluginInit) {
|
|
529
486
|
plugin.onPluginInit({
|
|
530
487
|
getData: () => this.props.data,
|
|
531
|
-
forceUpdate: () => this.
|
|
488
|
+
forceUpdate: () => this.initializePlugins(),
|
|
532
489
|
getScrollableElement: () => this.tableContainerRef.current,
|
|
533
490
|
infiniteScrollProps: this.props.infiniteScrollProps,
|
|
534
491
|
filterProps: this.props.filterProps,
|
|
@@ -545,17 +502,6 @@ class ResponsiveTable extends React.Component {
|
|
|
545
502
|
});
|
|
546
503
|
this.setState({ processedData });
|
|
547
504
|
}
|
|
548
|
-
processData() {
|
|
549
|
-
let processedData = [...this.props.data];
|
|
550
|
-
if (this.props.plugins) {
|
|
551
|
-
this.props.plugins.forEach((plugin) => {
|
|
552
|
-
if (plugin.processData) {
|
|
553
|
-
processedData = plugin.processData(processedData);
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
this.setState({ processedData });
|
|
558
|
-
}
|
|
559
505
|
getColumnDefinition(columnDefinition, rowIndex) {
|
|
560
506
|
if (!this.hasData) {
|
|
561
507
|
return { displayLabel: '', cellRenderer: () => '' };
|
|
@@ -683,9 +629,12 @@ class ResponsiveTable extends React.Component {
|
|
|
683
629
|
const fixedHeadersStyle = useFixedHeaders
|
|
684
630
|
? { maxHeight: this.props.maxHeight, overflowY: 'auto' }
|
|
685
631
|
: {};
|
|
632
|
+
const headerClassName = useFixedHeaders
|
|
633
|
+
? styles$2.internalStickyHeader
|
|
634
|
+
: (this.state.isHeaderSticky ? styles$2.stickyHeader : '');
|
|
686
635
|
return (React.createElement("div", { style: fixedHeadersStyle, ref: this.tableContainerRef },
|
|
687
636
|
React.createElement("table", { className: styles$2['responsiveTable'] },
|
|
688
|
-
React.createElement("thead", { ref: this.headerRef, className:
|
|
637
|
+
React.createElement("thead", { ref: this.headerRef, className: headerClassName },
|
|
689
638
|
React.createElement("tr", null, this.props.columnDefinitions.map((columnDefinition, colIndex) => {
|
|
690
639
|
const onHeaderClickCallback = this.onHeaderClickCallback(columnDefinition);
|
|
691
640
|
const clickableHeaderClassName = this.getClickableHeaderClassName(onHeaderClickCallback, columnDefinition);
|
|
@@ -750,16 +699,78 @@ class ResponsiveTable extends React.Component {
|
|
|
750
699
|
}
|
|
751
700
|
}
|
|
752
701
|
|
|
702
|
+
class InfiniteScrollPlugin {
|
|
703
|
+
constructor() {
|
|
704
|
+
this.id = 'infinite-scroll';
|
|
705
|
+
this.isLoadingMore = false;
|
|
706
|
+
this.onPluginInit = (api) => {
|
|
707
|
+
this.api = api;
|
|
708
|
+
this.attachScrollListener();
|
|
709
|
+
};
|
|
710
|
+
this.attachScrollListener = () => {
|
|
711
|
+
var _a, _b;
|
|
712
|
+
const scrollableElement = (_b = (_a = this.api).getScrollableElement) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
713
|
+
if (scrollableElement) {
|
|
714
|
+
scrollableElement.addEventListener('scroll', this.handleScroll);
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
this.handleScroll = () => __awaiter(this, void 0, void 0, function* () {
|
|
718
|
+
var _a, _b, _c, _d, _e;
|
|
719
|
+
const scrollableElement = (_b = (_a = this.api).getScrollableElement) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
720
|
+
if (!scrollableElement || !((_c = this.api.infiniteScrollProps) === null || _c === void 0 ? void 0 : _c.enableInfiniteScroll)) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const { scrollTop, scrollHeight, clientHeight } = scrollableElement;
|
|
724
|
+
const scrollThreshold = 200; // Load more data when 200px from the bottom
|
|
725
|
+
if (scrollHeight - scrollTop - clientHeight < scrollThreshold &&
|
|
726
|
+
this.api.infiniteScrollProps.hasMore &&
|
|
727
|
+
!this.isLoadingMore) {
|
|
728
|
+
this.isLoadingMore = true;
|
|
729
|
+
this.api.forceUpdate(); // Trigger re-render to show loading component
|
|
730
|
+
yield ((_e = (_d = this.api.infiniteScrollProps).onLoadMore) === null || _e === void 0 ? void 0 : _e.call(_d, this.api.getData()));
|
|
731
|
+
this.isLoadingMore = false;
|
|
732
|
+
this.api.forceUpdate(); // Trigger re-render to hide loading component
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
this.processData = (data) => {
|
|
736
|
+
// This plugin doesn't modify the data directly, but rather triggers loading more.
|
|
737
|
+
// The main component's data prop should be updated by the consumer of the table.
|
|
738
|
+
return data;
|
|
739
|
+
};
|
|
740
|
+
this.renderFooter = () => {
|
|
741
|
+
if (!this.api.infiniteScrollProps) {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
if (this.isLoadingMore) {
|
|
745
|
+
return this.api.infiniteScrollProps.loadingMoreComponent || React.createElement("div", null, "Loading more...");
|
|
746
|
+
}
|
|
747
|
+
else if (!this.api.infiniteScrollProps.hasMore) {
|
|
748
|
+
return this.api.infiniteScrollProps.noMoreDataComponent || React.createElement("div", null, "No more data.");
|
|
749
|
+
}
|
|
750
|
+
return null;
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
753
755
|
// Type-safe sort comparer helpers
|
|
754
756
|
const createSortComparers = () => ({
|
|
755
757
|
numeric: (key) => (a, b, direction) => {
|
|
756
|
-
const
|
|
757
|
-
const
|
|
758
|
-
|
|
758
|
+
const numA = parseFloat(String(a[key]));
|
|
759
|
+
const numB = parseFloat(String(b[key]));
|
|
760
|
+
const aIsNaN = isNaN(numA);
|
|
761
|
+
const bIsNaN = isNaN(numB);
|
|
762
|
+
if (aIsNaN && bIsNaN)
|
|
763
|
+
return 0;
|
|
764
|
+
if (aIsNaN)
|
|
765
|
+
return 1; // Put non-numbers at the end
|
|
766
|
+
if (bIsNaN)
|
|
767
|
+
return -1;
|
|
768
|
+
return direction === 'asc' ? numA - numB : numB - numA;
|
|
759
769
|
},
|
|
760
770
|
caseInsensitiveString: (key) => (a, b, direction) => {
|
|
761
|
-
|
|
762
|
-
const
|
|
771
|
+
var _a, _b;
|
|
772
|
+
const valA = String((_a = a[key]) !== null && _a !== void 0 ? _a : '').toLowerCase();
|
|
773
|
+
const valB = String((_b = b[key]) !== null && _b !== void 0 ? _b : '').toLowerCase();
|
|
763
774
|
if (valA < valB)
|
|
764
775
|
return direction === 'asc' ? -1 : 1;
|
|
765
776
|
if (valA > valB)
|
|
@@ -767,8 +778,16 @@ const createSortComparers = () => ({
|
|
|
767
778
|
return 0;
|
|
768
779
|
},
|
|
769
780
|
date: (key) => (a, b, direction) => {
|
|
770
|
-
const dateA = new Date(a[key]).getTime();
|
|
771
|
-
const dateB = new Date(b[key]).getTime();
|
|
781
|
+
const dateA = new Date(String(a[key])).getTime();
|
|
782
|
+
const dateB = new Date(String(b[key])).getTime();
|
|
783
|
+
const aIsNaN = isNaN(dateA);
|
|
784
|
+
const bIsNaN = isNaN(dateB);
|
|
785
|
+
if (aIsNaN && bIsNaN)
|
|
786
|
+
return 0;
|
|
787
|
+
if (aIsNaN)
|
|
788
|
+
return 1; // Put invalid dates at the end
|
|
789
|
+
if (bIsNaN)
|
|
790
|
+
return -1;
|
|
772
791
|
return direction === 'asc' ? dateA - dateB : dateB - dateA;
|
|
773
792
|
},
|
|
774
793
|
});
|
|
@@ -784,43 +803,47 @@ class SortPlugin {
|
|
|
784
803
|
if (!this.sortColumn || !this.sortDirection) {
|
|
785
804
|
return data;
|
|
786
805
|
}
|
|
787
|
-
const columnDef = this.api.columnDefinitions.find((col) => ('
|
|
788
|
-
if (!columnDef
|
|
806
|
+
const columnDef = this.api.columnDefinitions.find((col) => (typeof col === 'object' && col.columnId === this.sortColumn));
|
|
807
|
+
if (!columnDef) {
|
|
789
808
|
return data;
|
|
790
809
|
}
|
|
791
810
|
const sortedData = [...data].sort((a, b) => {
|
|
792
811
|
if ('sortComparer' in columnDef && columnDef.sortComparer) {
|
|
793
812
|
return columnDef.sortComparer(a, b, this.sortDirection);
|
|
794
813
|
}
|
|
795
|
-
let aValue, bValue;
|
|
796
814
|
if ('getSortableValue' in columnDef && columnDef.getSortableValue) {
|
|
797
|
-
aValue = columnDef.getSortableValue(a);
|
|
798
|
-
bValue = columnDef.getSortableValue(b);
|
|
815
|
+
const aValue = columnDef.getSortableValue(a);
|
|
816
|
+
const bValue = columnDef.getSortableValue(b);
|
|
817
|
+
if (aValue < bValue)
|
|
818
|
+
return this.sortDirection === 'asc' ? -1 : 1;
|
|
819
|
+
if (aValue > bValue)
|
|
820
|
+
return this.sortDirection === 'asc' ? 1 : -1;
|
|
821
|
+
return 0;
|
|
799
822
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
823
|
+
// Fallback to dataKey if it exists and no other sorter is provided
|
|
824
|
+
if ('dataKey' in columnDef && columnDef.dataKey) {
|
|
825
|
+
const key = columnDef.dataKey;
|
|
826
|
+
const aValue = a[key];
|
|
827
|
+
const bValue = b[key];
|
|
828
|
+
if (aValue < bValue)
|
|
829
|
+
return this.sortDirection === 'asc' ? -1 : 1;
|
|
830
|
+
if (aValue > bValue)
|
|
831
|
+
return this.sortDirection === 'asc' ? 1 : -1;
|
|
832
|
+
return 0;
|
|
803
833
|
}
|
|
804
|
-
if (aValue < bValue)
|
|
805
|
-
return this.sortDirection === 'asc' ? -1 : 1;
|
|
806
|
-
if (aValue > bValue)
|
|
807
|
-
return this.sortDirection === 'asc' ? 1 : -1;
|
|
808
834
|
return 0;
|
|
809
835
|
});
|
|
810
836
|
return sortedData;
|
|
811
837
|
};
|
|
812
838
|
this.getHeaderProps = (columnDef) => {
|
|
813
|
-
|
|
814
|
-
if (!('dataKey' in columnDef)) {
|
|
815
|
-
return {};
|
|
816
|
-
}
|
|
817
|
-
const { dataKey } = columnDef;
|
|
839
|
+
const { columnId } = columnDef;
|
|
818
840
|
const isSortable = ('sortComparer' in columnDef && !!columnDef.sortComparer) || ('getSortableValue' in columnDef && !!columnDef.getSortableValue);
|
|
819
|
-
|
|
841
|
+
// A column must have a columnId and a sort function to be sortable
|
|
842
|
+
if (!isSortable || !columnId) {
|
|
820
843
|
return {};
|
|
821
844
|
}
|
|
822
845
|
const onHeaderClick = () => {
|
|
823
|
-
if (this.sortColumn ===
|
|
846
|
+
if (this.sortColumn === columnId) {
|
|
824
847
|
if (this.sortDirection === 'desc') {
|
|
825
848
|
this.sortColumn = null;
|
|
826
849
|
this.sortDirection = null;
|
|
@@ -830,19 +853,19 @@ class SortPlugin {
|
|
|
830
853
|
}
|
|
831
854
|
}
|
|
832
855
|
else {
|
|
833
|
-
this.sortColumn =
|
|
856
|
+
this.sortColumn = columnId;
|
|
834
857
|
this.sortDirection = 'asc';
|
|
835
858
|
}
|
|
836
859
|
this.api.forceUpdate();
|
|
837
860
|
};
|
|
838
861
|
let sortClassName = 'sortable';
|
|
839
|
-
if (this.sortColumn ===
|
|
862
|
+
if (this.sortColumn === columnId) {
|
|
840
863
|
sortClassName = `sorted-${this.sortDirection}`;
|
|
841
864
|
}
|
|
842
865
|
return {
|
|
843
866
|
onClick: onHeaderClick,
|
|
844
867
|
className: sortClassName,
|
|
845
|
-
'aria-sort': (this.sortColumn ===
|
|
868
|
+
'aria-sort': (this.sortColumn === columnId ? (this.sortDirection === 'asc' ? 'ascending' : 'descending') : 'none'),
|
|
846
869
|
};
|
|
847
870
|
};
|
|
848
871
|
this.sortColumn = (_a = options === null || options === void 0 ? void 0 : options.initialSortColumn) !== null && _a !== void 0 ? _a : null;
|