tp-react-elements 3.0.26 → 3.0.27

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 DayCell = {};
1
+ var InputRangeField = {};
2
2
 
3
- export { DayCell as __exports };
3
+ export { InputRangeField as __exports };
@@ -1,3 +1,3 @@
1
- var Month = {};
1
+ var DayCell = {};
2
2
 
3
- export { Month as __exports };
3
+ export { DayCell as __exports };
@@ -1,3 +1,3 @@
1
- var InputRangeField = {};
1
+ var Month = {};
2
2
 
3
- export { InputRangeField as __exports };
3
+ export { Month as __exports };
@@ -1,3 +1,3 @@
1
- var DateRangePicker = {};
1
+ var DefinedRange = {};
2
2
 
3
- export { DateRangePicker as __exports };
3
+ export { DefinedRange as __exports };
@@ -1,3 +1,3 @@
1
- var DateRange = {};
1
+ var DateRangePicker = {};
2
2
 
3
- export { DateRange as __exports };
3
+ export { DateRangePicker as __exports };
@@ -1,3 +1,3 @@
1
- var Calendar = {};
1
+ var DateRange = {};
2
2
 
3
- export { Calendar as __exports };
3
+ export { DateRange as __exports };
@@ -1,3 +1,3 @@
1
- var DefinedRange = {};
1
+ var Calendar = {};
2
2
 
3
- export { DefinedRange as __exports };
3
+ export { Calendar as __exports };
@@ -1,5 +1,13 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import require$$0__default, { lazy, Suspense } from 'react';
3
+ import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
4
+ import FormCheckBoxGroup from '../FormComponents/FormCheckBoxGroup/FormCheckBoxGroup.esm.js';
5
+ import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.esm.js';
6
+ import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
7
+ import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
8
+ import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
9
+ import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
10
+ import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
3
11
  import SingleSelect from '../FormComponents/Select/SingleSelect.esm.js';
4
12
  import MultiSelectAutocomplete from '../FormComponents/Select/MultiSelectAutocomplete.esm.js';
5
13
  import SingleSelectNonAutoComplete from '../FormComponents/Select/SingleSelectNonAutoComplete.esm.js';
@@ -11,14 +19,6 @@ import RichTextEditorWrapper from '../FormComponents/RichTextEditor/RichTextEdit
11
19
  import SingleSelectSearchApi from '../FormComponents/Select/SingleSelectSearchApi.esm.js';
12
20
  import MultiSelectSearchApi from '../FormComponents/Select/MultiSelectSearchApi.esm.js';
13
21
  import DatetimepickerWrapper from '../FormComponents/DatePicker/DatetimepickerWrapper.esm.js';
14
- import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
15
- import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
16
- import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
17
- import FormCheckBoxGroup from '../FormComponents/FormCheckBoxGroup/FormCheckBoxGroup.esm.js';
18
- import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
19
- import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.esm.js';
20
- import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
21
- import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
22
22
 
23
23
  // Lazy-loaded heavy components
24
24
  // const RichTextEditorWrapper = lazy(() =>
@@ -1,3 +1,9 @@
1
+ export { default as FormNumberField } from './FormNumberField/FormNumberField.esm.js';
2
+ export { default as FormNumberFieldDecimal } from './FormNumberField/FormNumberFieldDecimal.esm.js';
3
+ export { default as FormTextAreaField } from './FormTextAreaField/FormTextAreaField.esm.js';
4
+ export { default as FormTextField } from './FormTextField/FormTextField.esm.js';
5
+ export { default as PasswordField } from './PasswordField/PasswordField.esm.js';
6
+ export { default as FormCheckBox } from './FormCheckBox/FormCheckBox.esm.js';
1
7
  export { default as SingleSelect } from './Select/SingleSelect.esm.js';
2
8
  export { default as SingleSelectNonAutoComplete } from './Select/SingleSelectNonAutoComplete.esm.js';
3
9
  export { default as SingleSelectSearchApi } from './Select/SingleSelectSearchApi.esm.js';
@@ -5,22 +11,33 @@ export { default as MultiSelectSearchApi } from './Select/MultiSelectSearchApi.e
5
11
  export { default as MultiSelectV1 } from './Select/MultiSelectv1.esm.js';
6
12
  export { default as MultiSelectTable } from './Select/MultiSelectTable.esm.js';
