tp-react-elements 2.0.5 → 2.0.7

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,3 +1,3 @@
1
- var DateInput = {};
1
+ var Month = {};
2
2
 
3
- export { DateInput as __exports };
3
+ export { Month as __exports };
@@ -1,3 +1,3 @@
1
- var Month = {};
1
+ var DateInput = {};
2
2
 
3
- export { Month as __exports };
3
+ export { DateInput as __exports };
@@ -6,6 +6,7 @@ import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.e
6
6
  import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
7
7
  import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
8
8
  import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
9
+ import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
9
10
  import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
10
11
  import SingleSelect from '../FormComponents/Select/SingleSelect.esm.js';
11
12
  import MultiSelectAutocomplete from '../FormComponents/Select/MultiSelectAutocomplete.esm.js';
@@ -17,7 +18,6 @@ import DateRangePickerComponent from '../DateRangePicker/DateRangePicker.esm.js'
17
18
  import RichTextEditorWrapper from '../FormComponents/RichTextEditor/RichTextEditorWrapper.esm.js';
18
19
  import SingleSelectSearchApi from '../FormComponents/Select/SingleSelectSearchApi.esm.js';
19
20
  import DatetimepickerWrapper from '../FormComponents/DatePicker/DatetimepickerWrapper.esm.js';
20
- import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
21
21
 
22
22
  // Lazy-loaded heavy components
23
23
  // const RichTextEditorWrapper = lazy(() =>
@@ -1,6 +1,7 @@
1
1
  export { default as FormNumberField } from './FormNumberField/FormNumberField.esm.js';
2
2
  export { default as FormNumberFieldDecimal } from './FormNumberField/FormNumberFieldDecimal.esm.js';
3
3
  export { default as FormTextAreaField } from './FormTextAreaField/FormTextAreaField.esm.js';
4
+ export { default as FormTextField } from './FormTextField/FormTextField.esm.js';
4
5
  export { default as PasswordField } from './PasswordField/PasswordField.esm.js';
5
6
  export { default as FormCheckBox } from './FormCheckBox/FormCheckBox.esm.js';
6
7
  export { default as SingleSelect } from './Select/SingleSelect.esm.js';
@@ -14,6 +15,7 @@ export { default as FormRadioGroup } from './FormRadioGroup/FormRadioGroup.esm.j
14
15
  export { default as DatepickerWrapperV2 } from './DatePicker/DatepickerWrapperV2.esm.js';
15
16
  export { default as Monthpickerrender } from './DatePicker/Monthpickerrender.esm.js';
16
17
  export { default as YearPickerField } from './YearPickerField/YearPickerField.esm.js';
18
+ export { default as TimePicker } from './TimePicker/TimePicker.esm.js';
17
19
  export { default as FormRenderFileUpload } from './FileUpload/FormRenderFileUpload.esm.js';
18
20
  export { default as FormRenderMultiFileUpload } from './FileUpload/FormRenderMultiFileUpload.esm.js';
19
21
  import 'react/jsx-runtime';
@@ -32,8 +34,6 @@ import '@mui/icons-material/PictureAsPdf';
32
34
  import '@mui/icons-material/TableChart';
33
35
  import '@mui/icons-material/Description';
34
36
  export { default as FormErrorField } from './FormErrorField/FormErrorField.esm.js';
35
- export { default as FormTextField } from './FormTextField/FormTextField.esm.js';
36
37
  export { default as HelperText } from './HelperText/HelperText.esm.js';
37
38
  export { default as RichTextEditor } from './RichTextEditor/RichTextEditor.esm.js';
38
39
  export { default as RichTextEditorWrapper } from './RichTextEditor/RichTextEditorWrapper.esm.js';
