jattac.libs.web.responsive-table 0.1.5 → 0.2.0

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.
@@ -1,21 +1,21 @@
1
- import { ReactNode } from 'react';
2
-
3
- export default interface IFooterColumnDefinition {
4
- colSpan: number;
5
- cellRenderer: () => ReactNode;
6
- /**
7
- * An optional, explicit label for the footer cell, especially for mobile view.
8
- * If not provided, the table could try to infer it from the corresponding column.
9
- */
10
- displayLabel?: ReactNode;
11
-
12
- /**
13
- * An optional click handler for the footer cell.
14
- */
15
- onCellClick?: () => void;
16
-
17
- /**
18
- * Optional class name for custom styling of the footer cell.
19
- */
20
- className?: string;
21
- }
1
+ import { ReactNode } from 'react';
2
+
3
+ export default interface IFooterColumnDefinition {
4
+ colSpan: number;
5
+ cellRenderer: () => ReactNode;
6
+ /**
7
+ * An optional, explicit label for the footer cell, especially for mobile view.
8
+ * If not provided, the table could try to infer it from the corresponding column.
9
+ */
10
+ displayLabel?: ReactNode;
11
+
12
+ /**
13
+ * An optional click handler for the footer cell.
14
+ */
15
+ onCellClick?: () => void;
16
+
17
+ /**
18
+ * Optional class name for custom styling of the footer cell.
19
+ */
20
+ className?: string;
21
+ }
@@ -1,11 +1,12 @@
1
- import { ReactNode } from 'react';
2
-
3
- export default interface IResponsiveTableColumnDefinition<TData> {
4
- displayLabel: ReactNode;
5
- cellRenderer: (data: TData) => ReactNode;
6
- interactivity?: {
7
- id: string;
8
- onHeaderClick?: (id: string) => void;
9
- className?: string;
10
- };
11
- }
1
+ import { ReactNode } from 'react';
2
+
3
+ export default interface IResponsiveTableColumnDefinition<TData> {
4
+ displayLabel: ReactNode;
5
+ cellRenderer: (data: TData) => ReactNode;
6
+ interactivity?: {
7
+ id: string;
8
+ onHeaderClick?: (id: string) => void;
9
+ className?: string;
10
+ };
11
+ getFilterableValue?: (data: TData) => string | number;
12
+ }
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { IResponsiveTablePlugin, IPluginAPI } from './IResponsiveTablePlugin';
3
+ import IResponsiveTableColumnDefinition from '../Data/IResponsiveTableColumnDefinition';
4
+
5
+ export class FilterPlugin<TData> implements IResponsiveTablePlugin<TData> {
6
+ public id = 'filter';
7
+ private filterText = '';
8
+ private api!: IPluginAPI<TData>;
9
+
10
+ constructor() {
11
+ }
12
+
13
+ public onPluginInit = (api: IPluginAPI<TData>) => {
14
+ this.api = api;
15
+ };
16
+
17
+ public renderHeader = () => {
18
+ if (!this.api.filterProps?.showFilter) {
19
+ return null;
20
+ }
21
+ return (
22
+ <div style={{ float: 'right', marginBottom: '1rem' }}>
23
+ <input
24
+ type="text"
25
+ placeholder={this.api.filterProps.filterPlaceholder || "Search..."}
26
+ onChange={this.handleFilterChange}
27
+ style={{
28
+ padding: '0.5rem',
29
+ border: '1px solid #ccc',
30
+ borderRadius: '4px',
31
+ }}
32
+ />
33
+ </div>
34
+ );
35
+ };
36
+
37
+ public processData = (data: TData[]): TData[] => {
38
+ if (!this.filterText || !this.api.columnDefinitions) {
39
+ return data;
40
+ }
41
+
42
+ const lowercasedFilter = this.filterText.toLowerCase();
43
+
44
+ return data.filter((row) => {
45
+ return this.api.columnDefinitions!.some((colDef) => {
46
+ // If colDef is a function, it won't have getFilterableValue, so skip it.
47
+ if (typeof colDef === 'function') {
48
+ return false;
49
+ }
50
+
51
+ // Now we know colDef is an object (IResponsiveTableColumnDefinition<TData>)
52
+ const typedColDef = colDef as IResponsiveTableColumnDefinition<TData>;
53
+
54
+ // Check if getFilterableValue exists and is a function
55
+ if (typedColDef.getFilterableValue && typeof typedColDef.getFilterableValue === 'function') {
56
+ const value = typedColDef.getFilterableValue(row);
57
+ return value?.toString().toLowerCase().includes(lowercasedFilter);
58
+ }
59
+ return false; // If getFilterableValue is not present or not a function
60
+ });
61
+ });
62
+ };
63
+
64
+ private handleFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
65
+ // Debounce the filter change
66
+ setTimeout(() => {
67
+ this.filterText = e.target.value;
68
+ this.api.forceUpdate();
69
+ }, 300);
70
+ };
71
+ }
@@ -0,0 +1,48 @@
1
+ import { ReactNode } from 'react';
2
+ import { ColumnDefinition } from '../UI/ResponsiveTable';
3
+
4
+ export interface IResponsiveTablePlugin<TData> {
5
+ // A unique identifier for the plugin
6
+ id: string;
7
+
8
+ // Optional: Renders a UI component above the table
9
+ renderHeader?: () => ReactNode;
10
+
11
+ // Optional: Renders a UI component below the table
12
+ renderFooter?: () => ReactNode;
13
+
14
+ // Optional: Processes the data before it's rendered
15
+ processData?: (data: TData[]) => TData[];
16
+
17
+ // Optional: A callback that the table can use to provide the plugin with its own API
18
+ onPluginInit?: (api: IPluginAPI<TData>) => void;
19
+ }
20
+
21
+ export interface IPluginAPI<TData> {
22
+ // Function to get the current data from the table
23
+ getData: () => TData[];
24
+
25
+ // Function to force the table to re-render
26
+ forceUpdate: () => void;
27
+
28
+ // Function to get the column definitions from the table
29
+ columnDefinitions: ColumnDefinition<TData>[];
30
+
31
+ // Function to get the scrollable element of the table
32
+ getScrollableElement?: () => HTMLElement | null;
33
+
34
+ // Optional: Infinite scroll props from the ResponsiveTable component
35
+ infiniteScrollProps?: {
36
+ enableInfiniteScroll?: boolean;
37
+ onLoadMore?: (currentData: TData[]) => Promise<TData[] | null>;
38
+ hasMore?: boolean;
39
+ loadingMoreComponent?: ReactNode;
40
+ noMoreDataComponent?: ReactNode;
41
+ };
42
+
43
+ // Optional: Filter props from the ResponsiveTable component
44
+ filterProps?: {
45
+ showFilter?: boolean;
46
+ filterPlaceholder?: string;
47
+ };
48
+ }
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import { IResponsiveTablePlugin, IPluginAPI } from './IResponsiveTablePlugin';
3
+
4
+ export class InfiniteScrollPlugin<TData> implements IResponsiveTablePlugin<TData> {
5
+ public id = 'infinite-scroll';
6
+ private api!: IPluginAPI<TData>;
7
+ private isLoadingMore = false;
8
+
9
+ constructor() {
10
+ }
11
+
12
+ public onPluginInit = (api: IPluginAPI<TData>) => {
13
+ this.api = api;
14
+ this.attachScrollListener();
15
+ };
16
+
17
+ private attachScrollListener = () => {
18
+ const scrollableElement = this.api.getScrollableElement?.();
19
+ if (scrollableElement) {
20
+ scrollableElement.addEventListener('scroll', this.handleScroll);
21
+ }
22
+ };
23
+
24
+ private handleScroll = async () => {
25
+ const scrollableElement = this.api.getScrollableElement?.();
26
+ if (!scrollableElement || !this.api.infiniteScrollProps?.enableInfiniteScroll) {
27
+ return;
28
+ }
29
+
30
+ const { scrollTop, scrollHeight, clientHeight } = scrollableElement;
31
+
32
+ const scrollThreshold = 200; // Load more data when 200px from the bottom
33
+
34
+ if (
35
+ scrollHeight - scrollTop - clientHeight < scrollThreshold &&
36
+ this.api.infiniteScrollProps.hasMore &&
37
+ !this.isLoadingMore
38
+ ) {
39
+ this.isLoadingMore = true;
40
+ this.api.forceUpdate(); // Trigger re-render to show loading component
41
+
42
+ const newData = await this.api.infiniteScrollProps.onLoadMore?.(this.api.getData());
43
+
44
+ if (newData) {
45
+ // The main component will handle appending data via processData
46
+ } else {
47
+ // No more data, update hasMore in parent if necessary
48
+ }
49
+
50
+ this.isLoadingMore = false;
51
+ this.api.forceUpdate(); // Trigger re-render to hide loading component
52
+ }
53
+ };
54
+
55
+ public processData = (data: TData[]): TData[] => {
56
+ // This plugin doesn't modify the data directly, but rather triggers loading more.
57
+ // The main component's data prop should be updated by the consumer of the table.
58
+ return data;
59
+ };
60
+
61
+ public renderFooter = () => {
62
+ if (!this.api.infiniteScrollProps) {
63
+ return null;
64
+ }
65
+
66
+ if (this.isLoadingMore) {
67
+ return this.api.infiniteScrollProps.loadingMoreComponent || <div>Loading more...</div>;
68
+ } else if (!this.api.infiniteScrollProps.hasMore) {
69
+ return this.api.infiniteScrollProps.noMoreDataComponent || <div>No more data.</div>;
70
+ }
71
+ return null;
72
+ };
73
+ }
@@ -2,8 +2,8 @@
2
2
  :root {
3
3
  --table-border-color: #e0e0e0;
4
4
  --table-header-bg: #f8f9fa;
5
- --table-row-hover-bg: #f1f3f5;
6
- --table-row-stripe-bg: #fdfdfd;
5
+ --table-row-hover-bg: #e9ecef;
6
+ --table-row-stripe-bg: #f2f2f2;
7
7
  --card-bg: #ffffff;
8
8
  --card-border-color: #e0e0e0;
9
9
  --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
@@ -226,3 +226,28 @@
226
226
  font-weight: 500; /* Less aggressive than bold */
227
227
  font-size: 1rem;
228
228
  }