7
13
  export { default as SearchableSelect } from './Select/SearchableSelect.esm.js';
8
- export { default as DatepickerWrapperV2 } from './DatePicker/DatepickerWrapperV2.esm.js';
9
- export { default as FormBottomField } from './FormBottomField/FormBottomField.esm.js';
10
- export { default as FormCheckBox } from './FormCheckBox/FormCheckBox.esm.js';
11
14
  export { default as FormCheckBoxGroup } from './FormCheckBoxGroup/FormCheckBoxGroup.esm.js';
12
- export { default as FormErrorField } from './FormErrorField/FormErrorField.esm.js';
13
- export { default as FormNumberField } from './FormNumberField/FormNumberField.esm.js';
14
- export { default as FormNumberFieldDecimal } from './FormNumberField/FormNumberFieldDecimal.esm.js';
15
15
  export { default as FormRadioGroup } from './FormRadioGroup/FormRadioGroup.esm.js';
16
+ export { default as DatepickerWrapperV2 } from './DatePicker/DatepickerWrapperV2.esm.js';
17
+ export { default as Monthpickerrender } from './DatePicker/Monthpickerrender.esm.js';
18
+ import 'react/jsx-runtime';
19
+ import 'react';
20
+ import '@mui/material';
21
+ import '@mui/icons-material/CalendarToday';
22
+ import '@mui/icons-material/ChevronLeft';
23
+ import '@mui/icons-material/ChevronRight';
24
+ import 'react-hook-form';
25
+ export { default as FormBottomField } from './FormBottomField/FormBottomField.esm.js';
26
+ import 'dayjs';
27
+ export { default as YearPickerField } from './YearPickerField/YearPickerField.esm.js';
28
+ export { default as TimePicker } from './TimePicker/TimePicker.esm.js';
16
29
  export { default as FormRenderFileUpload } from './FileUpload/FormRenderFileUpload.esm.js';
17
30
  export { default as FormRenderMultiFileUpload } from './FileUpload/FormRenderMultiFileUpload.esm.js';
18
- export { default as FormTextAreaField } from './FormTextAreaField/FormTextAreaField.esm.js';
19
- export { default as FormTextField } from './FormTextField/FormTextField.esm.js';
31
+ import '@mui/icons-material/Visibility';
32
+ import '@mui/icons-material/Close';
33
+ import '@mui/icons-material/NavigateBefore';
34
+ import '@mui/icons-material/NavigateNext';
35
+ import '@mui/icons-material/Delete';
36
+ import '@mui/icons-material/UploadFile';
37
+ import '@mui/icons-material/PictureAsPdf';
38
+ import '@mui/icons-material/TableChart';
39
+ import '@mui/icons-material/Description';
40
+ export { default as FormErrorField } from './FormErrorField/FormErrorField.esm.js';
20
41
  export { default as HelperText } from './HelperText/HelperText.esm.js';
21
- export { default as Monthpickerrender } from './DatePicker/Monthpickerrender.esm.js';
22
- export { default as PasswordField } from './PasswordField/PasswordField.esm.js';
23
42
  export { default as RichTextEditor } from './RichTextEditor/RichTextEditor.esm.js';
24
43
  export { default as RichTextEditorWrapper } from './RichTextEditor/RichTextEditorWrapper.esm.js';
25
- export { default as TimePicker } from './TimePicker/TimePicker.esm.js';
26
- export { default as YearPickerField } from './YearPickerField/YearPickerField.esm.js';
@@ -213,6 +213,7 @@ export interface DynamicFilterBarProps {
213
213
  uiDensity?: 'default' | 'compact';
214
214
  filterChipFontSize?: number;
215
215
  filterChipMinHeight?: number;
216
+ /** @deprecated No longer used — the "Filter by:" label was removed. Kept for backward compatibility; now a no-op. */
216
217
  filterLabelFontSize?: number;
217
218
  /**
218
219
  * Current value of MRT's global filter (free-text search across every
@@ -227,5 +228,5 @@ export interface DynamicFilterBarProps {
227
228
  /** ID prefix for all interactive elements — passed down to chips and ManageFilters. */
228
229
  gridId?: string;
229
230
  }