39
- export { default as TimePicker } from './TimePicker/TimePicker.esm.js';
@@ -91,10 +91,32 @@ export interface GenericPaginatedGridProps<TData extends object> {
91
91
  * Use `grid-no-grow` only when you need fixed column widths with horizontal scroll.
92
92
  */
93
93
  layoutMode?: 'grid' | 'semantic' | 'grid-no-grow';
94
- /** Compact toolbar, filter chips, and footer. Default: `default`. */
94
+ /**
95
+ * Compact toolbar, filter chips, and footer.
96
+ * When omitted, the grid auto-selects `compact` at the `tablet`/`mobile`
97
+ * responsive tiers and `default` at `desktop`. Pass explicitly to opt out
98
+ * of that auto-switch.
99
+ */
95
100
  uiDensity?: GridUiDensity;
96
101
  /** Override individual sizes from the `uiDensity` preset. */
97
102
  uiDensityOptions?: GridUiDensityOptions;
103
+ /**
104
+ * Viewport-width cutoffs (px) for the three responsive tiers:
105
+ * `desktop` (above `tablet`), `tablet` (between `mobile` and `tablet`),
106
+ * and `mobile` (at/below `mobile`). Each tier gets progressively tighter
107
+ * defaults — density, filter-chip cap, toolbar wrap, footer layout.
108
+ * Default: `{ tablet: 1024, mobile: 640 }`.
109
+ */
110
+ responsiveBreakpoints?: {
111
+ tablet?: number;
112
+ mobile?: number;
113
+ };
114
+ /**
115
+ * Opt out of all automatic responsive-tier adjustments (density, filter
116
+ * cap, toolbar wrap, footer layout) — the grid always behaves as `desktop`.
117
+ * Default: `false`.
118
+ */
119
+ disableResponsive?: boolean;
98
120
  /** Choices shown in the "Rows per page" footer dropdown. Default: [1 000, 5 000, 10 000]. */
99
121
  pageSizeOptions?: number[];
100
122
  /**
@@ -210,5 +232,5 @@ export interface GenericPaginatedGridProps<TData extends object> {
210
232
  */
211
233
  enableExpanding?: boolean;
212
234
  }
213
- declare function GenericPaginatedGrid<TData extends object>({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize, exportFilename, gridId, tableHeight, tableMinHeight, tableMaxHeight, autoFillViewport, layoutMode, uiDensity, uiDensityOptions, pageSizeOptions, showGlobalSearchByDefault, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, renderDetailPanel, enableExpanding, }: GenericPaginatedGridProps<TData>): import("react/jsx-runtime").JSX.Element;
235
+ declare function GenericPaginatedGrid<TData extends object>({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize, exportFilename, gridId, tableHeight, tableMinHeight, tableMaxHeight, autoFillViewport, layoutMode, uiDensity, uiDensityOptions, responsiveBreakpoints, disableResponsive, pageSizeOptions, showGlobalSearchByDefault, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, renderDetailPanel, enableExpanding, }: GenericPaginatedGridProps<TData>): import("react/jsx-runtime").JSX.Element;
214
236
  export default GenericPaginatedGrid;
@@ -1,9 +1,8 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useMemo, useState, useRef, useLayoutEffect, useCallback } from 'react';
3
- import * as XLSX from 'xlsx-js-style';
2
+ import { useMemo, useState, useRef, useLayoutEffect, useCallback, useEffect } from 'react';
4
3
  import { MRT_ToggleGlobalFilterButton, MRT_ShowHideColumnsButton, MRT_ToggleDensePaddingButton, MRT_ToggleFullScreenButton, useMaterialReactTable, MaterialReactTable } from 'material-react-table';
5
4
  import { useQuery, keepPreviousData } from '@tanstack/react-query';
6
- import { Box, Tooltip, IconButton, GlobalStyles, Typography, Select, MenuItem, CircularProgress } from '@mui/material';
5
+ import { Box, Tooltip, IconButton, GlobalStyles, Typography, Select, MenuItem, Skeleton, CircularProgress } from '@mui/material';
7
6
  import RefreshIcon from '@mui/icons-material/Refresh';
8
7
  import FileDownloadIcon from '@mui/icons-material/FileDownload';
9
8
  import FirstPageIcon from '@mui/icons-material/FirstPage';
@@ -11,6 +10,7 @@ import LastPageIcon from '@mui/icons-material/LastPage';
11
10
  import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
12
11
  import ChevronRightIcon from '@mui/icons-material/ChevronRight';
13
12
  import DynamicFilterBar from './DynamicFilterBar.esm.js';
13
+ import { getXLSX } from '../../utils/Constants/xlsx-js-style.esm.js';
14
14
 
