jattac.libs.web.responsive-table 0.8.0 → 0.8.1

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.
@@ -0,0 +1,1053 @@
1
+ import React, { createContext, useCallback, useMemo, useContext, useState, useRef, useEffect } from 'react';
2
+
3
+ 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","selectedRow":"ResponsiveTable-module_selectedRow__-JyNW","clickableRow":"ResponsiveTable-module_clickableRow__0kjWm","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"};
4
+
5
+ /******************************************************************************
6
+ Copyright (c) Microsoft Corporation.
7
+
8
+ Permission to use, copy, modify, and/or distribute this software for any
9
+ purpose with or without fee is hereby granted.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ ***************************************************************************** */
19
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
20
+
21
+
22
+ function __rest(s, e) {
23
+ var t = {};
24
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
25
+ t[p] = s[p];
26
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
27
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
28
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
29
+ t[p[i]] = s[p[i]];
30
+ }
31
+ return t;
32
+ }
33
+
34
+ function __awaiter(thisArg, _arguments, P, generator) {
35
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
36
+ return new (P || (P = Promise))(function (resolve, reject) {
37
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
38
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
39
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
40
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
41
+ });
42
+ }
43
+
44
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
45
+ var e = new Error(message);
46
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
47
+ };
48
+
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ const TableContext = createContext(undefined);
51
+ const useTableContext = () => {
52
+ const context = useContext(TableContext);
53
+ if (!context) {
54
+ throw new Error('useTableContext must be used within a TableProvider');
55
+ }
56
+ return context;
57
+ };
58
+ function TableProvider({ children, value }) {
59
+ const { processedData, activePlugins, selectionProps, } = value;
60
+ const getRawColumnDefinition = useCallback((columnDefinition) => {
61
+ if (typeof columnDefinition === 'function') {
62
+ if (processedData.length === 0) {
63
+ return { displayLabel: '', cellRenderer: () => '' };
64
+ }
65
+ return columnDefinition(processedData[0], 0);
66
+ }
67
+ return columnDefinition;
68
+ }, [processedData]);
69
+ const getColumnDefinition = useCallback((columnDefinition, rowIndex) => {
70
+ if (processedData.length === 0) {
71
+ return { displayLabel: '', cellRenderer: () => '' };
72
+ }
73
+ return columnDefinition instanceof Function ? columnDefinition(processedData[0], rowIndex) : columnDefinition;
74
+ }, [processedData]);
75
+ const onHeaderClickCallback = useCallback((colDef) => {
76
+ var _a;
77
+ const rawColumnDefinition = getRawColumnDefinition(colDef);
78
+ return (_a = rawColumnDefinition.interactivity) === null || _a === void 0 ? void 0 : _a.onHeaderClick;
79
+ }, [getRawColumnDefinition]);
80
+ const getClickableHeaderClassName = useCallback((onHeaderClick, colDef) => {
81
+ var _a;
82
+ const rawColumnDefinition = getRawColumnDefinition(colDef);
83
+ return onHeaderClick
84
+ ? ((_a = rawColumnDefinition.interactivity) === null || _a === void 0 ? void 0 : _a.className) || styles$2.clickableHeader
85
+ : '';
86
+ }, [getRawColumnDefinition]);
87
+ const getHeaderProps = useCallback((colDef) => {
88
+ const headerProps = {};
89
+ activePlugins.forEach((plugin) => {
90
+ if (plugin.getHeaderProps) {
91
+ Object.assign(headerProps, plugin.getHeaderProps(getRawColumnDefinition(colDef)));
92
+ }
93
+ });
94
+ return headerProps;
95
+ }, [activePlugins, getRawColumnDefinition]);
96
+ const getRowId = useCallback((row, index) => {
97
+ if (selectionProps && selectionProps.rowIdKey) {
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ return row[selectionProps.rowIdKey];
100
+ }
101
+ return index;
102
+ }, [selectionProps]);
103
+ const getRowProps = useCallback((row) => {
104
+ const rowProps = {};
105
+ const clickHandlers = [];
106
+ activePlugins.forEach((plugin) => {
107
+ if (plugin.getRowProps) {
108
+ const props = plugin.getRowProps(row);
109
+ if (props.className) {
110
+ rowProps.className = `${rowProps.className || ''} ${props.className}`.trim();
111
+ }
112
+ if (props.onClick) {
113
+ clickHandlers.push(props.onClick);
114
+ }
115
+ const rest = __rest(props, []);
116
+ Object.assign(rowProps, rest);
117
+ }
118
+ });
119
+ if (clickHandlers.length > 0) {
120
+ rowProps.onClick = (e) => {
121
+ clickHandlers.forEach(handler => handler(e));
122
+ };
123
+ }
124
+ return rowProps;
125
+ }, [activePlugins]);
126
+ const renderCell = useCallback((content, row, colDef) => {
127
+ let processedContent = content;
128
+ activePlugins.forEach((plugin) => {
129
+ if (plugin.renderCell) {
130
+ processedContent = plugin.renderCell(processedContent, row, colDef);
131
+ }
132
+ });
133
+ return processedContent;
134
+ }, [activePlugins]);
135
+ const contextValue = useMemo(() => (Object.assign(Object.assign({}, value), { currentData: processedData, getRawColumnDefinition,
136
+ getColumnDefinition,
137
+ onHeaderClickCallback,
138
+ getClickableHeaderClassName,
139
+ getHeaderProps,
140
+ getRowProps,
141
+ getRowId,
142
+ renderCell })), [
143
+ value,
144
+ processedData,
145
+ getRawColumnDefinition,
146
+ getColumnDefinition,
147
+ onHeaderClickCallback,
148
+ getClickableHeaderClassName,
149
+ getHeaderProps,
150
+ getRowProps,
151
+ getRowId,
152
+ renderCell,
153
+ ]);
154
+ return (React.createElement(TableContext.Provider, { value: contextValue }, children));
155
+ }
156
+
157
+ function TableHeaderCell(props) {
158
+ const { columnDefinition, colIndex } = props;
159
+ const { onHeaderClickCallback, getClickableHeaderClassName, getHeaderProps, getRawColumnDefinition, getColumnDefinition, } = useTableContext();
160
+ const onHeaderClick = onHeaderClickCallback(columnDefinition);
161
+ const clickableHeaderClassName = getClickableHeaderClassName(onHeaderClick, columnDefinition);
162
+ const headerProps = getHeaderProps(columnDefinition);
163
+ const combinedClassName = `${clickableHeaderClassName} ${headerProps.className ? styles$2[headerProps.className] : ''}`.trim();
164
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
165
+ const { className } = headerProps, restHeaderProps = __rest(headerProps, ["className"]);
166
+ return (React.createElement("th", Object.assign({ key: colIndex, className: combinedClassName }, restHeaderProps, { onClick: onHeaderClick ? () => onHeaderClick(getRawColumnDefinition(columnDefinition).interactivity.id) : restHeaderProps.onClick }),
167
+ React.createElement("div", { className: styles$2.headerInnerWrapper },
168
+ React.createElement("div", { className: styles$2.headerContent }, getColumnDefinition(columnDefinition, 0).displayLabel),
169
+ React.createElement("span", { className: styles$2.sortIcon }))));
170
+ }
171
+
172
+ function TableBodyCell(props) {
173
+ const { row, rowIndex, columnDefinition } = props;
174
+ const { getColumnDefinition, renderCell } = useTableContext();
175
+ const colDef = getColumnDefinition(columnDefinition, rowIndex);
176
+ const cellContent = colDef.cellRenderer(row);
177
+ return (React.createElement(React.Fragment, null, renderCell(cellContent, row, colDef)));
178
+ }
179
+
180
+ function TableBodyRow(props) {
181
+ const { row, rowIndex, columnDefinitions, onRowClick, selectionProps, animationProps, } = props;
182
+ const { getRowProps } = useTableContext();
183
+ const rowProps = getRowProps(row);
184
+ const isClickable = onRowClick || selectionProps;
185
+ const pluginOnClick = rowProps.onClick;
186
+ return (React.createElement("tr", { className: `${isClickable ? styles$2.clickableRow : ''} ${(animationProps === null || animationProps === void 0 ? void 0 : animationProps.animateOnLoad) ? styles$2.animatedRow : ''} ${rowProps.className || ''}`.trim(), style: { animationDelay: `${rowIndex * 0.05}s` }, "aria-selected": rowProps['aria-selected'], onClick: (e) => {
187
+ if (pluginOnClick) {
188
+ pluginOnClick(e);
189
+ }
190
+ if (onRowClick) {
191
+ onRowClick(row);
192
+ }
193
+ } }, columnDefinitions.map((columnDefinition, colIndex) => (React.createElement("td", { key: colIndex },
194
+ React.createElement(TableBodyCell, { row: row, rowIndex: rowIndex, columnDefinition: columnDefinition }))))));
195
+ }
196
+
197
+ function DesktopView(props) {
198
+ const { maxHeight, isHeaderSticky, tableContainerRef, headerRef, footerRows, renderPluginFooters, onScroll, } = props;
199
+ const { visibleColumns, originalColumnDefinitions, currentData, getRawColumnDefinition, onRowClick, selectionProps, animationProps, } = useTableContext();
200
+ const getEffectiveColSpan = useCallback((footerCol, startIndex) => {
201
+ const originalSpan = footerCol.colSpan || 1;
202
+ const endIndex = startIndex + originalSpan;
203
+ let visibleCount = 0;
204
+ for (let i = startIndex; i < endIndex; i++) {
205
+ const col = originalColumnDefinitions[i];
206
+ if (col && getRawColumnDefinition(col).visible !== false) {
207
+ visibleCount++;
208
+ }
209
+ }
210
+ return visibleCount;
211
+ }, [originalColumnDefinitions, getRawColumnDefinition]);
212
+ const tableFooter = useMemo(() => {
213
+ if (!footerRows || footerRows.length === 0) {
214
+ return null;
215
+ }
216
+ return (React.createElement("tfoot", null, footerRows.map((row, rowIndex) => {
217
+ let currentOriginalIndex = 0;
218
+ return (React.createElement("tr", { key: rowIndex }, row.columns.map((col, colIndex) => {
219
+ const effectiveColSpan = getEffectiveColSpan(col, currentOriginalIndex);
220
+ currentOriginalIndex += (col.colSpan || 1);
221
+ if (effectiveColSpan === 0)
222
+ return null;
223
+ return (React.createElement("td", { key: colIndex, colSpan: effectiveColSpan, className: `${styles$2.footerCell} ${col.className || ''} ${col.onCellClick ? styles$2.clickableFooterCell : ''}`, onClick: col.onCellClick }, col.cellRenderer()));
224
+ })));
225
+ })));
226
+ }, [footerRows, getEffectiveColSpan]);
227
+ const useFixedHeaders = !!maxHeight;
228
+ const fixedHeadersStyle = useFixedHeaders
229
+ ? { maxHeight, overflowY: 'auto' }
230
+ : {};
231
+ const headerClassName = useFixedHeaders
232
+ ? styles$2.internalStickyHeader
233
+ : (isHeaderSticky ? styles$2.stickyHeader : '');
234
+ return (React.createElement("div", { style: fixedHeadersStyle, ref: tableContainerRef, onScroll: onScroll },
235
+ React.createElement("table", { className: styles$2['responsiveTable'] },
236
+ React.createElement("thead", { ref: headerRef, className: headerClassName },
237
+ React.createElement("tr", null, visibleColumns.map((columnDefinition, colIndex) => (React.createElement(TableHeaderCell, { key: colIndex, columnDefinition: columnDefinition, colIndex: colIndex }))))),
238
+ React.createElement("tbody", null, currentData.map((row, rowIndex) => (React.createElement(TableBodyRow, { key: rowIndex, row: row, rowIndex: rowIndex, columnDefinitions: visibleColumns, onRowClick: onRowClick, selectionProps: selectionProps, animationProps: animationProps })))),
239
+ tableFooter),
240
+ renderPluginFooters()));
241
+ }
242
+
243
+ function MobileView(props) {
244
+ const { mobileFooter } = props;
245
+ const { currentData, visibleColumns, onRowClick, selectionProps, animationProps, getRowProps, getRowId, getColumnDefinition, onHeaderClickCallback, getClickableHeaderClassName, } = useTableContext();
246
+ const isClickable = onRowClick || selectionProps;
247
+ return (React.createElement("div", null,
248
+ currentData.map((row, rowIndex) => {
249
+ const rowProps = getRowProps(row);
250
+ const pluginOnClick = rowProps.onClick;
251
+ return (React.createElement("div", { key: getRowId(row, rowIndex), className: `${styles$2.card} ${isClickable ? styles$2.clickableRow : ''} ${(animationProps === null || animationProps === void 0 ? void 0 : animationProps.animateOnLoad) ? styles$2.animatedRow : ''} ${rowProps.className || ''}`.trim(), style: { animationDelay: `${rowIndex * 0.05}s` }, "aria-selected": rowProps['aria-selected'], onClick: (e) => {
252
+ if (pluginOnClick)
253
+ pluginOnClick(e);
254
+ if (onRowClick)
255
+ onRowClick(row);
256
+ } },
257
+ React.createElement("div", { className: styles$2['card-header'] }, " "),
258
+ React.createElement("div", { className: styles$2['card-body'] }, visibleColumns.map((columnDefinition, colIndex) => {
259
+ const colDef = getColumnDefinition(columnDefinition, rowIndex);
260
+ const onHeaderClick = onHeaderClickCallback(columnDefinition);
261
+ const clickableHeaderClassName = getClickableHeaderClassName(onHeaderClick, columnDefinition);
262
+ return (React.createElement("div", { key: colIndex, className: styles$2['card-row'] },
263
+ React.createElement("p", null,
264
+ React.createElement("span", { className: `${styles$2['card-label']} ${clickableHeaderClassName}`, onClick: (e) => {
265
+ if (onHeaderClick) {
266
+ e.stopPropagation();
267
+ onHeaderClick(colDef.interactivity.id);
268
+ }
269
+ } }, colDef.displayLabel),
270
+ React.createElement("span", { className: styles$2['card-value'] },
271
+ React.createElement(TableBodyCell, { row: row, rowIndex: rowIndex, columnDefinition: columnDefinition })))));
272
+ }))));
273
+ }),
274
+ mobileFooter));
275
+ }
276
+
277
+ function SkeletonView(props) {
278
+ const { isMobile, columnDefinitions } = props;
279
+ const skeletonRowCount = 5;
280
+ const columnCount = columnDefinitions.length;
281
+ if (isMobile) {
282
+ return (React.createElement("div", null, [...Array(skeletonRowCount)].map((_, i) => (React.createElement("div", { key: i, className: styles$2.skeletonCard }, [...Array(columnCount)].map((_, j) => (React.createElement("div", { key: j, className: `${styles$2.skeleton} ${styles$2.skeletonText}`, style: { marginBottom: '0.5rem' } }))))))));
283
+ }
284
+ return (React.createElement("table", { className: styles$2.responsiveTable },
285
+ React.createElement("thead", null,
286
+ React.createElement("tr", null, [...Array(columnCount)].map((_, i) => (React.createElement("th", { key: i },
287
+ React.createElement("div", { className: `${styles$2.skeleton} ${styles$2.skeletonText}` })))))),
288
+ React.createElement("tbody", null, [...Array(skeletonRowCount)].map((_, i) => (React.createElement("tr", { key: i }, [...Array(columnCount)].map((_, j) => (React.createElement("td", { key: j },
289
+ React.createElement("div", { className: `${styles$2.skeleton} ${styles$2.skeletonText}` }))))))))));
290
+ }
291
+
292
+ var styles$1 = {"spinner":"LoadingSpinner-module_spinner__F9V3x"};
293
+
294
+ var styles = {"infoContainer":"NoMoreDataMessage-module_infoContainer__dk1r5","noMoreData":"NoMoreDataMessage-module_noMoreData__ATuIg"};
295
+
296
+ const LoadingSpinner = () => {
297
+ return (React.createElement("div", { className: styles.infoContainer },
298
+ React.createElement("div", { className: styles$1.spinner }),
299
+ React.createElement("span", null, "Loading more items...")));
300
+ };
301
+
302
+ const NoMoreDataMessage = () => {
303
+ return (React.createElement("div", { className: `${styles.infoContainer} ${styles.noMoreData}` },
304
+ React.createElement("p", null, "You've reached the end!")));
305
+ };
306
+
307
+ function debounce(func, delay) {
308
+ let timeout;
309
+ return function (...args) {
310
+ clearTimeout(timeout);
311
+ timeout = setTimeout(() => {
312
+ func.apply(this, args);
313
+ }, delay);
314
+ };
315
+ }
316
+ const useResponsiveTable = (props) => {
317
+ const { mobileBreakpoint = 600, enablePageLevelStickyHeader = true, maxHeight, headerRef, scrollableRef } = props;
318
+ const [isMobile, setIsMobile] = useState(false);
319
+ const [isHeaderSticky, setIsHeaderSticky] = useState(false);
320
+ const handleResize = useCallback(() => {
321
+ setIsMobile(window.innerWidth <= mobileBreakpoint);
322
+ }, [mobileBreakpoint]);
323
+ const handleScroll = useCallback((currentTarget) => {
324
+ // Page-level sticky header logic
325
+ if (enablePageLevelStickyHeader && !maxHeight && (headerRef === null || headerRef === void 0 ? void 0 : headerRef.current)) {
326
+ const { top } = headerRef.current.getBoundingClientRect();
327
+ const sticky = top <= 0;
328
+ if (sticky !== isHeaderSticky) {
329
+ setIsHeaderSticky(sticky);
330
+ }
331
+ }
332
+ }, [enablePageLevelStickyHeader, maxHeight, headerRef, isHeaderSticky]);
333
+ const debouncedScrollHandler = useRef(debounce(handleScroll, 200)).current;
334
+ useEffect(() => {
335
+ handleResize(); // Initial check
336
+ window.addEventListener('resize', handleResize);
337
+ const scrollTarget = (scrollableRef === null || scrollableRef === void 0 ? void 0 : scrollableRef.current) || window;
338
+ if (enablePageLevelStickyHeader || maxHeight) { // Only add scroll listener if sticky header or internal scroll is enabled
339
+ scrollTarget.addEventListener('scroll', (e) => debouncedScrollHandler(e.currentTarget));
340
+ }
341
+ return () => {
342
+ window.removeEventListener('resize', handleResize);
343
+ if (enablePageLevelStickyHeader || maxHeight) {
344
+ scrollTarget.removeEventListener('scroll', (e) => debouncedScrollHandler(e.currentTarget));
345
+ }
346
+ };
347
+ }, [handleResize, debouncedScrollHandler, enablePageLevelStickyHeader, maxHeight, scrollableRef]);
348
+ return { isMobile, isHeaderSticky, debouncedScrollHandler: debouncedScrollHandler };
349
+ };
350
+
351
+ class FilterPlugin {
352
+ constructor() {
353
+ this.id = 'filter';
354
+ this.filterText = '';
355
+ this.debounceTimeout = null;
356
+ this.onPluginInit = (api) => {
357
+ this.api = api;
358
+ };
359
+ this.renderHeader = () => {
360
+ var _a;
361
+ if (!((_a = this.api.filterProps) === null || _a === void 0 ? void 0 : _a.showFilter)) {
362
+ return null;
363
+ }
364
+ return (React.createElement("div", { style: { marginBottom: '1rem' } },
365
+ React.createElement("input", { type: "text", placeholder: this.api.filterProps.filterPlaceholder || "Search...", onChange: this.handleFilterChange, className: this.api.filterProps.className, style: {
366
+ padding: '0.5rem',
367
+ border: '1px solid #ccc',
368
+ borderRadius: '4px',
369
+ } })));
370
+ };
371
+ this.processData = (data) => {
372
+ if (!this.filterText || !this.api.columnDefinitions) {
373
+ return data;
374
+ }
375
+ const lowercasedFilter = this.filterText.toLowerCase();
376
+ return data.filter((row) => {
377
+ return this.api.columnDefinitions.some((colDef) => {
378
+ // If colDef is a function, it won't have getFilterableValue, so skip it.
379
+ if (typeof colDef === 'function') {
380
+ return false;
381
+ }
382
+ // Now we know colDef is an object (IResponsiveTableColumnDefinition<TData>)
383
+ const typedColDef = colDef;
384
+ // Use a type guard to check if getFilterableValue exists on this branch of the union
385
+ if ('getFilterableValue' in typedColDef && typedColDef.getFilterableValue) {
386
+ const value = typedColDef.getFilterableValue(row);
387
+ return value === null || value === void 0 ? void 0 : value.toString().toLowerCase().includes(lowercasedFilter);
388
+ }
389
+ return false; // If getFilterableValue is not present or not a function
390
+ });
391
+ });
392
+ };
393
+ this.renderCell = (content,
394
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
395
+ _row,
396
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
397
+ _column) => {
398
+ if (!this.filterText || typeof content !== 'string') {
399
+ return content;
400
+ }
401
+ const parts = content.split(new RegExp(`(${this.filterText})`, 'gi'));
402
+ return (React.createElement("span", null, parts.map((part, i) => part.toLowerCase() === this.filterText.toLowerCase() ? (React.createElement("strong", { key: i }, part)) : (part))));
403
+ };
404
+ this.handleFilterChange = (e) => {
405
+ const currentFilterText = e.target.value;
406
+ if (this.debounceTimeout) {
407
+ clearTimeout(this.debounceTimeout);
408
+ }
409
+ this.debounceTimeout = setTimeout(() => {
410
+ this.filterText = currentFilterText;
411
+ this.api.forceUpdate();
412
+ }, 300);
413
+ };
414
+ }
415
+ }
416
+
417
+ class SelectionPlugin {
418
+ constructor() {
419
+ this.id = 'selection';
420
+ // Internal state for uncontrolled mode
421
+ this.internalSelectedIds = new Set();
422
+ this.onPluginInit = (api) => {
423
+ var _a;
424
+ this.api = api;
425
+ // For uncontrolled mode, initialize with selectedItems if provided on first render
426
+ if (((_a = this.api.selectionProps) === null || _a === void 0 ? void 0 : _a.selectedItems) && this.internalSelectedIds.size === 0) {
427
+ const { selectedItems, rowIdKey } = this.api.selectionProps;
428
+ if (selectedItems && rowIdKey) {
429
+ const initialIds = selectedItems.map(item => item[rowIdKey]);
430
+ this.internalSelectedIds = new Set(initialIds);
431
+ }
432
+ }
433
+ };
434
+ this.getRowId = (row) => {
435
+ const { rowIdKey } = this.api.selectionProps;
436
+ return row[rowIdKey];
437
+ };
438
+ this.isControlled = () => {
439
+ var _a;
440
+ return ((_a = this.api.selectionProps) === null || _a === void 0 ? void 0 : _a.selectedItems) !== undefined;
441
+ };
442
+ this.getSelectedIds = () => {
443
+ if (this.isControlled()) {
444
+ const { selectedItems, rowIdKey } = this.api.selectionProps;
445
+ return new Set((selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.map(item => item[rowIdKey])) || []);
446
+ }
447
+ return this.internalSelectedIds;
448
+ };
449
+ this.handleRowClick = (row) => {
450
+ const { onSelectionChange, mode = 'multiple', rowIdKey } = this.api.selectionProps;
451
+ const currentSelectedIds = this.getSelectedIds();
452
+ const newSelectedIds = new Set(currentSelectedIds);
453
+ const rowId = this.getRowId(row);
454
+ if (mode === 'single') {
455
+ if (newSelectedIds.has(rowId)) {
456
+ newSelectedIds.clear();
457
+ }
458
+ else {
459
+ newSelectedIds.clear();
460
+ newSelectedIds.add(rowId);
461
+ }
462
+ }
463
+ else { // multiple
464
+ if (newSelectedIds.has(rowId)) {
465
+ newSelectedIds.delete(rowId);
466
+ }
467
+ else {
468
+ newSelectedIds.add(rowId);
469
+ }
470
+ }
471
+ if (!this.isControlled()) {
472
+ this.internalSelectedIds = newSelectedIds;
473
+ }
474
+ const allData = this.api.getData();
475
+ const selectedItems = allData.filter(item => newSelectedIds.has(item[rowIdKey]));
476
+ onSelectionChange(selectedItems);
477
+ // In uncontrolled mode, we need to force a re-render to show the selection change
478
+ if (!this.isControlled()) {
479
+ this.api.forceUpdate();
480
+ }
481
+ };
482
+ this.getRowProps = (row) => {
483
+ const { selectedRowClassName } = this.api.selectionProps || {};
484
+ const selectedIds = this.getSelectedIds();
485
+ const rowId = this.getRowId(row);
486
+ const isSelected = selectedIds.has(rowId);
487
+ const combinedClassName = [
488
+ isSelected ? styles$2.selectedRow : '',
489
+ isSelected ? selectedRowClassName : ''
490
+ ].filter(Boolean).join(' ');
491
+ return {
492
+ className: combinedClassName,
493
+ onClick: () => this.handleRowClick(row),
494
+ 'aria-selected': isSelected,
495
+ };
496
+ };
497
+ }
498
+ }
499
+
500
+ // Type-safe sort comparer helpers
501
+ const createSortComparers = () => ({
502
+ numeric: (key) => (a, b, direction) => {
503
+ const numA = parseFloat(String(a[key]));
504
+ const numB = parseFloat(String(b[key]));
505
+ const aIsNaN = isNaN(numA);
506
+ const bIsNaN = isNaN(numB);
507
+ if (aIsNaN && bIsNaN)
508
+ return 0;
509
+ if (aIsNaN)
510
+ return 1; // Put non-numbers at the end
511
+ if (bIsNaN)
512
+ return -1;
513
+ return direction === 'asc' ? numA - numB : numB - numA;
514
+ },
515
+ caseInsensitiveString: (key) => (a, b, direction) => {
516
+ var _a, _b;
517
+ const valA = String((_a = a[key]) !== null && _a !== void 0 ? _a : '').toLowerCase();
518
+ const valB = String((_b = b[key]) !== null && _b !== void 0 ? _b : '').toLowerCase();
519
+ if (valA < valB)
520
+ return direction === 'asc' ? -1 : 1;
521
+ if (valA > valB)
522
+ return direction === 'asc' ? 1 : -1;
523
+ return 0;
524
+ },
525
+ date: (key) => (a, b, direction) => {
526
+ const dateA = new Date(String(a[key])).getTime();
527
+ const dateB = new Date(String(b[key])).getTime();
528
+ const aIsNaN = isNaN(dateA);
529
+ const bIsNaN = isNaN(dateB);
530
+ if (aIsNaN && bIsNaN)
531
+ return 0;
532
+ if (aIsNaN)
533
+ return 1; // Put invalid dates at the end
534
+ if (bIsNaN)
535
+ return -1;
536
+ return direction === 'asc' ? dateA - dateB : dateB - dateA;
537
+ },
538
+ });
539
+ class SortPlugin {
540
+ constructor(options) {
541
+ var _a, _b;
542
+ this.id = 'sort';
543
+ this.comparers = createSortComparers();
544
+ this.onPluginInit = (api) => {
545
+ this.api = api;
546
+ };
547
+ this.processData = (data) => {
548
+ if (!this.sortColumn || !this.sortDirection) {
549
+ return data;
550
+ }
551
+ const columnDef = this.api.columnDefinitions.find((col) => (typeof col === 'object' && col.columnId === this.sortColumn));
552
+ if (!columnDef) {
553
+ return data;
554
+ }
555
+ const sortedData = [...data].sort((a, b) => {
556
+ if ('sortComparer' in columnDef && columnDef.sortComparer) {
557
+ if (columnDef.sortComparer.length < 3) {
558
+ console.warn(`The custom sortComparer for column '${this.sortColumn}' should accept all three parameters (a, b, direction) to ensure correct sorting behavior. You provided a function with ${columnDef.sortComparer.length} parameters.`);
559
+ }
560
+ return columnDef.sortComparer(a, b, this.sortDirection);
561
+ }
562
+ if ('getSortableValue' in columnDef && columnDef.getSortableValue) {
563
+ const aValue = columnDef.getSortableValue(a);
564
+ const bValue = columnDef.getSortableValue(b);
565
+ if (aValue < bValue)
566
+ return this.sortDirection === 'asc' ? -1 : 1;
567
+ if (aValue > bValue)
568
+ return this.sortDirection === 'asc' ? 1 : -1;
569
+ return 0;
570
+ }
571
+ // Fallback to dataKey if it exists and no other sorter is provided
572
+ if ('dataKey' in columnDef && columnDef.dataKey) {
573
+ const key = columnDef.dataKey;
574
+ const aValue = a[key];
575
+ const bValue = b[key];
576
+ if (aValue < bValue)
577
+ return this.sortDirection === 'asc' ? -1 : 1;
578
+ if (aValue > bValue)
579
+ return this.sortDirection === 'asc' ? 1 : -1;
580
+ return 0;
581
+ }
582
+ return 0;
583
+ });
584
+ return sortedData;
585
+ };
586
+ this.getHeaderProps = (columnDef) => {
587
+ const { columnId } = columnDef;
588
+ const isSortable = ('sortComparer' in columnDef && !!columnDef.sortComparer) || ('getSortableValue' in columnDef && !!columnDef.getSortableValue);
589
+ // A column must have a columnId and a sort function to be sortable
590
+ if (!isSortable || !columnId) {
591
+ return {};
592
+ }
593
+ const onHeaderClick = (e) => {
594
+ const target = e.target;
595
+ console.log('SortPlugin: Header clicked. Target:', target);
596
+ // If the click is on an interactive element, don't sort
597
+ if (target.closest('input, button, a, [onclick]')) {
598
+ console.log('SortPlugin: Interactive element clicked, ignoring sort.');
599
+ return;
600
+ }
601
+ console.log('SortPlugin: Non-interactive element clicked, proceeding with sort.');
602
+ if (this.sortColumn === columnId) {
603
+ if (this.sortDirection === 'desc') {
604
+ this.sortColumn = null;
605
+ this.sortDirection = null;
606
+ }
607
+ else {
608
+ this.sortDirection = 'desc';
609
+ }
610
+ }
611
+ else {
612
+ this.sortColumn = columnId;
613
+ this.sortDirection = 'asc';
614
+ }
615
+ this.api.forceUpdate();
616
+ };
617
+ let sortClassName = 'sortable';
618
+ if (this.sortColumn === columnId) {
619
+ sortClassName = `sorted-${this.sortDirection}`;
620
+ }
621
+ return {
622
+ onClick: onHeaderClick,
623
+ className: sortClassName,
624
+ 'aria-sort': (this.sortColumn === columnId ? (this.sortDirection === 'asc' ? 'ascending' : 'descending') : 'none'),
625
+ };
626
+ };
627
+ this.sortColumn = (_a = options === null || options === void 0 ? void 0 : options.initialSortColumn) !== null && _a !== void 0 ? _a : null;
628
+ this.sortDirection = (_b = options === null || options === void 0 ? void 0 : options.initialSortDirection) !== null && _b !== void 0 ? _b : null;
629
+ }
630
+ }
631
+
632
+ const useTablePlugins = (props) => {
633
+ const { data, plugins, filterProps, selectionProps, sortProps, columnDefinitions, getScrollableElement, infiniteScrollProps, } = props;
634
+ const [processedData, setProcessedData] = useState(data);
635
+ const [activePlugins, setActivePlugins] = useState([]);
636
+ // Persist internal plugins using refs to prevent state loss
637
+ const filterPluginRef = useRef(null);
638
+ const selectionPluginRef = useRef(null);
639
+ const sortPluginRef = useRef(null);
640
+ const getRawColumnDefinition = useCallback((columnDefinition) => {
641
+ if (typeof columnDefinition === 'function') {
642
+ return columnDefinition(data[0] || {}, 0);
643
+ }
644
+ return columnDefinition;
645
+ }, [data]);
646
+ const visibleColumns = useMemo(() => {
647
+ return columnDefinitions.filter(col => {
648
+ const raw = getRawColumnDefinition(col);
649
+ return raw.visible !== false;
650
+ });
651
+ }, [columnDefinitions, getRawColumnDefinition]);
652
+ const initializePlugins = useCallback(() => {
653
+ const newActivePlugins = [];
654
+ // 1. Add external plugins
655
+ if (plugins) {
656
+ newActivePlugins.push(...plugins);
657
+ }
658
+ // 2. Manage internal FilterPlugin
659
+ if (filterProps === null || filterProps === void 0 ? void 0 : filterProps.showFilter) {
660
+ if (!filterPluginRef.current) {
661
+ filterPluginRef.current = new FilterPlugin();
662
+ }
663
+ if (!newActivePlugins.some(p => p.id === 'filter')) {
664
+ newActivePlugins.push(filterPluginRef.current);
665
+ }
666
+ }
667
+ else {
668
+ filterPluginRef.current = null;
669
+ }
670
+ // 3. Manage internal SelectionPlugin
671
+ if (selectionProps === null || selectionProps === void 0 ? void 0 : selectionProps.onSelectionChange) {
672
+ if (!selectionPluginRef.current) {
673
+ selectionPluginRef.current = new SelectionPlugin();
674
+ }
675
+ if (!newActivePlugins.some(p => p.id === 'selection')) {
676
+ newActivePlugins.push(selectionPluginRef.current);
677
+ }
678
+ }
679
+ else {
680
+ selectionPluginRef.current = null;
681
+ }
682
+ // 4. Manage internal SortPlugin
683
+ const isAnyColumnSortable = columnDefinitions.some(col => {
684
+ const rawCol = getRawColumnDefinition(col);
685
+ return rawCol.sortComparer || rawCol.getSortableValue;
686
+ });
687
+ if (isAnyColumnSortable) {
688
+ if (!sortPluginRef.current) {
689
+ sortPluginRef.current = new SortPlugin(sortProps);
690
+ }
691
+ if (!newActivePlugins.some(p => p.id === 'sort')) {
692
+ newActivePlugins.push(sortPluginRef.current);
693
+ }
694
+ }
695
+ else {
696
+ sortPluginRef.current = null;
697
+ }
698
+ setActivePlugins(newActivePlugins);
699
+ const api = {
700
+ getData: () => data,
701
+ forceUpdate: forceUpdatePlugins,
702
+ getScrollableElement: getScrollableElement,
703
+ infiniteScrollProps: infiniteScrollProps,
704
+ filterProps: filterProps,
705
+ selectionProps: selectionProps,
706
+ columnDefinitions: columnDefinitions,
707
+ };
708
+ // Initialize/Refresh all active plugins with the current API
709
+ newActivePlugins.forEach((plugin) => {
710
+ if (plugin.onPluginInit) {
711
+ plugin.onPluginInit(api);
712
+ }
713
+ });
714
+ // Run the data processing pipeline
715
+ let currentProcessedData = [...data];
716
+ newActivePlugins.forEach((plugin) => {
717
+ if (plugin.processData) {
718
+ currentProcessedData = plugin.processData(currentProcessedData);
719
+ }
720
+ });
721
+ return currentProcessedData;
722
+ }, [
723
+ data,
724
+ plugins,
725
+ filterProps,
726
+ selectionProps,
727
+ sortProps,
728
+ columnDefinitions,
729
+ getScrollableElement,
730
+ infiniteScrollProps,
731
+ getRawColumnDefinition,
732
+ ]);
733
+ const forceUpdatePlugins = useCallback(() => {
734
+ setProcessedData(initializePlugins());
735
+ }, [initializePlugins]);
736
+ // Handle re-initialization when props change
737
+ useEffect(() => {
738
+ setProcessedData(initializePlugins());
739
+ }, [initializePlugins]);
740
+ return { processedData, activePlugins, visibleColumns, forceUpdatePlugins };
741
+ };
742
+
743
+ function InfiniteTable(props) {
744
+ const { columnDefinitions, data, noDataComponent, maxHeight, onRowClick, footerRows, mobileBreakpoint, plugins, enablePageLevelStickyHeader, infiniteScrollProps, filterProps, selectionProps, animationProps, sortProps, } = props;
745
+ const tableContainerRef = useRef(null);
746
+ const headerRef = useRef(null);
747
+ const { isMobile, isHeaderSticky, debouncedScrollHandler } = useResponsiveTable({
748
+ mobileBreakpoint,
749
+ enablePageLevelStickyHeader,
750
+ maxHeight,
751
+ headerRef,
752
+ scrollableRef: tableContainerRef,
753
+ });
754
+ const [internalData, setInternalData] = useState(data || []);
755
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
756
+ const [internalHasMore, setInternalHasMore] = useState(true);
757
+ const getScrollableElement = useCallback(() => tableContainerRef.current, []);
758
+ const { processedData, activePlugins, visibleColumns } = useTablePlugins({
759
+ data: internalData,
760
+ plugins,
761
+ filterProps,
762
+ selectionProps,
763
+ sortProps,
764
+ columnDefinitions,
765
+ getScrollableElement,
766
+ infiniteScrollProps,
767
+ });
768
+ const currentHasMore = (infiniteScrollProps === null || infiniteScrollProps === void 0 ? void 0 : infiniteScrollProps.hasMore) !== undefined
769
+ ? infiniteScrollProps.hasMore
770
+ : internalHasMore;
771
+ const hasData = useMemo(() => processedData.length > 0, [processedData]);
772
+ const noDataComponentNode = noDataComponent || React.createElement("div", { className: styles$2.noData }, "No data");
773
+ const defaultLoadingComponent = React.createElement(LoadingSpinner, null);
774
+ const defaultNoMoreDataComponent = React.createElement(NoMoreDataMessage, null);
775
+ const loadMoreData = useCallback(() => __awaiter(this, void 0, void 0, function* () {
776
+ var _a;
777
+ if (!infiniteScrollProps || isLoadingMore)
778
+ return;
779
+ setIsLoadingMore(true);
780
+ try {
781
+ const newItems = yield ((_a = infiniteScrollProps.onLoadMore) === null || _a === void 0 ? void 0 : _a.call(infiniteScrollProps, internalData));
782
+ if (infiniteScrollProps.hasMore === undefined) {
783
+ if (!newItems || newItems.length === 0) {
784
+ setInternalHasMore(false);
785
+ }
786
+ }
787
+ if (newItems && newItems.length > 0) {
788
+ setInternalData(prevData => [...prevData, ...newItems]);
789
+ }
790
+ }
791
+ catch (error) {
792
+ console.error("Failed to load more items for infinite scroll:", error);
793
+ }
794
+ finally {
795
+ setIsLoadingMore(false);
796
+ }
797
+ }), [infiniteScrollProps, isLoadingMore, internalData]);
798
+ useEffect(() => {
799
+ setInternalData(data || []);
800
+ }, [data]);
801
+ useEffect(() => {
802
+ if (data.length === 0) {
803
+ loadMoreData();
804
+ }
805
+ }, [data.length, loadMoreData]);
806
+ const handleScrollForInfinite = useCallback((currentTarget) => {
807
+ if (!currentTarget)
808
+ return;
809
+ const { scrollHeight, scrollTop, clientHeight } = currentTarget;
810
+ if (currentHasMore && !isLoadingMore && scrollHeight - scrollTop - clientHeight < 100) {
811
+ loadMoreData();
812
+ }
813
+ }, [currentHasMore, isLoadingMore, loadMoreData]);
814
+ const mobileFooter = useMemo(() => {
815
+ if (!footerRows || footerRows.length === 0) {
816
+ return null;
817
+ }
818
+ // Helper to get raw column definition in this context
819
+ const getRaw = (colDef) => {
820
+ if (typeof colDef === 'function') {
821
+ return processedData.length > 0 ? colDef(processedData[0], 0) : { displayLabel: '', cellRenderer: () => '' };
822
+ }
823
+ return colDef;
824
+ };
825
+ return (React.createElement("div", { className: styles$2.footerCard },
826
+ React.createElement("div", { className: styles$2['footer-card-body'] }, footerRows.map((row, rowIndex) => {
827
+ let currentColumnIndex = 0;
828
+ return (React.createElement("div", { key: rowIndex }, row.columns.map((col, colIndex) => {
829
+ let label = col.displayLabel;
830
+ if (!label && col.colSpan === 1) {
831
+ const header = columnDefinitions[currentColumnIndex];
832
+ if (header) {
833
+ label = getRaw(header).displayLabel;
834
+ }
835
+ }
836
+ currentColumnIndex += col.colSpan;
837
+ return (React.createElement("p", { key: colIndex, className: `${styles$2['footer-card-row']} ${col.className || ''} ${col.onCellClick ? styles$2.clickableFooterCell : ''}`, onClick: col.onCellClick },
838
+ label && React.createElement("span", { className: styles$2['card-label'] }, label),
839
+ React.createElement("span", { className: styles$2['card-value'] }, col.cellRenderer())));
840
+ })));
841
+ }))));
842
+ }, [footerRows, columnDefinitions, processedData]);
843
+ const renderPluginHeaders = useCallback(() => {
844
+ if (!activePlugins) {
845
+ return null;
846
+ }
847
+ return activePlugins.map((plugin) => {
848
+ if (plugin.renderHeader) {
849
+ if (plugin.id === 'sort' && !isMobile) {
850
+ return null;
851
+ }
852
+ return React.createElement("div", { key: plugin.id }, plugin.renderHeader());
853
+ }
854
+ return null;
855
+ });
856
+ }, [activePlugins, isMobile]);
857
+ const renderPluginFooters = useCallback(() => {
858
+ if (!plugins) {
859
+ return null;
860
+ }
861
+ return plugins.map((plugin) => {
862
+ if (plugin.renderFooter) {
863
+ return React.createElement("div", { key: plugin.id + '-footer' }, plugin.renderFooter());
864
+ }
865
+ return null;
866
+ });
867
+ }, [plugins]);
868
+ const infiniteStatusUI = (React.createElement(React.Fragment, null,
869
+ isLoadingMore && ((infiniteScrollProps === null || infiniteScrollProps === void 0 ? void 0 : infiniteScrollProps.loadingMoreComponent) || defaultLoadingComponent),
870
+ !isLoadingMore && !currentHasMore && ((infiniteScrollProps === null || infiniteScrollProps === void 0 ? void 0 : infiniteScrollProps.noMoreDataComponent) || defaultNoMoreDataComponent)));
871
+ if ((animationProps === null || animationProps === void 0 ? void 0 : animationProps.isLoading) && !hasData) {
872
+ return React.createElement(SkeletonView, { isMobile: isMobile, columnDefinitions: visibleColumns });
873
+ }
874
+ return (React.createElement(TableProvider, { value: {
875
+ data: internalData,
876
+ processedData,
877
+ visibleColumns,
878
+ originalColumnDefinitions: columnDefinitions,
879
+ activePlugins,
880
+ onRowClick,
881
+ selectionProps,
882
+ animationProps,
883
+ } },
884
+ React.createElement("div", null,
885
+ React.createElement("div", { style: { display: 'flex', justifyContent: 'flex-end' } }, renderPluginHeaders()),
886
+ !hasData && noDataComponentNode,
887
+ hasData && (isMobile ? (React.createElement(MobileView, { mobileFooter: mobileFooter })) : (React.createElement(DesktopView, { maxHeight: maxHeight, isHeaderSticky: isHeaderSticky, tableContainerRef: tableContainerRef, headerRef: headerRef, footerRows: footerRows, renderPluginFooters: renderPluginFooters, onScroll: (e) => {
888
+ debouncedScrollHandler(e.currentTarget); // For sticky header
889
+ handleScrollForInfinite(e.currentTarget); // For infinite scroll
890
+ } }))),
891
+ hasData && infiniteStatusUI)));
892
+ }
893
+
894
+ function ResponsiveTable(props) {
895
+ const { columnDefinitions, data, noDataComponent, maxHeight, onRowClick, footerRows, mobileBreakpoint, plugins, enablePageLevelStickyHeader, infiniteScrollProps, filterProps, selectionProps, animationProps, sortProps, } = props;
896
+ const tableContainerRef = useRef(null);
897
+ const headerRef = useRef(null);
898
+ const { isMobile, isHeaderSticky } = useResponsiveTable({
899
+ mobileBreakpoint,
900
+ enablePageLevelStickyHeader,
901
+ maxHeight,
902
+ headerRef,
903
+ scrollableRef: tableContainerRef,
904
+ });
905
+ const getScrollableElement = useCallback(() => tableContainerRef.current, []);
906
+ const { processedData, activePlugins, visibleColumns } = useTablePlugins({
907
+ data,
908
+ plugins,
909
+ filterProps,
910
+ selectionProps,
911
+ sortProps,
912
+ columnDefinitions,
913
+ getScrollableElement,
914
+ infiniteScrollProps,
915
+ });
916
+ const hasData = useMemo(() => processedData.length > 0, [processedData]);
917
+ const noDataSvg = (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "#ccc", height: "40", width: "40", viewBox: "0 0 24 24" },
918
+ React.createElement("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-14h2v6h-2zm0 8h2v2h-2z" })));
919
+ const noDataComponentNode = noDataComponent || (React.createElement("div", { className: styles$2.noDataWrapper },
920
+ noDataSvg,
921
+ React.createElement("div", { className: styles$2.noData }, "No data")));
922
+ const mobileFooter = useMemo(() => {
923
+ if (!footerRows || footerRows.length === 0) {
924
+ return null;
925
+ }
926
+ // Helper to get raw column definition in this context
927
+ const getRaw = (colDef) => {
928
+ if (typeof colDef === 'function') {
929
+ return processedData.length > 0 ? colDef(processedData[0], 0) : { displayLabel: '', cellRenderer: () => '' };
930
+ }
931
+ return colDef;
932
+ };
933
+ return (React.createElement("div", { className: styles$2.footerCard },
934
+ React.createElement("div", { className: styles$2['footer-card-body'] }, footerRows.map((row, rowIndex) => {
935
+ let currentColumnIndex = 0;
936
+ return (React.createElement("div", { key: rowIndex }, row.columns.map((col, colIndex) => {
937
+ let label = col.displayLabel;
938
+ if (!label && col.colSpan === 1) {
939
+ const header = columnDefinitions[currentColumnIndex];
940
+ if (header) {
941
+ label = getRaw(header).displayLabel;
942
+ }
943
+ }
944
+ currentColumnIndex += col.colSpan;
945
+ return (React.createElement("p", { key: colIndex, className: `${styles$2['footer-card-row']} ${col.className || ''} ${col.onCellClick ? styles$2.clickableFooterCell : ''}`, onClick: col.onCellClick },
946
+ label && React.createElement("span", { className: styles$2['card-label'] }, label),
947
+ React.createElement("span", { className: styles$2['card-value'] }, col.cellRenderer())));
948
+ })));
949
+ }))));
950
+ }, [footerRows, columnDefinitions, processedData]);
951
+ const renderPluginHeaders = useCallback(() => {
952
+ if (!activePlugins) {
953
+ return null;
954
+ }
955
+ return activePlugins.map((plugin) => {
956
+ if (plugin.renderHeader) {
957
+ if (plugin.id === 'sort' && !isMobile) {
958
+ return null;
959
+ }
960
+ return React.createElement("div", { key: plugin.id }, plugin.renderHeader());
961
+ }
962
+ return null;
963
+ });
964
+ }, [activePlugins, isMobile]);
965
+ const renderPluginFooters = useCallback(() => {
966
+ if (!plugins) {
967
+ return null;
968
+ }
969
+ return plugins.map((plugin) => {
970
+ if (plugin.renderFooter) {
971
+ return React.createElement("div", { key: plugin.id + '-footer' }, plugin.renderFooter());
972
+ }
973
+ return null;
974
+ });
975
+ }, [plugins]);
976
+ if (infiniteScrollProps) {
977
+ return React.createElement(InfiniteTable, Object.assign({}, props));
978
+ }
979
+ if (animationProps === null || animationProps === void 0 ? void 0 : animationProps.isLoading) {
980
+ return React.createElement(SkeletonView, { isMobile: isMobile, columnDefinitions: visibleColumns });
981
+ }
982
+ return (React.createElement(TableProvider, { value: {
983
+ data,
984
+ processedData,
985
+ visibleColumns,
986
+ originalColumnDefinitions: columnDefinitions,
987
+ activePlugins,
988
+ onRowClick,
989
+ selectionProps,
990
+ animationProps,
991
+ } },
992
+ React.createElement("div", null,
993
+ React.createElement("div", { style: { display: 'flex', justifyContent: 'flex-end' } }, renderPluginHeaders()),
994
+ !hasData && noDataComponentNode,
995
+ hasData && isMobile && (React.createElement(MobileView, { mobileFooter: mobileFooter })),
996
+ hasData && !isMobile && (React.createElement(DesktopView, { maxHeight: maxHeight, isHeaderSticky: isHeaderSticky, tableContainerRef: tableContainerRef, headerRef: headerRef, footerRows: footerRows, renderPluginFooters: renderPluginFooters })))));
997
+ }
998
+
999
+ class InfiniteScrollPlugin {
1000
+ constructor() {
1001
+ this.id = 'infinite-scroll';
1002
+ this.isLoadingMore = false;
1003
+ this.onPluginInit = (api) => {
1004
+ this.api = api;
1005
+ this.attachScrollListener();
1006
+ };
1007
+ this.attachScrollListener = () => {
1008
+ var _a, _b;
1009
+ const scrollableElement = (_b = (_a = this.api).getScrollableElement) === null || _b === void 0 ? void 0 : _b.call(_a);
1010
+ if (scrollableElement) {
1011
+ scrollableElement.addEventListener('scroll', this.handleScroll);
1012
+ }
1013
+ };
1014
+ this.handleScroll = () => __awaiter(this, void 0, void 0, function* () {
1015
+ var _a, _b, _c, _d;
1016
+ const scrollableElement = (_b = (_a = this.api).getScrollableElement) === null || _b === void 0 ? void 0 : _b.call(_a);
1017
+ if (!scrollableElement || !this.api.infiniteScrollProps) {
1018
+ return;
1019
+ }
1020
+ const { scrollTop, scrollHeight, clientHeight } = scrollableElement;
1021
+ const scrollThreshold = 200; // Load more data when 200px from the bottom
1022
+ if (scrollHeight - scrollTop - clientHeight < scrollThreshold &&
1023
+ this.api.infiniteScrollProps.hasMore &&
1024
+ !this.isLoadingMore) {
1025
+ this.isLoadingMore = true;
1026
+ this.api.forceUpdate(); // Trigger re-render to show loading component
1027
+ yield ((_d = (_c = this.api.infiniteScrollProps).onLoadMore) === null || _d === void 0 ? void 0 : _d.call(_c, this.api.getData()));
1028
+ this.isLoadingMore = false;
1029
+ this.api.forceUpdate(); // Trigger re-render to hide loading component
1030
+ }
1031
+ });
1032
+ this.processData = (data) => {
1033
+ // This plugin doesn't modify the data directly, but rather triggers loading more.
1034
+ // The main component's data prop should be updated by the consumer of the table.
1035
+ return data;
1036
+ };
1037
+ this.renderFooter = () => {
1038
+ if (!this.api.infiniteScrollProps) {
1039
+ return null;
1040
+ }
1041
+ if (this.isLoadingMore) {
1042
+ return this.api.infiniteScrollProps.loadingMoreComponent || React.createElement("div", null, "Loading more...");
1043
+ }
1044
+ else if (!this.api.infiniteScrollProps.hasMore) {
1045
+ return this.api.infiniteScrollProps.noMoreDataComponent || React.createElement("div", null, "No more data.");
1046
+ }
1047
+ return null;
1048
+ };
1049
+ }
1050
+ }
1051
+
1052
+ export { FilterPlugin, InfiniteScrollPlugin, SelectionPlugin, SortPlugin, ResponsiveTable as default };
1053
+ //# sourceMappingURL=index.es.js.map