230
- declare const _default: import("react").MemoExoticComponent<({ serverFilterDefs, visibleFilterIds, onToggleFilter, filterValues, onFilterValuesChange, columnFilters, onColumnFilterChange, availableColumns, maxVisibleFilters, uiDensity, filterChipFontSize, filterChipMinHeight, filterLabelFontSize, globalFilter, onGlobalFilterChange, globalSearchPlaceholder, gridId, }: DynamicFilterBarProps) => import("react/jsx-runtime").JSX.Element>;
231
+ declare const _default: import("react").MemoExoticComponent<({ serverFilterDefs, visibleFilterIds, onToggleFilter, filterValues, onFilterValuesChange, columnFilters, onColumnFilterChange, availableColumns, maxVisibleFilters, uiDensity, filterChipFontSize, filterChipMinHeight, globalFilter, onGlobalFilterChange, globalSearchPlaceholder, gridId, }: DynamicFilterBarProps) => import("react/jsx-runtime").JSX.Element>;
231
232
  export default _default;
@@ -11,7 +11,7 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
11
11
  import SingleSelectNonAutoComplete from '../FormComponents/Select/SingleSelectNonAutoComplete.esm.js';
12
12
  import SingleSelectSearchApi from '../FormComponents/Select/SingleSelectSearchApi.esm.js';
13
13
  import MultiSelectSearchApi from '../FormComponents/Select/MultiSelectSearchApi.esm.js';
14
- import { TextField, InputAdornment, IconButton, Box, Typography, Tooltip, Button, Popover, Stack, List, ListItemButton, ListItemText, Checkbox, Divider } from '@mui/material';
14
+ import { TextField, InputAdornment, IconButton, Box, Tooltip, Button, Popover, Typography, Stack, List, ListItemButton, ListItemText, Checkbox, Divider } from '@mui/material';
15
15
  import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
16
16
  import TuneIcon from '@mui/icons-material/Tune';
17
17
  import CloseIcon from '@mui/icons-material/Close';
@@ -566,12 +566,11 @@ const ManageFiltersButton = ({ serverFilterDefs, visibleIds, availableColumns, o
566
566
  } }), selected && jsx(CheckIcon, { sx: { fontSize: 16, color: 'primary.main', flexShrink: 0 } })] }) }) }, col.id));
567
567
  }) })] }))] })] })] }));
568
568
  };
569
- const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter, filterValues, onFilterValuesChange, columnFilters, onColumnFilterChange, availableColumns, maxVisibleFilters = 4, uiDensity = 'default', filterChipFontSize, filterChipMinHeight, filterLabelFontSize, globalFilter, onGlobalFilterChange, globalSearchPlaceholder, gridId, }) => {
569
+ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter, filterValues, onFilterValuesChange, columnFilters, onColumnFilterChange, availableColumns, maxVisibleFilters = 4, uiDensity = 'default', filterChipFontSize, filterChipMinHeight, globalFilter, onGlobalFilterChange, globalSearchPlaceholder, gridId, }) => {
570
570
  const chipStyle = {
571
571
  fontSize: filterChipFontSize ?? (uiDensity === 'compact' ? 11 : 12),
572
572
  minHeight: filterChipMinHeight ?? (uiDensity === 'compact' ? 26 : 30),
573
573
  };
574
- const labelFontSize = filterLabelFontSize ?? (uiDensity === 'compact' ? 11 : 12);
575
574
  // ── Single-value column filter helpers ──────────────────────────────────────
576
575
  const getColValue = (id) => columnFilters.find((f) => f.id === id)?.value ?? '';
577
576
  const applyColFilter = useCallback((id, value) => {
@@ -621,7 +620,7 @@ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter,
621
620
  }
622
621
  }
623
622
  }, [filterValues, serverFilterDefs, availableColumns, onFilterValuesChange, applyColFilter, applyColMultiFilter]);