15
15
  const UI_DENSITY_PRESETS = {
16
16
  default: {
@@ -194,27 +194,60 @@ const OverlayLoader = ({ message }) => (jsxs(Box, { sx: {
194
194
  bgcolor: 'rgba(255,255,255,0.80)', backdropFilter: 'blur(2px)',
195
195
  zIndex: 10, gap: 1.5,
196
196
  }, children: [jsx(CircularProgress, { size: 50, thickness: 4 }), jsx(Typography, { variant: "body2", color: "text.secondary", fontWeight: 500, children: message })] }));
197
- const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoading, canPrev, canNext, onFirst, onPrev, onNext, onLast, onPageSizeChange, pageSizeOptions, density, gridId, }) => {
197
+ // ─── Internal: skeleton loader (initial load — no data on screen yet) ────────
198
+ const SkeletonOverlay = ({ columnCount, rowCount = 10, }) => (jsx(Box, { sx: {
199
+ position: 'absolute', inset: 0,
200
+ display: 'flex', flexDirection: 'column',
201
+ bgcolor: 'background.paper',
202
+ zIndex: 10, p: 1.5, gap: 1,
203
+ overflow: 'hidden',
204
+ }, children: Array.from({ length: rowCount }).map((_, rowIdx) => (jsx(Box, { sx: { display: 'flex', gap: 1.5 }, children: Array.from({ length: Math.max(columnCount, 1) }).map((_, colIdx) => (jsx(Skeleton, { variant: "rounded", height: 28, sx: { flex: 1, minWidth: 0 }, animation: "wave" }, colIdx))) }, rowIdx))) }));
205
+ const useResponsiveTier = (tabletBreakpoint, mobileBreakpoint, enabled) => {
206
+ const [tier, setTier] = useState('desktop');
207
+ useEffect(() => {
208
+ if (!enabled || typeof window === 'undefined') {
209
+ setTier('desktop');
210
+ return;
211
+ }
212
+ const mqlTablet = window.matchMedia(`(max-width: ${tabletBreakpoint}px)`);
213
+ const mqlMobile = window.matchMedia(`(max-width: ${mobileBreakpoint}px)`);
214
+ const update = () => {
215
+ setTier(mqlMobile.matches ? 'mobile' : mqlTablet.matches ? 'tablet' : 'desktop');
216
+ };
217
+ update();
218
+ mqlTablet.addEventListener('change', update);
219
+ mqlMobile.addEventListener('change', update);
220
+ return () => {
221
+ mqlTablet.removeEventListener('change', update);
222
+ mqlMobile.removeEventListener('change', update);
223
+ };
224
+ }, [tabletBreakpoint, mobileBreakpoint, enabled]);
225
+ return enabled ? tier : 'desktop';
226
+ };
227
+ const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoading, canPrev, canNext, onFirst, onPrev, onNext, onLast, onPageSizeChange, pageSizeOptions, density, gridId, isStacked, }) => {
198
228
  const fs = density.footerFontSize;
199
229
  const selectH = density.footerMinHeight - 8;
200
230
  const startRow = totalRecords === 0 ? 0 : pageIndex * pageSize + 1;
201
231
  const endRow = Math.min((pageIndex + 1) * pageSize, totalRecords);
202
232
  return (jsxs(Box, { sx: {
203
- display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
233
+ display: 'flex',
234
+ flexDirection: isStacked ? 'column' : 'row',
235
+ alignItems: isStacked ? 'stretch' : 'center',
236
+ justifyContent: isStacked ? 'center' : 'flex-end',
204
237
  gap: { xs: 1, md: 2 }, px: 1.5, py: density.footerPaddingY,
205
238
  borderTop: '1px solid', borderColor: 'divider',
206
239
  bgcolor: 'background.paper', flexWrap: 'wrap',
207
240
  minHeight: density.footerMinHeight,
208
241
  flexShrink: 0,
209
- }, children: [jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.75 }, children: [jsx(Typography, { sx: { fontSize: fs }, color: "text.secondary", noWrap: true, children: "Rows per page:" }), jsx(Select, { value: pageSize, onChange: (e) => onPageSizeChange(Number(e.target.value)), inputProps: { id: `${gridId}_page_size_select` }, size: "small", variant: "outlined", sx: {
242
+ }, children: [jsxs(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: isStacked ? 'center' : 'flex-start', gap: 0.75 }, children: [jsx(Typography, { sx: { fontSize: fs }, color: "text.secondary", noWrap: true, children: "Rows per page:" }), jsx(Select, { value: pageSize, onChange: (e) => onPageSizeChange(Number(e.target.value)), inputProps: { id: `${gridId}_page_size_select` }, size: "small", variant: "outlined", sx: {
210
243
  height: selectH, fontSize: fs - 1, minWidth: 72,
211
244
  '.MuiSelect-select': { py: '2px !important' },
212
245
  '.MuiOutlinedInput-notchedOutline': { borderColor: 'divider' },
213
- }, children: pageSizeOptions.map((opt) => (jsx(MenuItem, { value: opt, sx: { fontSize: fs - 1 }, children: opt.toLocaleString() }, opt))) })] }), jsx(Typography, { sx: { fontSize: fs }, color: "text.secondary", noWrap: true, children: isLoading || (isFetching && totalRecords === 0)
246
+ }, children: pageSizeOptions.map((opt) => (jsx(MenuItem, { value: opt, sx: { fontSize: fs - 1 }, children: opt.toLocaleString() }, opt))) })] }), jsx(Typography, { sx: { fontSize: fs, textAlign: isStacked ? 'center' : 'left' }, color: "text.secondary", noWrap: true, children: isLoading || (isFetching && totalRecords === 0)
214
247
  ? 'Loading…'
215
248
  : totalRecords === 0
216
249
  ? 'No records'
217
- : `${startRow.toLocaleString()}–${endRow.toLocaleString()} of ${totalRecords.toLocaleString()}` }), jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.25 }, children: [jsx(Tooltip, { title: "First page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_first_page_icon`, size: "small", onClick: onFirst, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(FirstPageIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Tooltip, { title: "Previous page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_prev_page_icon`, size: "small", onClick: onPrev, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(ChevronLeftIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Typography, { fontWeight: 600, sx: {
250
+ : `${startRow.toLocaleString()}–${endRow.toLocaleString()} of ${totalRecords.toLocaleString()}` }), jsxs(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: isStacked ? 'center' : 'flex-start', gap: 0.25 }, children: [jsx(Tooltip, { title: "First page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_first_page_icon`, size: "small", onClick: onFirst, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(FirstPageIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Tooltip, { title: "Previous page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_prev_page_icon`, size: "small", onClick: onPrev, disabled: !canPrev, sx: { p: 0.5 }, children: jsx(ChevronLeftIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Typography, { fontWeight: 600, sx: {
218
251
  mx: 0.5,
219
252
  minWidth: density.footerMinHeight - 12,
220
253
  height: density.footerMinHeight - 12,
@@ -224,13 +257,29 @@ const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoa
224
257
  }, children: pageIndex + 1 }), jsx(Tooltip, { title: "Next page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_next_page_icon`, size: "small", onClick: onNext, disabled: !canNext, sx: { p: 0.5 }, children: jsx(ChevronRightIcon, { sx: { fontSize: density.footerIconSize } }) }) }) }), jsx(Tooltip, { title: "Last page", children: jsx("span", { children: jsx(IconButton, { id: `${gridId}_last_page_icon`, size: "small", onClick: onLast, disabled: !canNext, sx: { p: 0.5 }, children: jsx(LastPageIcon, { sx: { fontSize: density.footerIconSize } }) }) }) })] })] }));
225
258
  };
226
259
  // ─── GenericPaginatedGrid ─────────────────────────────────────────────────────
227
- function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize = 10000, exportFilename = 'export', gridId, tableHeight, tableMinHeight = '300px', tableMaxHeight, autoFillViewport = true, layoutMode = 'grid', uiDensity = 'default', uiDensityOptions, pageSizeOptions = [1000, 5000, 10000], showGlobalSearchByDefault = false, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters = 4, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, renderDetailPanel, enableExpanding = !!renderDetailPanel, }) {
228
- const densityTokens = useMemo(() => ({ ...UI_DENSITY_PRESETS[uiDensity], ...uiDensityOptions }), [uiDensity, uiDensityOptions]);
260
+ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize = 10000, exportFilename = 'export', gridId, tableHeight, tableMinHeight = '300px', tableMaxHeight, autoFillViewport = true, layoutMode = 'grid', uiDensity, uiDensityOptions, responsiveBreakpoints, disableResponsive = false, pageSizeOptions = [1000, 5000, 10000], showGlobalSearchByDefault = false, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters = 4, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, renderDetailPanel, enableExpanding = !!renderDetailPanel, }) {
261
+ // ── Responsive tier detection ────────────────────────────────────────────────
262
+ // Three tiers (desktop / tablet / mobile) drive auto density, a progressively
263
+ // lower filter-chip cap, toolbar wrapping, and a stacked pagination footer —
264
+ // unless the consumer opts out via `disableResponsive` or pins `uiDensity`
265
+ // explicitly (density only).
266
+ const tier = useResponsiveTier(responsiveBreakpoints?.tablet ?? 1024, responsiveBreakpoints?.mobile ?? 640, !disableResponsive);
267
+ const isNarrow = tier !== 'desktop'; // tablet or mobile — density, toolbar wrap
268
+ const isMobileTier = tier === 'mobile'; // mobile only — footer stacking, tightest chip cap
269
+ const resolvedUiDensity = uiDensity ?? (isNarrow ? 'compact' : 'default');
270
+ const effectiveMaxVisibleFilters = disableResponsive
271
+ ? maxVisibleFilters
272
+ : tier === 'mobile'
273
+ ? Math.min(maxVisibleFilters, 2)
274
+ : tier === 'tablet'
275
+ ? Math.min(maxVisibleFilters, 3)
276
+ : maxVisibleFilters;
277
+ const densityTokens = useMemo(() => ({ ...UI_DENSITY_PRESETS[resolvedUiDensity], ...uiDensityOptions }), [resolvedUiDensity, uiDensityOptions]);
229
278
  const resolvedGridId = gridId ?? exportFilename;
230
- const resolvedHeaderMinHeight = toCssSize(headerMinHeight, uiDensity === 'compact' ? '34px' : '40px');
231
- const resolvedHeaderMaxHeight = toCssSize(headerMaxHeight, uiDensity === 'compact' ? '56px' : '72px');
232
- const resolvedHeaderFontSize = toCssSize(headerFontSize ?? densityTokens.headerFontSize, uiDensity === 'compact' ? '11px' : '12px');
233
- const resolvedBodyFontSize = toCssSize(bodyFontSize ?? densityTokens.bodyFontSize, uiDensity === 'compact' ? '11px' : '12px');
279
+ const resolvedHeaderMinHeight = toCssSize(headerMinHeight, resolvedUiDensity === 'compact' ? '34px' : '40px');
280
+ const resolvedHeaderMaxHeight = toCssSize(headerMaxHeight, resolvedUiDensity === 'compact' ? '56px' : '72px');
281
+ const resolvedHeaderFontSize = toCssSize(headerFontSize ?? densityTokens.headerFontSize, resolvedUiDensity === 'compact' ? '11px' : '12px');
282
+ const resolvedBodyFontSize = toCssSize(bodyFontSize ?? densityTokens.bodyFontSize, resolvedUiDensity === 'compact' ? '11px' : '12px');
234
283
  const tableStyleOverrides = useMemo(() => ({
235
284
  // Static rules folded in here so the whole object comes from one useMemo
236
285
  // and GlobalStyles receives a stable reference unless these props change.
@@ -239,6 +288,17 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
239
288
  '.tp-gpg-root button[aria-label="Move"]': { display: 'none !important' },
240
289
  '.tp-gpg-root [data-column-id="mrt-row-spacer"]': { display: 'none !important' },
241
290
  '.tp-gpg-root .Mui-TableHeadCell-ColumnSpacer': { display: 'none !important' },
291
+ // Visible horizontal scrollbar — touch devices (iOS) hide scrollbars by
292
+ // default, leaving no affordance that a horizontally-overflowing table
293
+ // (common on narrow viewports) can be scrolled.
294
+ '.tp-gpg-root .MuiTableContainer-root::-webkit-scrollbar': {
295
+ height: 6,
296
+ width: 6,
297
+ },
298
+ '.tp-gpg-root .MuiTableContainer-root::-webkit-scrollbar-thumb': {
299
+ backgroundColor: 'rgba(0,0,0,0.25)',
300
+ borderRadius: 4,
301
+ },
242
302
  ...buildHeaderGlobalStyles(resolvedHeaderMinHeight, resolvedHeaderMaxHeight, resolvedHeaderFontSize, headerBgColor),
243
303
  ...buildBodyGlobalStyles(resolvedBodyFontSize),
244
304
  }), [
@@ -407,10 +467,15 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
407
467
  const handleToggleFilter = useCallback((id) => setVisibleFilterIds((prev) => {
408
468
  if (prev.includes(id))
409
469
  return prev.filter((v) => v !== id); // always allow remove
410
- if (prev.length >= maxVisibleFilters)
470
+ if (prev.length >= effectiveMaxVisibleFilters)
411
471
  return prev; // cap reached — ignore add
412
472
  return [...prev, id];
413
- }), [maxVisibleFilters]);
473
+ }), [effectiveMaxVisibleFilters]);
474
+ // Trim already-visible chips down to the new cap when the viewport narrows
475
+ // (e.g. resizing from desktop to mobile with 4 chips already shown).
476
+ useEffect(() => {
477
+ setVisibleFilterIds((prev) => prev.length > effectiveMaxVisibleFilters ? prev.slice(0, effectiveMaxVisibleFilters) : prev);
478
+ }, [effectiveMaxVisibleFilters]);
414
479
  const handleClearAll = useCallback(() => {
415
480
  setFilterValues(defaultFilters);
416
481
  setColumnFilters([]);
@@ -418,7 +483,10 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
418
483
  setGlobalFilter('');
419
484
  setPagination((p) => ({ ...p, pageIndex: 0 }));
420
485
  }, [defaultFilters, defaultVisible]);
421
- const handleExport = useCallback(() => {
486
+ const handleExport = useCallback(async () => {
487
+ // Loaded on demand (and cached) so the xlsx-js-style library is excluded
488
+ // from the initial bundle for consumers who never click Export.
489
+ const XLSX = await getXLSX();
422
490
  const dataCols = columns.filter((c) => 'accessorKey' in c && c.accessorKey);
423
491
  // xlsx-js-style requires 8-char ARGB (AA + RRGGBB). Prepend full-opacity "FF".
424
492
  const toArgb = (hex, fallback = 'FF1565C0') => hex?.startsWith('#') ? 'FF' + hex.slice(1).toUpperCase().padEnd(6, '0') : fallback;
@@ -516,15 +584,20 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
516
584
  // ── Memoized toolbar render functions ─────────────────────────────────────
517
585
  // Defined with useCallback so MRT receives stable references and does not
518
586
  // re-render the toolbar on every isFetching / data update cycle.
519
- const renderTopToolbarCustomActions = useCallback(() => (jsxs(Box, { sx: { display: 'flex', alignItems: 'center', flex: 1, minWidth: 0, pr: 1, gap: 0.5 }, children: [jsxs(Box, { sx: { display: 'flex', alignItems: 'center', flexShrink: 0, gap: 0.25 }, children: [jsx(Tooltip, { title: "Clear filters & refresh", children: jsx("span", { children: jsx(IconButton, { id: `${resolvedGridId}_refresh_icon`, size: "small", onClick: () => { handleClearAll(); refetch(); }, disabled: isFetching, sx: { p: uiDensity === 'compact' ? 0.35 : 0.5 }, children: jsx(RefreshIcon, { sx: { fontSize: uiDensity === 'compact' ? 18 : 20 } }) }) }) }), jsx(Tooltip, { title: "Export batch as Excel (.xlsx)", children: jsx("span", { children: jsx(IconButton, { id: `${resolvedGridId}_export_icon`, size: "small", onClick: handleExport, disabled: rows.length === 0 || isFetching, sx: { p: uiDensity === 'compact' ? 0.35 : 0.5 }, children: jsx(FileDownloadIcon, { sx: { fontSize: uiDensity === 'compact' ? 18 : 20 } }) }) }) })] }), jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: jsx(DynamicFilterBar, { serverFilterDefs: serverFilterDefs, visibleFilterIds: visibleFilterIds, onToggleFilter: handleToggleFilter, filterValues: filterValues, onFilterValuesChange: handleFilterValuesChange, columnFilters: columnFilters, onColumnFilterChange: setColumnFilters, availableColumns: availableColumns, maxVisibleFilters: maxVisibleFilters, uiDensity: uiDensity, filterChipFontSize: densityTokens.filterChipFontSize, filterChipMinHeight: densityTokens.filterChipMinHeight, filterLabelFontSize: densityTokens.filterLabelFontSize, gridId: resolvedGridId }) })] })), [
520
- resolvedGridId, isFetching, rows.length, uiDensity,
587
+ const renderTopToolbarCustomActions = useCallback(() => (jsxs(Box, { sx: {
588
+ display: 'flex',
589
+ alignItems: 'center',
590
+ flexWrap: isNarrow ? 'wrap' : 'nowrap',
591
+ flex: 1, minWidth: 0, pr: 1, gap: 0.5,
592
+ }, children: [jsxs(Box, { sx: { display: 'flex', alignItems: 'center', flexShrink: 0, gap: 0.25 }, children: [jsx(Tooltip, { title: "Clear filters & refresh", children: jsx("span", { children: jsx(IconButton, { id: `${resolvedGridId}_refresh_icon`, size: "small", onClick: () => { handleClearAll(); refetch(); }, disabled: isFetching, sx: { p: resolvedUiDensity === 'compact' ? 0.35 : 0.5 }, children: jsx(RefreshIcon, { sx: { fontSize: resolvedUiDensity === 'compact' ? 18 : 20 } }) }) }) }), jsx(Tooltip, { title: "Export batch as Excel (.xlsx)", children: jsx("span", { children: jsx(IconButton, { id: `${resolvedGridId}_export_icon`, size: "small", onClick: handleExport, disabled: rows.length === 0 || isFetching, sx: { p: resolvedUiDensity === 'compact' ? 0.35 : 0.5 }, children: jsx(FileDownloadIcon, { sx: { fontSize: resolvedUiDensity === 'compact' ? 18 : 20 } }) }) }) })] }), jsx(Box, { sx: { flex: 1, minWidth: isNarrow ? '100%' : 0 }, children: jsx(DynamicFilterBar, { serverFilterDefs: serverFilterDefs, visibleFilterIds: visibleFilterIds, onToggleFilter: handleToggleFilter, filterValues: filterValues, onFilterValuesChange: handleFilterValuesChange, columnFilters: columnFilters, onColumnFilterChange: setColumnFilters, availableColumns: availableColumns, maxVisibleFilters: effectiveMaxVisibleFilters, uiDensity: resolvedUiDensity, filterChipFontSize: densityTokens.filterChipFontSize, filterChipMinHeight: densityTokens.filterChipMinHeight, filterLabelFontSize: densityTokens.filterLabelFontSize, gridId: resolvedGridId }) })] })), [
593
+ resolvedGridId, isFetching, rows.length, resolvedUiDensity, isNarrow,
521
594
  handleClearAll, refetch, handleExport,
522
595
  serverFilterDefs, visibleFilterIds, handleToggleFilter,
523
596
  filterValues, handleFilterValuesChange, columnFilters, setColumnFilters,
524
- availableColumns, maxVisibleFilters, densityTokens, handleToggleFilter,
597
+ availableColumns, effectiveMaxVisibleFilters, densityTokens, handleToggleFilter,
525
598
  ]);
526
599
  const renderToolbarInternalActions = useCallback(({ table: t }) => (jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.5 }, children: [jsx(MRT_ToggleGlobalFilterButton, { table: t, id: `${resolvedGridId}_global_search_icon` }), jsx(MRT_ShowHideColumnsButton, { table: t, id: `${resolvedGridId}_columns_icon` }), jsx(MRT_ToggleDensePaddingButton, { table: t, id: `${resolvedGridId}_density_icon` }), jsx(MRT_ToggleFullScreenButton, { table: t, id: `${resolvedGridId}_fullscreen_icon` })] })), [resolvedGridId]);
527
- const renderBottomToolbar = useCallback(({ table: t }) => (jsx(PaginationFooter, { pageIndex: t.getState().pagination.pageIndex, pageSize: t.getState().pagination.pageSize, totalRecords: totalRecords, isFetching: isFetching, isLoading: isLoading, canPrev: t.getCanPreviousPage() && !isFetching, canNext: t.getCanNextPage() && !isFetching, onFirst: () => t.firstPage(), onPrev: () => t.previousPage(), onNext: () => t.nextPage(), onLast: () => t.lastPage(), onPageSizeChange: (s) => t.setPageSize(s), pageSizeOptions: pageSizeOptions, density: densityTokens, gridId: resolvedGridId })), [totalRecords, isFetching, isLoading, pageSizeOptions, densityTokens, resolvedGridId]);
600
+ const renderBottomToolbar = useCallback(({ table: t }) => (jsx(PaginationFooter, { pageIndex: t.getState().pagination.pageIndex, pageSize: t.getState().pagination.pageSize, totalRecords: totalRecords, isFetching: isFetching, isLoading: isLoading, canPrev: t.getCanPreviousPage() && !isFetching, canNext: t.getCanNextPage() && !isFetching, onFirst: () => t.firstPage(), onPrev: () => t.previousPage(), onNext: () => t.nextPage(), onLast: () => t.lastPage(), onPageSizeChange: (s) => t.setPageSize(s), pageSizeOptions: pageSizeOptions, density: densityTokens, gridId: resolvedGridId, isStacked: isMobileTier })), [totalRecords, isFetching, isLoading, pageSizeOptions, densityTokens, resolvedGridId, isMobileTier]);
528
601
  // ── Table instance ─────────────────────────────────────────────────────────
529
602
  const table = useMaterialReactTable({
530
603
  columns: processedColumns,
@@ -621,6 +694,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
621
694
  minHeight: 0,
622
695
  maxHeight: '100%',
623
696
  overflow: 'auto',
697
+ WebkitOverflowScrolling: 'touch',
624
698
  },
625
699
  },
626
700
  muiTablePaperProps: {
@@ -636,10 +710,10 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
636
710
  muiTopToolbarProps: {
637
711
  sx: {
638
712
  alignItems: 'center',
639
- flexWrap: 'nowrap',
713
+ flexWrap: isNarrow ? 'wrap' : 'nowrap',
640
714
  minHeight: densityTokens.filterChipMinHeight + 12,
641
- py: uiDensity === 'compact' ? 0.25 : 0.5,
642
- '& .MuiIconButton-root': { p: uiDensity === 'compact' ? 0.35 : 0.5 },
715
+ py: resolvedUiDensity === 'compact' ? 0.25 : 0.5,
716
+ '& .MuiIconButton-root': { p: resolvedUiDensity === 'compact' ? 0.35 : 0.5 },
643
717
  '& .MuiIconButton-root svg': { fontSize: densityTokens.toolbarIconFontSize === 'small' ? 18 : 20 },
644
718
  },
645
719
  },
@@ -650,7 +724,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
650
724
  size: 'small',
651
725
  variant: 'outlined',
652
726
  sx: {
653
- minWidth: uiDensity === 'compact' ? 160 : 200,
727
+ minWidth: resolvedUiDensity === 'compact' ? 160 : 200,
654
728
  '& .MuiOutlinedInput-root': {
655
729
  height: densityTokens.filterChipMinHeight,
656
730
  fontSize: densityTokens.filterChipFontSize,
@@ -700,7 +774,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
700
774
  display: 'flex',
701
775
  flexDirection: 'column',
702
776
  overflow: 'hidden',
703
- }, children: [jsx(GlobalStyles, { styles: tableStyleOverrides }), isLoading && jsx(OverlayLoader, { message: "Loading data\u2026" }), !isLoading && isFetching && jsx(OverlayLoader, { message: "Updating data\u2026" }), jsx(Box, { sx: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: jsx(MaterialReactTable, { table: table }) })] }));
777
+ }, children: [jsx(GlobalStyles, { styles: tableStyleOverrides }), isLoading && jsx(SkeletonOverlay, { columnCount: processedColumns.length }), !isLoading && isFetching && jsx(OverlayLoader, { message: "Updating data\u2026" }), jsx(Box, { sx: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: jsx(MaterialReactTable, { table: table }) })] }));
704
778
  }
705
779
 
706
780
  export { GenericPaginatedGrid as default };
@@ -1,4 +1,4 @@
1
- import { __exports as DateInput } from '../../../../../_virtual/index.esm13.js';
1
+ import { __exports as DateInput } from '../../../../../_virtual/index.esm14.js';
2
2
  import React__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireClassnames } from '../../../../classnames/index.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as Month } from '../../../../../_virtual/index.esm14.js';
1
+ import { __exports as Month } from '../../../../../_virtual/index.esm13.js';
2
2
  import React__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireDayCell } from '../DayCell/index.esm.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tp-react-elements",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "React form components library built with React Hook Form and Yup",