229
+
230
+ .row-exit {
231
+ opacity: 0;
232
+ transform: scaleY(0);
233
+ transition: transform 0.3s ease-out, opacity 0.3s ease-out;
234
+ }
235
+
236
+ .row-enter {
237
+ opacity: 0;
238
+ transform: translateY(20px);
239
+ transition: transform 0.5s ease-out, opacity 0.5s ease-out;
240
+ }
241
+
242
+ .row-flash {
243
+ animation: flash 0.5s ease-out;
244
+ }
245
+
246
+ @keyframes flash {
247
+ 0% {
248
+ background-color: var(--table-row-hover-bg);
249
+ }
250
+ 100% {
251
+ background-color: transparent;
252
+ }
253
+ }
@@ -1,35 +1,58 @@
1
- import React, { CSSProperties, Component, ReactNode } from 'react';
1
+ import React, { CSSProperties, Component, ReactNode, createRef } from 'react';
2
2
  import styles from '../Styles/ResponsiveTable.module.css';
3
3
  import IResponsiveTableColumnDefinition from '../Data/IResponsiveTableColumnDefinition';
4
4
  import IFooterRowDefinition from '../Data/IFooterRowDefinition';
5
+ import { IResponsiveTablePlugin } from '../Plugins/IResponsiveTablePlugin';
6
+ import { FilterPlugin } from '../Plugins/FilterPlugin';
7
+ import { InfiniteScrollPlugin } from '../Plugins/InfiniteScrollPlugin';
8
+ import { FixedSizeList as List } from 'react-window';
5
9
 