624
- return (jsxs(Box, { sx: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 0.75 }, children: [jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.4, mr: 0.25 }, children: [jsx(FilterListIcon, { sx: { fontSize: labelFontSize + 3, color: 'text.disabled' } }), jsx(Typography, { sx: { fontSize: labelFontSize }, color: "text.secondary", fontWeight: 500, noWrap: true, children: "Filter by:" })] }), visibleFilterIds.map((id) => {
623
+ return (jsxs(Box, { sx: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 0.75 }, children: [visibleFilterIds.map((id) => {
625
624
  const def = serverFilterDefs.find((d) => d.id === id);
626
625
  if (def?.type === 'dateRange') {
627
626
  return (jsx(DateRangeChip, { label: def.label, fromDate: filterValues[def.fromKey] ?? '', toDate: filterValues[def.toKey] ?? '', onApply: (from, to) => onFilterValuesChange({ ...filterValues, [def.fromKey]: from, [def.toKey]: to }), onRemove: () => clearFilter(id), chipStyle: chipStyle, baseId: gridId ? `${gridId}_${id}` : undefined }, id));
@@ -329,6 +329,34 @@ export interface GenericPaginatedGridProps<TData extends object> {
329
329
  * Default: false.
330
330
  */
331
331
  enableExpanding?: boolean;
332
+ /**
333
+ * Show a totals row summing every column whose `TPColumnDef` has
334
+ * `showTotal: true`. Computed client-side over the rows currently loaded
335
+ * on this page only — this grid never holds more than one page of data in
336
+ * memory (`manualPagination: true`, no accumulation), so the total always
337
+ * reflects "this page's rows," not a grand total across the full
338
+ * server-side dataset. Columns without `showTotal` render a blank footer
339
+ * cell; the leading column shows a "Total" label when it isn't itself
340
+ * summed. Default: `false`.
341
+ */
342
+ displayTotals?: boolean;
343
+ /**
344
+ * Where the totals row appears when `displayTotals` is true.
345
+ *
346
+ * Only `'bottom'` is implemented: a native sticky footer row (the same
347
+ * mechanism `enableStickyHeader` uses for the header), pinned to the
348
+ * bottom of the grid's own scroll area — body rows scroll underneath it,
349
+ * it never scrolls away.
350
+ *
351
+ * `'top'` (a row directly under the header, scrolling normally with the
352
+ * body) is accepted by the type for forward compatibility but not yet
353
+ * implemented — it requires injecting a synthetic row into the data model
354
+ * itself, which isn't built yet. Passing `'top'` falls back to `'bottom'`
355
+ * behaviour with a one-time console warning in dev.
356
+ *
357
+ * Default: `'bottom'`.
358
+ */
359
+ totalsPosition?: 'top' | 'bottom';
332
360
  }
333
- 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, pageSize, showGlobalSearchByDefault, showDensityToggle, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, getRowSx, getHeaderSx, renderDetailPanel, enableExpanding, }: GenericPaginatedGridProps<TData>): import("react/jsx-runtime").JSX.Element;
361
+ 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, pageSize, showGlobalSearchByDefault, showDensityToggle, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, getRowSx, getHeaderSx, renderDetailPanel, enableExpanding, displayTotals, totalsPosition, }: GenericPaginatedGridProps<TData>): import("react/jsx-runtime").JSX.Element;
334
362
  export default GenericPaginatedGrid;
@@ -1,4 +1,4 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { useMemo, useState, useRef, useLayoutEffect, useCallback, useEffect } from 'react';
3
3
  import { MRT_ToggleGlobalFilterButton, MRT_ShowHideColumnsButton, MRT_ToggleDensePaddingButton, MRT_ToggleFullScreenButton, useMaterialReactTable, MaterialReactTable } from 'material-react-table';
4
4
  import { useQuery, keepPreviousData } from '@tanstack/react-query';
@@ -109,6 +109,16 @@ const buildBodyGlobalStyles = (bodyFont) => ({
109
109
  userSelect: 'text',
110
110
  },
111
111
  });
112
+ /** Totals row: bold, subtly tinted, pinned above the body's bottom edge. */
113
+ const buildFooterGlobalStyles = (bodyFont) => ({
114
+ '.tp-gpg-root .MuiTableFooter-root .MuiTableCell-root': {
115
+ fontSize: `${bodyFont} !important`,
116
+ fontWeight: 700,
117
+ backgroundColor: 'rgba(0,0,0,0.03)',
118
+ borderTop: '2px solid rgba(224,224,224,1)',
119
+ borderRight: '1px solid rgba(224,224,224,1)',
120
+ },
121
+ });
112
122
  const buildHeaderGlobalStyles = (headerMin, headerMax, headerFont, headerBgColor) => {
113
123
  const headCell = {
114
124
  height: `${headerMax} !important`,
@@ -340,7 +350,7 @@ const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoa
340
350
  }, 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 } }) }) }) })] })] }));
341
351
  };
342
352
  // ─── GenericPaginatedGrid ─────────────────────────────────────────────────────
343
- 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], pageSize, showGlobalSearchByDefault = false, showDensityToggle = false, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters = 4, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, getRowSx, getHeaderSx, renderDetailPanel, enableExpanding = !!renderDetailPanel, }) {
353
+ 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], pageSize, showGlobalSearchByDefault = false, showDensityToggle = false, headerBgColor, exportHeaderBgColor, exportHeaderTextColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters = 4, muiTableBodyCellProps: gridBodyCellProps, muiTableHeadCellProps: gridHeadCellProps, getRowSx, getHeaderSx, renderDetailPanel, enableExpanding = !!renderDetailPanel, displayTotals = false, totalsPosition = 'bottom', }) {
344
354
  // ── Responsive tier detection ────────────────────────────────────────────────
345
355
  // Three tiers (desktop / tablet / mobile) drive auto density, a progressively
346
356
  // lower filter-chip cap, toolbar wrapping, and a stacked pagination footer —
@@ -384,6 +394,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
384
394
  },
385
395
  ...buildHeaderGlobalStyles(resolvedHeaderMinHeight, resolvedHeaderMaxHeight, resolvedHeaderFontSize, headerBgColor),
386
396
  ...buildBodyGlobalStyles(resolvedBodyFontSize),
397
+ ...buildFooterGlobalStyles(resolvedBodyFontSize),
387
398
  }), [
388
399
  resolvedHeaderMinHeight,
389
400
  resolvedHeaderMaxHeight,
@@ -496,14 +507,60 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
496
507
  // `getSearchText`. MRT doesn't know these props, so we strip them here. We
497
508
  // also inject the appropriate `filterFn` automatically so callers never
498
509
  // have to write one.
510
+ // Sum each `showTotal` column's raw value across the currently loaded page.
511
+ // A ref (synced below) lets the per-column `Footer` below always read the
512
+ // latest totals without forcing `processedColumns` to rebuild on every
513
+ // page fetch — only `columns`/`layoutMode`/`displayTotals` do that.
514
+ const totalsByColumnId = useMemo(() => {
515
+ if (!displayTotals)
516
+ return {};
517
+ const totals = {};
518
+ for (const col of columns) {
519
+ if (!col.showTotal)
520
+ continue;
521
+ const colId = col.id ?? col.accessorKey;
522
+ if (!colId)
523
+ continue;
524
+ let sum = 0;
525
+ for (const row of rows) {
526
+ const raw = typeof col.accessorFn === 'function'
527
+ ? col.accessorFn(row)
528
+ : row[col.accessorKey];
529
+ const n = typeof raw === 'number' ? raw : parseFloat(raw);
530
+ if (!Number.isNaN(n))
531
+ sum += n;
532
+ }
533
+ totals[colId] = sum;
534
+ }
535
+ return totals;
536
+ }, [columns, rows, displayTotals]);
537
+ const totalsRef = useRef(totalsByColumnId);
538
+ totalsRef.current = totalsByColumnId;
539
+ // The leading column gets a "Total" label when it isn't itself summed.
540
+ const leadingColumnId = columns[0]?.id ?? columns[0]?.accessorKey;
541
+ const topWarnedRef = useRef(false);
542
+ if (totalsPosition === 'top' && !topWarnedRef.current && process.env.NODE_ENV !== 'production') {
543
+ topWarnedRef.current = true;
544
+ // eslint-disable-next-line no-console
545
+ console.warn("GenericPaginatedGrid: totalsPosition 'top' is not implemented yet — falling back to 'bottom'.");
546
+ }
499
547
  const processedColumns = useMemo(() => {
500
548
  const allowGrow = layoutMode === 'grid' || layoutMode === 'semantic';
501
549
  return columns.map((col) => {
502
550
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
503
- const { filterType, options, getSearchText, exportExcel, ...mrtCol } = col;
551
+ const { filterType, options, getSearchText, exportExcel, showTotal, ...mrtCol } = col;
504
552
  if (allowGrow && mrtCol.grow === undefined) {
505
553
  mrtCol.grow = true;
506
554
  }
555
+ if (displayTotals) {
556
+ const colId = mrtCol.id ?? mrtCol.accessorKey;
557
+ if (showTotal) {
558
+ mrtCol.Footer = () => (jsx("span", { children: (totalsRef.current[colId] ?? 0).toLocaleString() }));
559
+ }
560
+ else if (colId === leadingColumnId) {
561
+ mrtCol.Footer = () => jsx("span", { style: { fontWeight: 700 }, children: "Total" });
562
+ }
563
+ }
507
564
  // Auto-inject filterFn only when the user hasn't already supplied one.
508
565
  // Note: `filterType` omitted defaults to the same plain text-input chip
509
566
  // as `filterType: 'text'` (see `ColumnFilterType` doc comment), so the
@@ -543,7 +600,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
543
600
  }
544
601
  return mrtCol;
545
602
  });
546
- }, [columns, layoutMode]);
603
+ }, [columns, layoutMode, displayTotals, leadingColumnId]);
547
604
  // ── Global Search: match formatted `Cell` text, not just the raw value ───────