6
10
  export type ColumnDefinition<TData> =
7
11
  | IResponsiveTableColumnDefinition<TData>
8
12
  | ((data: TData, rowIndex?: number) => IResponsiveTableColumnDefinition<TData>);
9
- interface IProps<TData> {
10
- columnDefinitions: ColumnDefinition<TData>[];
11
- data: TData[];
12
- noDataComponent?: ReactNode;
13
- maxHeight?: string;
14
- onRowClick?: (item: TData) => void;
15
- footerRows?: IFooterRowDefinition[];
16
- mobileBreakpoint?: number;
17
- isLoading?: boolean;
18
- animateOnLoad?: boolean;
13
+ interface IProps<TData> {
14
+ columnDefinitions: ColumnDefinition<TData>[];
15
+ data: TData[];
16
+ noDataComponent?: ReactNode;
17
+ maxHeight?: string;
18
+ onRowClick?: (item: TData) => void;
19
+ footerRows?: IFooterRowDefinition[];
20
+ mobileBreakpoint?: number;
21
+ plugins?: IResponsiveTablePlugin<TData>[];
22
+ infiniteScrollProps?: {
23
+ enableInfiniteScroll?: boolean;
24
+ onLoadMore?: (currentData: TData[]) => Promise<TData[] | null>;
25
+ hasMore?: boolean;
26
+ loadingMoreComponent?: ReactNode;
27
+ noMoreDataComponent?: ReactNode;
28
+ };
29
+ filterProps?: {
30
+ showFilter?: boolean;
31
+ filterPlaceholder?: string;
32
+ };
33
+ animationProps?: {
34
+ isLoading?: boolean;
35
+ animateOnLoad?: boolean;
36
+ };
19
37
  }
20
38
 
21
- interface IState {
39
+ interface IState<TData> {
22
40
  isMobile: boolean;
41
+ processedData: TData[];
42
+ isLoadingMore: boolean;
23
43
  }
24
44
 
25
45
  // Class component
26
- class ResponsiveTable<TData> extends Component<IProps<TData>, IState> {
46
+ class ResponsiveTable<TData> extends Component<IProps<TData>, IState<TData>> {
27
47
  private debouncedResize: () => void;
48
+ private tableContainerRef = createRef<HTMLDivElement>();
28
49
 
29
50
  constructor(props: IProps<TData>) {
30
51
  super(props);
31
52
  this.state = {
32
53
  isMobile: false,
54
+ processedData: props.data,
55
+ isLoadingMore: false,
33
56
  };
34
57
 
35
58
  this.debouncedResize = this.debounce(this.handleResize, 200);
@@ -48,8 +71,8 @@ class ResponsiveTable<TData> extends Component<IProps<TData>, IState> {
48
71
  }
49
72
 
50
73
  private get data(): TData[] {
51
- if (Array.isArray(this.props.data) && this.props.data.length > 0) {
52
- return this.props.data;
74
+ if (Array.isArray(this.state.processedData) && this.state.processedData.length > 0) {
75
+ return this.state.processedData;
53
76
  } else {
54
77
  return [];
55
78
  }
@@ -81,12 +104,81 @@ class ResponsiveTable<TData> extends Component<IProps<TData>, IState> {
81
104
  componentDidMount(): void {
82
105
  this.handleResize(); // Initial check
83
106
  window.addEventListener('resize', this.debouncedResize);
107
+ this.initializePlugins();
84
108
  }
85
109
 
86
110
  componentWillUnmount(): void {
87
111
  window.removeEventListener('resize', this.debouncedResize);
88
112
  }
89
113
 
114
+ componentDidUpdate(prevProps: IProps<TData>) {
115
+ if (prevProps.data !== this.props.data) {
116
+ this.processData();
117
+ }
118
+
119
+ // Handle infinite scroll loading
120
+ if (this.props.infiniteScrollProps?.enableInfiniteScroll && this.props.infiniteScrollProps?.hasMore && !this.state.isLoadingMore && this.props.infiniteScrollProps?.onLoadMore) {
121
+ // This condition will be met when the parent component updates `data` and `hasMore`
122
+ // after a successful load, or when `hasMore` becomes true.
123
+ // The actual load trigger is within the InfiniteScrollPlugin via `onItemsRendered`
124
+ // or `handleScroll`.
125
+
126
+ }
127
+ }
128
+
129
+ private initializePlugins() {
130
+ const activePlugins: IResponsiveTablePlugin<TData>[] = [];
131
+
132
+ // Add explicitly provided plugins first
133
+ if (this.props.plugins) {
134
+ activePlugins.push(...this.props.plugins);
135
+ }
136
+
137
+ // Automatically add FilterPlugin if filterProps are provided and not already present
138
+ if (this.props.filterProps?.showFilter && !activePlugins.some(p => p.id === 'filter')) {
139
+ activePlugins.push(new FilterPlugin());
140
+ }
141
+
142
+ // Automatically add InfiniteScrollPlugin if infiniteScrollProps are provided and not already present
143
+ if (this.props.infiniteScrollProps?.enableInfiniteScroll && !activePlugins.some(p => p.id === 'infinite-scroll')) {
144
+ activePlugins.push(new InfiniteScrollPlugin());
145
+ }
146
+
147
+ activePlugins.forEach((plugin) => {
148
+ if (plugin.onPluginInit) {
149
+ plugin.onPluginInit({
150
+ getData: () => this.props.data,
151
+ forceUpdate: () => this.processData(),
152
+ getScrollableElement: () => this.tableContainerRef.current,
153
+ infiniteScrollProps: this.props.infiniteScrollProps,
154
+ filterProps: this.props.filterProps,
155
+ columnDefinitions: this.props.columnDefinitions,
156
+ });
157
+ }
158
+ });
159
+
160
+ // Process data with all active plugins
161
+ let processedData = [...this.props.data];
162
+ activePlugins.forEach((plugin) => {
163
+ if (plugin.processData) {
164
+ processedData = plugin.processData(processedData);
165
+ }
166
+ });
167
+ this.setState({ processedData });
168
+ }
169
+
170
+ private processData() {
171
+ let processedData = [...this.props.data];
172
+ if (this.props.plugins) {
173
+ this.props.plugins.forEach((plugin) => {
174
+ if (plugin.processData) {
175
+ processedData = plugin.processData(processedData);
176
+ }
177
+ });
178
+ }
179
+ this.setState({ processedData });
180
+ }
181
+
90
182
  handleResize = (): void => {
91
183
  this.setState({
92
184
  isMobile: window.innerWidth <= this.mobileBreakpoint,
@@ -266,7 +358,7 @@ class ResponsiveTable<TData> extends Component<IProps<TData>, IState> {
266
358
  {this.data.map((row, rowIndex) => (
267
359
  <div
268
360
  key={rowIndex}
269
- className={`${styles['card']} ${this.props.animateOnLoad ? styles.animatedRow : ''}`}
361
+ className={`${styles['card']} ${this.props.animationProps?.animateOnLoad ? styles.animatedRow : ''}`}
270
362
  style={{ animationDelay: `${rowIndex * 0.05}s` }}
271
363
  onClick={(e) => {
272
364
  this.rowClickFunction(row);
@@ -311,8 +403,29 @@ class ResponsiveTable<TData> extends Component<IProps<TData>, IState> {
311
403
  ? ({ maxHeight: this.props.maxHeight, overflowY: 'auto' } as CSSProperties)
312
404
  : {};
313
405
 
406
+ const Row = ({ index, style }: { index: number; style: CSSProperties }) => {
407
+ const row = this.data[index];
408
+ if (!row) return null; // Should not happen with correct item count
409
+
410
+ return (
411
+ <tr
412
+ key={index}
413
+ className={this.props.animationProps?.animateOnLoad ? styles.animatedRow : ''}
414
+ style={{ ...style, animationDelay: `${index * 0.05}s` }}
415
+ >
416
+ {this.props.columnDefinitions.map((columnDefinition, colIndex) => (
417
+ <td onClick={() => this.rowClickFunction(row)} key={colIndex}>
418
+ <span style={{ ...this.rowClickStyle }}>
419
+ {this.getColumnDefinition(columnDefinition, index).cellRenderer(row)}
420
+ </span>
421
+ </td>
422
+ ))}
423
+ </tr>
424
+ );
425
+ };
426
+
314
427
  return (
315
- <div style={fixedHeadersStyle}>
428
+ <div style={fixedHeadersStyle} ref={this.tableContainerRef}>
316
429
  <table className={styles['responsiveTable']} style={{ zIndex: -1 }}>
317
430
  <thead>
318
431
  <tr>
@@ -340,42 +453,81 @@ class ResponsiveTable<TData> extends Component<IProps<TData>, IState> {
340
453
  </tr>
341
454
  </thead>
342
455
  <tbody>
343
- {this.data.map((row, rowIndex) => (
344
- <tr
345
- key={rowIndex}
346
- className={this.props.animateOnLoad ? styles.animatedRow : ''}
347
- style={{ animationDelay: `${rowIndex * 0.05}s` }}
456
+ {this.props.infiniteScrollProps?.enableInfiniteScroll ? (
457
+ <List
458
+ height={fixedHeadersStyle.maxHeight ? (typeof fixedHeadersStyle.maxHeight === 'string' ? parseFloat(fixedHeadersStyle.maxHeight) : fixedHeadersStyle.maxHeight) : 500} // Default height if not provided
459
+ itemCount={this.data.length}
460
+ itemSize={50} // Average row height, can be made configurable
461
+ width={'100%'}
462
+ outerRef={this.tableContainerRef} // Pass ref to outer element for scroll events
348
463
  >
349
- {this.props.columnDefinitions.map((columnDefinition, colIndex) => (
350
- <td onClick={() => this.rowClickFunction(row)} key={colIndex}>
351
- <span style={{ ...this.rowClickStyle }}>
352
- {this.getColumnDefinition(columnDefinition, rowIndex).cellRenderer(row)}
353
- </span>
354
- </td>
355
- ))}
356
- </tr>
357
- ))}
464
+ {Row}
465
+ </List>
466
+ ) : (
467
+ this.data.map((row, rowIndex) => (
468
+ <tr
469
+ key={rowIndex}
470
+ className={this.props.animationProps?.animateOnLoad ? styles.animatedRow : ''}
471
+ style={{ animationDelay: `${rowIndex * 0.05}s` }}
472
+ >
473
+ {this.props.columnDefinitions.map((columnDefinition, colIndex) => (
474
+ <td onClick={() => this.rowClickFunction(row)} key={colIndex}>
475
+ <span style={{ ...this.rowClickStyle }}>
476
+ {this.getColumnDefinition(columnDefinition, rowIndex).cellRenderer(row)}
477
+ </span>
478
+ </td>
479
+ ))}
480
+ </tr>
481
+ ))
482
+ )}
358
483
  </tbody>
359
484
  {this.tableFooter}
360
485
  </table>
486
+ {this.renderPluginFooters()}
361
487
  </div>
362
488
  );
363
489
  }
364
490
 
365
- render() {
366
- if (this.props.isLoading) {
367
- return this.skeletonView;
491
+ private renderPluginHeaders() {
492
+ if (!this.props.plugins) {
493
+ return null;
368
494
  }
369
495
 
370
- if (!this.hasData) {
371
- return this.noDataComponent;
496
+ return this.props.plugins.map((plugin) => {
497
+ if (plugin.renderHeader) {
498
+ return <div key={plugin.id}>{plugin.renderHeader()}</div>;
499
+ }
500
+ return null;
501
+ });
502
+ }
503
+
504
+ private renderPluginFooters() {
505
+ if (!this.props.plugins) {
506
+ return null;
372
507
  }
373
- if (this.state.isMobile) {
374
- return this.mobileView;
508
+
509
+ return this.props.plugins.map((plugin) => {
510
+ if (plugin.renderFooter) {
511
+ return <div key={plugin.id + '-footer'}>{plugin.renderFooter()}</div>;
512
+ }
513
+ return null;
514
+ });
515
+ }
516
+
517
+ render() {
518
+ if (this.props.animationProps?.isLoading) {
519
+ return this.skeletonView;
375
520
  }
376
521
 
377
- return this.largeScreenView;
522
+ return (
523
+ <div>
524
+ {this.renderPluginHeaders()}
525
+ {!this.hasData && this.noDataComponent}
526
+ {this.hasData && this.state.isMobile && this.mobileView}
527
+ {this.hasData && !this.state.isMobile && this.largeScreenView}
528
+ </div>
529
+ );
378
530
  }
379
531
  }
380
532
 
381
- export default ResponsiveTable;
533
+ export default ResponsiveTable;
package/src/index.tsx CHANGED
@@ -1,7 +1,10 @@
1
- import IFooterColumnDefinition from './Data/IFooterColumnDefinition';
2
- import IFooterRowDefinition from './Data/IFooterRowDefinition';
3
- import IResponsiveTableColumnDefinition from './Data/IResponsiveTableColumnDefinition';
4
- import ResponsiveTable, { ColumnDefinition } from './UI/ResponsiveTable';
5
-
6
- export { IResponsiveTableColumnDefinition, ColumnDefinition, IFooterColumnDefinition, IFooterRowDefinition };
7
- export default ResponsiveTable;
1
+ import IFooterColumnDefinition from './Data/IFooterColumnDefinition';
2
+ import IFooterRowDefinition from './Data/IFooterRowDefinition';
3
+ import IResponsiveTableColumnDefinition from './Data/IResponsiveTableColumnDefinition';
4
+ import ResponsiveTable, { ColumnDefinition } from './UI/ResponsiveTable';
5
+ import { FilterPlugin } from './Plugins/FilterPlugin';
6
+ import { InfiniteScrollPlugin } from './Plugins/InfiniteScrollPlugin';
7
+ import { IResponsiveTablePlugin } from './Plugins/IResponsiveTablePlugin';
8
+
9
+ export { IResponsiveTableColumnDefinition, ColumnDefinition, IFooterColumnDefinition, IFooterRowDefinition, FilterPlugin, InfiniteScrollPlugin, IResponsiveTablePlugin };
10
+ export default ResponsiveTable;