548
605
  //
549
606
  // MRT's built-in global filter always compares against `row.getValue(columnId)`
@@ -917,6 +974,8 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
917
974
  columnResizeMode: 'onChange',
918
975
  enableExpanding,
919
976
  renderDetailPanel,
977
+ enableTableFooter: displayTotals,
978
+ enableStickyFooter: displayTotals,
920
979
  // Note: `enableFilters` is the master switch for column AND global
921
980
  // filtering. Keep it enabled so the Global Search box (which relies on
922
981
  // `getCanGlobalFilter()`) actually filters rows. MRT's native per-column
@@ -119,6 +119,16 @@ export type TPColumnDef<TData extends object = object, TValue = any> = MRT_Colum
119
119
  * explicitly opted out.
120
120
  */
121
121
  exportExcel?: boolean;
122
+ /**
123
+ * Include this column in the grid's totals row (see `displayTotals` on
124
+ * `GenericPaginatedGrid`). Sums the column's raw value across every row
125
+ * currently loaded on the page — the grid never holds more than one page
126
+ * of data in memory, so the total always reflects "this page's rows," not
127
+ * a grand total across the full server-side dataset. Only meaningful on
128
+ * numeric columns. Non-numeric/unparseable values are skipped (treated as
129
+ * 0), not coerced. Default: `false`.
130
+ */
131
+ showTotal?: boolean;
122
132
  };
123
133
  export interface InvoiceDetail {
124
134
  Data_from: string;
@@ -1,4 +1,4 @@
1
- import { __exports as Calendar } from '../../../../../_virtual/index.esm6.js';
1
+ import { __exports as Calendar } from '../../../../../_virtual/index.esm7.js';
2
2
  import require$$0__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';
@@ -1,4 +1,4 @@
1
- import { __exports as DateRange } from '../../../../../_virtual/index.esm5.js';
1
+ import { __exports as DateRange } from '../../../../../_virtual/index.esm6.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireCalendar } from '../Calendar/index.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as DateRangePicker } from '../../../../../_virtual/index.esm4.js';
1
+ import { __exports as DateRangePicker } from '../../../../../_virtual/index.esm5.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireDateRange } from '../DateRange/index.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as DayCell } from '../../../../../_virtual/index.esm11.js';
1
+ import { __exports as DayCell } from '../../../../../_virtual/index.esm12.js';
2
2
  import require$$0__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 DefinedRange } from '../../../../../_virtual/index.esm7.js';
1
+ import { __exports as DefinedRange } from '../../../../../_virtual/index.esm4.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
  import { __require as requireStyles } from '../../styles.esm.js';
@@ -1,4 +1,4 @@
1
- import { __exports as InputRangeField } from '../../../../../_virtual/index.esm13.js';
1
+ import { __exports as InputRangeField } from '../../../../../_virtual/index.esm11.js';
2
2
  import require$$0__default from 'react';
3
3
  import { __require as requirePropTypes } from '../../../../prop-types/index.esm.js';
4
4
 
@@ -1,4 +1,4 @@
1
- import { __exports as Month } from '../../../../../_virtual/index.esm12.js';
1
+ import { __exports as Month } from '../../../../../_virtual/index.esm13.js';
2
2
  import require$$0__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": "3.0.26",
3
+ "version": "3.0.27",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "React form components library built with React Hook Form and Yup",