mui-datatables-updated 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # MUI Datatable library inspired by the gregnb/mui-datatables project, featuring an up-to-date implementation with Typescript Support
2
+
3
+ ## Installation
4
+
5
+ If not already installed, install the Material-UI library:
6
+
7
+ ```bash
8
+ npm install @mui/material @emotion/react @emotion/styled @mui/icons-material
9
+ ```
10
+
11
+ Then install the mui-datatables-updated package:
12
+
13
+ ```bash
14
+ npm install mui-datatables-updated
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```jsx
20
+ import React from 'react';
21
+ import MUIDataTable from 'mui-datatables-updated';
22
+
23
+ const sampleData = [
24
+ {
25
+ "id": 1,
26
+ "name": "Cupcake",
27
+ "carbs": 67,
28
+ },
29
+ {
30
+ "id": 2,
31
+ "name": "Donut",
32
+ "carbs": 51,
33
+ }
34
+ ];
35
+
36
+ export default function App() {
37
+ return (
38
+ <>
39
+ <MUITable title='Sample title' data={sampleData} />
40
+ </>
41
+ )
42
+ }
43
+ ```
44
+
45
+ ## Options
46
+
47
+ | Name | Type | Description |
48
+ | --- | --- | --- |
49
+ | title | string | Title of the table |
50
+ | data | array | Data to be displayed in the table |
51
+ | deactivateSelect | boolean | Disable row selection |
52
+ | defaultOrderBy | string -> must be a key in the data object | Default order by key to sort the table data on first load |
53
+ | defaultOrder | 'asc' or 'desc' | Default order to be used when sorting and displaying on first load |
54
+ | excludedColumns | array | Array of **keys** in data objects to be excluded from the table |
55
+ | columns | array | Array of objects to define the columns of the table |
56
+ | options | object | Object to define the options of the table |
57
+
58
+ ### Example
59
+
60
+ ```jsx
61
+ import MUITable from 'mui-datatables-updated'
62
+
63
+ const sampleColumns = [
64
+ {
65
+ name: 'name',
66
+ label: 'Name of dessert',
67
+ },
68
+ {
69
+ name: 'healthy',
70
+ label: 'Is Healthy?',
71
+ options: {
72
+ sort: false,
73
+ customBodyRender: (value: boolean) => value ? ':)' : ':('
74
+ }
75
+ },
76
+ {
77
+ name: 'carbs',
78
+ label: 'Carbs (g)',
79
+ options: {
80
+ sort: false
81
+ }
82
+ },
83
+ {
84
+ name: 'id',
85
+ label: 'ID',
86
+ }
87
+ ]
88
+
89
+ function App() {
90
+ return (
91
+ <>
92
+ <h1>MUI Table test</h1>
93
+ <MUITable
94
+ title='Sample Test'
95
+ data={sampleData}
96
+ defaultOrder='desc'
97
+ defaultOrderBy='name'
98
+ excludedColumns={['id']}
99
+ />
100
+ <MUITable
101
+ title='Sample Test'
102
+ data={sampleData}
103
+ defaultOrder='desc'
104
+ defaultOrderBy='name'
105
+ columns={sampleColumns}
106
+ deactivateSelect
107
+ options={{
108
+ translations: {
109
+ filterTooltip: 'Filtrar',
110
+ filtersTitle: 'Filtros',
111
+ resetButtonText: 'Reiniciar',
112
+ searchPlaceholder: 'Buscar...',
113
+ rowsPerPageText: 'Filas por página',
114
+ selectedTextRenderer: (numSelected) => `${numSelected} seleccionado/s`,
115
+ labelDisplayedRows: ({ from, to, count }) => `${from}-${to} de ${count}`,
116
+ }
117
+ }}
118
+ />
119
+ </>
120
+ )
121
+ }
122
+ ```
123
+
124
+ ## Custom Columns
125
+
126
+ On each column object, you have the ability to customize columns to show the filtering options in the filter dropdown and to disable sorting when clicking on the column header. Values are set to true by default.
127
+
128
+ | Name | Type | Description |
129
+ | --- | --- | --- |
130
+ | name | string | Key in the data object |
131
+ | label | string | Label to be displayed in the column header |
132
+ | options | object | Object to define the options of the column |
133
+
134
+ ### Options Object
135
+
136
+ | Name | Type | Description |
137
+ | --- | --- | --- |
138
+ | customBodyRender | function | Function to render custom content in the column |
139
+ | filter | boolean | Show filter dropdown in the column |
140
+ | sort | boolean | Enable sorting when clicking on the column header |
141
+
142
+ ### Custom columns example
143
+
144
+ ```jsx
145
+ const columns = [
146
+ {
147
+ name: "active",
148
+ label: "Is Active",
149
+ options: {
150
+ customBodyRender: (value: boolean) => value ? 'Yes' : 'No',
151
+ filter: true,
152
+ sort: false
153
+ }
154
+ },
155
+ ...
156
+ ];
157
+ ```
158
+
159
+ ## Localization
160
+
161
+ As well as gregnb original package, I decided that the cost of bringing in another library to perform localizations would be too expensive. Instead the ability to override most text labels (which aren't many) is offered through the translations property. The available customizations are:
162
+
163
+ ```jsx
164
+ <MUITable
165
+ title='Sample Test'
166
+ data={sampleData}
167
+ options={{
168
+ translations: {
169
+ filterTooltip: 'Filtrar',
170
+ filtersTitle: 'Filtros',
171
+ resetButtonText: 'Reiniciar',
172
+ searchPlaceholder: 'Buscar...',
173
+ rowsPerPageText: 'Filas por página',
174
+ selectedTextRenderer: (numSelected) => `${numSelected} seleccionado/s`,
175
+ labelDisplayedRows: ({ from, to, count }) => `${from}-${to} de ${count}`,
176
+ }
177
+ }}
178
+ />
179
+ ```
180
+
181
+ ## Contributing
182
+
183
+ Pull requests are welcome. I won't be able to implement new features in the short term, so feel free to contribute to the project by adding new features, fixing bugs, creating tests, and improving the documentation.
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { CustomSelectedToolbarProps } from './Toolbar';
3
+ import { Order } from './utils';
4
+ import { LabelDisplayedRowsArgs } from '@mui/material/TablePagination';
5
+ export interface Column {
6
+ name: string;
7
+ label?: string;
8
+ options?: {
9
+ customBodyRender?: (value: any) => React.ReactNode;
10
+ filter?: boolean;
11
+ sort?: boolean;
12
+ };
13
+ }
14
+ export interface Options {
15
+ translations?: {
16
+ filterTooltip?: string;
17
+ searchTooltip?: string;
18
+ downloadTooltip?: string;
19
+ printTooltip?: string;
20
+ filtersTitle?: string;
21
+ resetButtonText?: string;
22
+ rowsPerPageText?: string;
23
+ searchPlaceholder?: string;
24
+ selectedTextRenderer?: (selected: number) => string;
25
+ labelDisplayedRows?: ({ from, to, count }: LabelDisplayedRowsArgs) => string;
26
+ };
27
+ }
28
+ export interface EnhancedTableProps<T extends object> extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
29
+ title: string;
30
+ data: T[];
31
+ deactivateSelect?: boolean;
32
+ defaultOrderBy?: string;
33
+ defaultOrder?: Order;
34
+ excludedColumns?: (keyof T)[];
35
+ columns?: Column[];
36
+ options?: Options;
37
+ CustomToolbar?: React.FC;
38
+ CustomSelectedToolbar?: React.FC<CustomSelectedToolbarProps<T>>;
39
+ }
40
+ export declare const MUITable: <T extends object>({ title, data, deactivateSelect, defaultOrderBy, defaultOrder, excludedColumns, columns: passedColumns, CustomToolbar, CustomSelectedToolbar, options, ...rest }: EnhancedTableProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import { Column } from './MUITable';
2
+ import { Order } from './utils';
3
+ interface EnhancedTableProps<T> {
4
+ numSelected: number;
5
+ onRequestSort: (event: React.MouseEvent<unknown>, property: keyof T) => void;
6
+ onSelectAllClick?: (event: React.ChangeEvent<HTMLInputElement>) => void;
7
+ order: Order;
8
+ orderBy: keyof T;
9
+ rowCount: number;
10
+ columns: Column[];
11
+ deactivateSelectAll?: boolean;
12
+ }
13
+ export declare function EnhancedTableHead<T>({ onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort, columns, deactivateSelectAll, }: EnhancedTableProps<T>): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { Column, Options } from "./MUITable";
3
+ import { UseReactToPrintFn } from "react-to-print";
4
+ export interface CustomSelectedToolbarProps<T> {
5
+ selected?: readonly T[];
6
+ data?: T[];
7
+ }
8
+ interface EnhancedTableToolbarProps<T> {
9
+ title: string;
10
+ numSelected: number;
11
+ selected: readonly T[];
12
+ onFilterChange: (filterFunc: (row: T) => boolean) => void;
13
+ onSearch: (query: string) => void;
14
+ printFn: UseReactToPrintFn;
15
+ columns: Column[];
16
+ CustomToolbar?: React.FC;
17
+ CustomSelectedToolbar?: React.FC<CustomSelectedToolbarProps<T>>;
18
+ data?: T[];
19
+ options?: Options;
20
+ }
21
+ export declare function EnhancedTableToolbar<T>(props: EnhancedTableToolbarProps<T>): import("react/jsx-runtime").JSX.Element;
22
+ export {};
@@ -0,0 +1 @@
1
+ export declare const Table: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { HeadCell } from "./TableHead";
2
+ export interface Data {
3
+ id: number;
4
+ calories: number;
5
+ carbs: number;
6
+ fat: number;
7
+ name: string;
8
+ protein: number;
9
+ }
10
+ export declare const rows: Data[];
11
+ export declare const headCells: HeadCell<typeof rows[0]>[];
@@ -0,0 +1,2 @@
1
+ export type Order = 'asc' | 'desc';
2
+ export declare function getComparator<T, Key extends keyof T>(order: Order, orderBy: Key): (a: T, b: T) => number;
@@ -0,0 +1,3 @@
1
+ export { MUITable as default } from './components/MUITable';
2
+ export type { Column } from './components/MUITable';
3
+ export type { CustomSelectedToolbarProps } from './components/Toolbar';
@@ -0,0 +1,361 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import Checkbox from '@mui/material/Checkbox';
3
+ import Paper from '@mui/material/Paper';
4
+ import Table from '@mui/material/Table';
5
+ import TableBody from '@mui/material/TableBody';
6
+ import TableCell from '@mui/material/TableCell';
7
+ import TableContainer from '@mui/material/TableContainer';
8
+ import TablePagination from '@mui/material/TablePagination';
9
+ import TableRow from '@mui/material/TableRow';
10
+ import React, { useState, useEffect, useRef, useCallback } from 'react';
11
+ import { useReactToPrint } from 'react-to-print';
12
+ import Box from '@mui/material/Box';
13
+ import TableHead from '@mui/material/TableHead';
14
+ import TableSortLabel from '@mui/material/TableSortLabel';
15
+ import { visuallyHidden } from '@mui/utils';
16
+ import { Close, CloudDownload, Print } from '@mui/icons-material';
17
+ import FilterListIcon from '@mui/icons-material/FilterList';
18
+ import SearchIcon from '@mui/icons-material/Search';
19
+ import { Toolbar, Typography, Stack, TextField, IconButton, Tooltip, Popover, Button, Box as Box$1, Slider, Checkbox as Checkbox$1 } from '@mui/material';
20
+ import { alpha } from '@mui/material/styles';
21
+
22
+ /******************************************************************************
23
+ Copyright (c) Microsoft Corporation.
24
+
25
+ Permission to use, copy, modify, and/or distribute this software for any
26
+ purpose with or without fee is hereby granted.
27
+
28
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
29
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
30
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
31
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
32
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
33
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
34
+ PERFORMANCE OF THIS SOFTWARE.
35
+ ***************************************************************************** */
36
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
37
+
38
+
39
+ function __rest(s, e) {
40
+ var t = {};
41
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
42
+ t[p] = s[p];
43
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
44
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
45
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
46
+ t[p[i]] = s[p[i]];
47
+ }
48
+ return t;
49
+ }
50
+
51
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
52
+ var e = new Error(message);
53
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
54
+ };
55
+
56
+ function EnhancedTableHead({ onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort, columns, deactivateSelectAll, }) {
57
+ const createSortHandler = (property) => (event) => {
58
+ onRequestSort(event, property);
59
+ };
60
+ return (jsx(TableHead, { children: jsxs(TableRow, { children: [!deactivateSelectAll &&
61
+ jsx(TableCell, { padding: "checkbox", children: jsx(Checkbox, { color: "primary", indeterminate: numSelected > 0 && numSelected < rowCount, checked: rowCount > 0 && numSelected === rowCount, onChange: onSelectAllClick, inputProps: {
62
+ 'aria-label': 'select all items',
63
+ } }) }), columns.map((column) => {
64
+ var _a;
65
+ return (jsx(TableCell, { padding: 'normal', sortDirection: orderBy === column.name ? order : false, children: ((_a = column.options) === null || _a === undefined ? undefined : _a.sort) !== false ? (jsxs(TableSortLabel, { active: orderBy === column.name, direction: orderBy === column.name ? order : 'asc', onClick: createSortHandler(column.name), sx: { fontWeight: 'bold' }, children: [column.label, orderBy === column.name ? (jsx(Box, { component: "span", sx: visuallyHidden, children: order === 'desc' ? 'sorted descending' : 'sorted ascending' })) : null] })) : (jsx("span", { style: { fontWeight: 700 }, children: column.label })) }, String(column.name)));
66
+ })] }) }));
67
+ }
68
+
69
+ function EnhancedTableToolbar(props) {
70
+ var _a, _b, _c, _d, _e, _f, _g, _h;
71
+ const { title, numSelected, selected, onFilterChange, onSearch, printFn, columns, CustomToolbar, CustomSelectedToolbar, data, options } = props;
72
+ const [anchorEl, setAnchorEl] = useState(null);
73
+ const [filters, setFilters] = useState([]);
74
+ const [filterConfig, setFilterConfig] = useState([]);
75
+ // rest value to force re-render of filters
76
+ const [resetCounter, setResetCounter] = useState(0);
77
+ const [openSearch, setOpenSearch] = useState(false);
78
+ function downloadCSV(data, filename = "data.csv") {
79
+ // Base case
80
+ if (data.length === 0) {
81
+ console.warn("No data to export.");
82
+ return;
83
+ }
84
+ // Create CSV content
85
+ const headers = Object.keys(data[0]);
86
+ const csvRows = data.map(obj => headers.map(field => { var _a; return JSON.stringify((_a = obj[field]) !== null && _a !== undefined ? _a : ""); }).join(","));
87
+ const csvContent = [headers.join(","), ...csvRows].join("\n");
88
+ // Create Blob and download
89
+ const blob = new Blob([csvContent], { type: "text/csv" });
90
+ const link = document.createElement("a");
91
+ link.href = URL.createObjectURL(blob);
92
+ link.download = filename;
93
+ document.body.appendChild(link);
94
+ link.click();
95
+ document.body.removeChild(link);
96
+ URL.revokeObjectURL(link.href);
97
+ }
98
+ useEffect(() => {
99
+ if (data && data.length > 0) {
100
+ const inferredConfig = columns.map((column) => {
101
+ const key = column.name;
102
+ const values = data.map((row) => row[key]);
103
+ const isNumber = values.every((val) => typeof val === "number");
104
+ const inferredType = isNumber ? "number" : typeof values[0] === "boolean" ? "boolean" : "string";
105
+ return {
106
+ key,
107
+ type: inferredType,
108
+ min: isNumber ? Math.min(...values) : undefined,
109
+ max: isNumber ? Math.max(...values) : undefined,
110
+ };
111
+ });
112
+ setFilterConfig(inferredConfig);
113
+ }
114
+ }, [data, columns]);
115
+ useEffect(() => {
116
+ const newFilterFunc = (row) => {
117
+ if (filters.length === 0)
118
+ return true;
119
+ return filters.every((filter) => {
120
+ const rowValue = row[filter.key];
121
+ if (filter.type === "number") {
122
+ const [min, max] = filter.value;
123
+ return rowValue >= min && rowValue <= max;
124
+ }
125
+ if (filter.type === "string") {
126
+ return rowValue.toString().toLowerCase().includes(filter.value.toLowerCase());
127
+ }
128
+ if (filter.type === "boolean") {
129
+ return rowValue === filter.value;
130
+ }
131
+ return true;
132
+ });
133
+ };
134
+ onFilterChange(newFilterFunc);
135
+ }, [filters, onFilterChange]);
136
+ const handleOpen = (event) => {
137
+ setAnchorEl(event.currentTarget);
138
+ };
139
+ const handleClose = () => {
140
+ setAnchorEl(null);
141
+ };
142
+ const handleSearchChange = () => {
143
+ setOpenSearch((prev) => !prev);
144
+ };
145
+ const open = Boolean(anchorEl);
146
+ const getFilter = (key) => filters.find((filter) => filter.key === key);
147
+ const updateFilter = (updatedFilter) => {
148
+ setFilters((prevFilters) => {
149
+ const existingIndex = prevFilters.findIndex((filter) => filter.key === updatedFilter.key);
150
+ if (existingIndex !== -1) {
151
+ // Update existing filter
152
+ const newFilters = [...prevFilters];
153
+ newFilters[existingIndex] = updatedFilter;
154
+ return newFilters;
155
+ }
156
+ // Add new filter
157
+ return [...prevFilters, updatedFilter];
158
+ });
159
+ };
160
+ const removeFilter = (key) => {
161
+ setFilters((prevFilters) => prevFilters.filter((filter) => filter.key !== key));
162
+ };
163
+ const handleFilterChange = (key, value) => {
164
+ if (value === undefined || value === null || value === "") {
165
+ removeFilter(key);
166
+ }
167
+ else {
168
+ const config = filterConfig.find((config) => config.key === key);
169
+ if (!config)
170
+ return;
171
+ updateFilter({
172
+ key,
173
+ type: config.type,
174
+ value,
175
+ });
176
+ }
177
+ };
178
+ const resetFilters = () => {
179
+ setFilters([]);
180
+ setResetCounter((prev) => prev + 1);
181
+ };
182
+ return (jsxs(Toolbar, { sx: [
183
+ { px: { sm: 2 }, borderBottom: 1, borderColor: "divider" },
184
+ numSelected > 0 && { bgcolor: (theme) => alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity) },
185
+ ], children: [numSelected > 0 ? (jsxs(Fragment, { children: [jsx(Typography, { sx: { flex: "1 1 100%" }, color: "inherit", variant: "subtitle1", component: "div", children: ((_a = options === null || options === undefined ? undefined : options.translations) === null || _a === undefined ? undefined : _a.selectedTextRenderer)
186
+ ? options.translations.selectedTextRenderer(numSelected)
187
+ : `${numSelected} selected` }), CustomSelectedToolbar && (jsx(CustomSelectedToolbar, { data: data, selected: selected }))] })) : (jsxs(Stack, { direction: "row", justifyContent: "space-between", width: "100%", alignItems: "center", children: [openSearch ? (jsxs(Stack, { direction: "row", alignItems: "center", children: [jsx(SearchIcon, {}), jsx(TextField, { placeholder: ((_b = options === null || options === undefined ? undefined : options.translations) === null || _b === undefined ? undefined : _b.searchPlaceholder) || "Search...", onChange: (e) => onSearch(e.target.value), variant: "standard", autoFocus: true, fullWidth: true, sx: { marginLeft: 1 } }), jsx(IconButton, { onClick: handleSearchChange, sx: { '&:hover': { color: 'error.main' } }, children: jsx(Close, {}) })] })) : (jsx(Typography, { sx: { flex: "1 1 100%", alignContent: "center", paddingLeft: 1 }, variant: "h6", id: "tableTitle", component: "div", children: title })), jsxs(Stack, { direction: "row", spacing: 0.5, children: [jsx(Tooltip, { title: ((_c = options === null || options === undefined ? undefined : options.translations) === null || _c === undefined ? undefined : _c.searchTooltip) || "Search", children: jsx(IconButton, { onClick: handleSearchChange, children: jsx(SearchIcon, {}) }) }), jsx(Tooltip, { title: ((_d = options === null || options === undefined ? undefined : options.translations) === null || _d === undefined ? undefined : _d.downloadTooltip) || "Download CSV", children: jsx(IconButton, { onClick: () => downloadCSV(data, "data.csv"), children: jsx(CloudDownload, {}) }) }), jsx(Tooltip, { title: ((_e = options === null || options === undefined ? undefined : options.translations) === null || _e === undefined ? undefined : _e.printTooltip) || "Print", children: jsx(IconButton, { onClick: () => printFn(), children: jsx(Print, {}) }) }), jsx(Tooltip, { title: ((_f = options === null || options === undefined ? undefined : options.translations) === null || _f === undefined ? undefined : _f.filterTooltip) || "Filter list", children: jsx(IconButton, { onClick: handleOpen, children: jsx(FilterListIcon, {}) }) }), CustomToolbar && jsx(CustomToolbar, {})] })] })), jsxs(Popover, { open: open, anchorEl: anchorEl, onClose: handleClose, anchorOrigin: {
188
+ vertical: "bottom",
189
+ horizontal: "right",
190
+ }, transformOrigin: {
191
+ vertical: "top",
192
+ horizontal: "right",
193
+ }, slotProps: {
194
+ paper: { sx: { width: "20%", padding: 2, boxShadow: 2 } }
195
+ }, children: [jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [jsx(Typography, { variant: "h6", sx: { marginBottom: 2 }, children: ((_g = options === null || options === undefined ? undefined : options.translations) === null || _g === undefined ? undefined : _g.filtersTitle) || "Filters" }), jsx(Button, { variant: "contained", size: "small", sx: { height: "fit-content" }, onClick: resetFilters, children: ((_h = options === null || options === undefined ? undefined : options.translations) === null || _h === undefined ? undefined : _h.resetButtonText) || "Reset" })] }), jsx(Stack, { children: filterConfig.map(({ key, type, min, max }) => {
196
+ var _a, _b, _c, _d, _e, _f;
197
+ const currentFilter = getFilter(key);
198
+ return (jsxs(Box$1, { children: [jsx(Typography, { variant: "subtitle1", children: (_a = columns.find((cell) => cell.name === key)) === null || _a === undefined ? undefined : _a.label }), type === "number" && min !== undefined && max !== undefined && (jsx(Slider, { value: [
199
+ (_c = (_b = currentFilter === null || currentFilter === undefined ? undefined : currentFilter.value) === null || _b === undefined ? undefined : _b[0]) !== null && _c !== undefined ? _c : min,
200
+ (_e = (_d = currentFilter === null || currentFilter === undefined ? undefined : currentFilter.value) === null || _d === undefined ? undefined : _d[1]) !== null && _e !== undefined ? _e : max,
201
+ ], onChange: (_, newValue) => {
202
+ const [newMin, newMax] = newValue;
203
+ handleFilterChange(key, [newMin, newMax]);
204
+ }, valueLabelDisplay: "auto", min: min || 0, max: max || 100, step: 1 })), type === "string" && (jsx(TextField, { placeholder: ((_f = options === null || options === undefined ? undefined : options.translations) === null || _f === undefined ? undefined : _f.searchPlaceholder) || "Search...", size: "small", value: (currentFilter === null || currentFilter === undefined ? undefined : currentFilter.value) || "", onChange: (e) => handleFilterChange(key, e.target.value), sx: {
205
+ marginBottom: 1,
206
+ paddingY: 0.5,
207
+ width: "100%",
208
+ } })), type === "boolean" && (jsxs(Box$1, { display: "flex", alignItems: "center", children: [jsx(Checkbox$1, { checked: (currentFilter === null || currentFilter === undefined ? undefined : currentFilter.value) === true, onChange: (e) => handleFilterChange(key, e.target.checked) }), currentFilter && (jsx(Close, { color: "error", sx: { cursor: "pointer", fontSize: 15 }, onClick: () => handleFilterChange(key, undefined) }))] }))] }, key));
209
+ }) }, resetCounter)] })] }));
210
+ }
211
+
212
+ function getComparator(order, orderBy) {
213
+ return order === 'desc'
214
+ ? (a, b) => descendingComparator(a[orderBy], b[orderBy])
215
+ : (a, b) => -descendingComparator(a[orderBy], b[orderBy]);
216
+ }
217
+ function descendingComparator(a, b) {
218
+ if (b < a)
219
+ return -1;
220
+ if (b > a)
221
+ return 1;
222
+ return 0;
223
+ }
224
+
225
+ const MUITable = (_a) => {
226
+ var _b, _c;
227
+ var { title, data, deactivateSelect, defaultOrderBy, defaultOrder, excludedColumns, columns: passedColumns, CustomToolbar, CustomSelectedToolbar, options } = _a, rest = __rest(_a, ["title", "data", "deactivateSelect", "defaultOrderBy", "defaultOrder", "excludedColumns", "columns", "CustomToolbar", "CustomSelectedToolbar", "options"]);
228
+ const tableRef = useRef(null);
229
+ const reactToPrintFn = useReactToPrint({ contentRef: tableRef });
230
+ const getDefaultOrderByKey = React.useCallback(() => {
231
+ if (data.length === 0)
232
+ return "id";
233
+ const keys = data.length > 0 ? Object.keys(data[0]) : [];
234
+ if (defaultOrderBy) {
235
+ if (keys.includes(defaultOrderBy)) {
236
+ return defaultOrderBy;
237
+ }
238
+ else {
239
+ console.warn(`{defaultOrderBy}: "${defaultOrderBy}" not found among the object keys. Falling back to automatic key detection.`);
240
+ }
241
+ }
242
+ return (keys.includes("id") ? "id" : keys[0]);
243
+ }, [data, defaultOrderBy]);
244
+ const [state, setState] = React.useState(() => {
245
+ const orderByKey = getDefaultOrderByKey();
246
+ return {
247
+ order: defaultOrder || 'asc',
248
+ orderBy: orderByKey,
249
+ selected: [],
250
+ page: 0,
251
+ rowsPerPage: 5,
252
+ searchQuery: "",
253
+ filterFunc: () => true,
254
+ currentData: data,
255
+ visibleRows: data,
256
+ emptyRows: 0,
257
+ };
258
+ });
259
+ const generateColumns = React.useCallback(() => {
260
+ if (data.length === 0)
261
+ return [];
262
+ return Object.keys(data[0])
263
+ .filter((key) => !(excludedColumns || []).includes(key))
264
+ .map((key) => ({
265
+ name: key,
266
+ label: key.charAt(0).toUpperCase() + key.slice(1),
267
+ }));
268
+ }, [data, excludedColumns]);
269
+ const columns = passedColumns || generateColumns();
270
+ const handleSearch = (query) => {
271
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { searchQuery: query.toLowerCase() })));
272
+ };
273
+ React.useEffect(() => {
274
+ const orderByKey = getDefaultOrderByKey();
275
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { orderBy: orderByKey })));
276
+ }, [data, getDefaultOrderByKey]);
277
+ // Data sorting, filtering, and pagination
278
+ React.useEffect(() => {
279
+ // Apply filters, search query, and sort data
280
+ const sortedData = [...data]
281
+ .filter(state.filterFunc)
282
+ .filter((row) => Object.values(row).some((value) => typeof value === "string" &&
283
+ value.toLowerCase().includes(state.searchQuery)))
284
+ .sort(getComparator(state.order, state.orderBy));
285
+ // Paginate the processed data
286
+ const startIndex = state.page * state.rowsPerPage;
287
+ const paginatedData = sortedData.slice(startIndex, startIndex + state.rowsPerPage);
288
+ const calculatedEmptyRows = Math.max(0, (1 + state.page) * state.rowsPerPage - sortedData.length);
289
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { visibleRows: paginatedData, emptyRows: calculatedEmptyRows, selected: prevState.selected.filter((selectedRow) => data.includes(selectedRow)) })));
290
+ }, [
291
+ state.filterFunc,
292
+ state.order,
293
+ state.orderBy,
294
+ state.page,
295
+ state.rowsPerPage,
296
+ state.searchQuery,
297
+ data,
298
+ ]);
299
+ // Remove selected rows deleted from the data to prevent stale selected state
300
+ React.useEffect(() => {
301
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { selected: prevState.selected.filter((selectedRow) => data.some((row) => row === selectedRow)) })));
302
+ }, [data]);
303
+ const handleRequestSort = (_event, property) => {
304
+ const isAsc = state.orderBy === property && state.order === 'asc';
305
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { order: isAsc ? 'desc' : 'asc', orderBy: property })));
306
+ };
307
+ const handleSelectAllClick = (event) => {
308
+ if (event.target.checked) {
309
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { selected: [...state.currentData] })));
310
+ }
311
+ else {
312
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { selected: [] })));
313
+ }
314
+ };
315
+ const handleClick = (_event, row) => {
316
+ const selectedIndex = state.selected.findIndex((selectedRow) => selectedRow === row);
317
+ let newSelected = [];
318
+ if (selectedIndex === -1) {
319
+ newSelected = [...state.selected, row];
320
+ }
321
+ else if (selectedIndex === 0) {
322
+ newSelected = state.selected.slice(1);
323
+ }
324
+ else if (selectedIndex === state.selected.length - 1) {
325
+ newSelected = state.selected.slice(0, -1);
326
+ }
327
+ else if (selectedIndex > 0) {
328
+ newSelected = [
329
+ ...state.selected.slice(0, selectedIndex),
330
+ ...state.selected.slice(selectedIndex + 1),
331
+ ];
332
+ }
333
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { selected: newSelected })));
334
+ };
335
+ const handleChangePage = (_event, newPage) => {
336
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { page: newPage })));
337
+ };
338
+ const handleChangeRowsPerPage = (event) => {
339
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { rowsPerPage: parseInt(event.target.value, 10), page: 0 })));
340
+ };
341
+ const handleFilterChange = useCallback((filterFunc) => {
342
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { filterFunc })));
343
+ }, []);
344
+ return (jsx("div", Object.assign({}, rest, { ref: tableRef, children: jsxs(Paper, { sx: { width: '100%', mb: 2 }, children: [jsx(EnhancedTableToolbar, { title: title, numSelected: state.selected.length, selected: state.selected, onFilterChange: handleFilterChange, onSearch: handleSearch, printFn: reactToPrintFn, columns: columns, CustomToolbar: CustomToolbar, CustomSelectedToolbar: CustomSelectedToolbar, data: data, options: options }), jsx(TableContainer, { children: jsxs(Table, { sx: { minWidth: 750 }, "aria-labelledby": "tableTitle", size: "small", children: [jsx(EnhancedTableHead, { columns: columns, numSelected: state.selected.length, order: state.order, orderBy: state.orderBy, onSelectAllClick: deactivateSelect ? undefined : handleSelectAllClick, onRequestSort: handleRequestSort, rowCount: state.currentData.length, deactivateSelectAll: deactivateSelect }), jsxs(TableBody, { children: [state.visibleRows.map((row, index) => {
345
+ const isItemSelected = state.selected.some((selectedRow) => selectedRow === row);
346
+ const labelId = `enhanced-table-checkbox-${index}`;
347
+ return (jsxs(TableRow, { hover: true, onClick: deactivateSelect ? undefined : (event) => handleClick(event, row), role: "checkbox", "aria-checked": isItemSelected, tabIndex: -1, selected: isItemSelected, sx: { cursor: 'pointer' }, children: [!deactivateSelect && (jsx(TableCell, { padding: "checkbox", children: jsx(Checkbox, { color: "primary", checked: isItemSelected, inputProps: {
348
+ 'aria-labelledby': labelId,
349
+ } }) })), columns.map((column, cellIndex) => {
350
+ var _a;
351
+ return (jsx(TableCell, { component: cellIndex === 0 ? "th" : undefined, id: cellIndex === 0 ? labelId : undefined, scope: cellIndex === 0 ? "row" : undefined, padding: "normal", children: ((_a = column.options) === null || _a === undefined ? undefined : _a.customBodyRender)
352
+ ? column.options.customBodyRender(row[column.name])
353
+ : String(row[column.name]) }, String(column.name)));
354
+ })] }, index));
355
+ }), state.emptyRows > 0 && (jsx(TableRow, { style: {
356
+ height: 33 * state.emptyRows,
357
+ }, children: jsx(TableCell, { colSpan: columns.length + 1 }) }))] })] }) }), jsx(TablePagination, { rowsPerPageOptions: [5, 10, 25], component: "div", count: state.currentData.length, rowsPerPage: state.rowsPerPage, page: state.page, onPageChange: handleChangePage, onRowsPerPageChange: handleChangeRowsPerPage, labelRowsPerPage: ((_b = options === null || options === undefined ? undefined : options.translations) === null || _b === undefined ? undefined : _b.rowsPerPageText) || "Rows per page", labelDisplayedRows: ((_c = options === null || options === undefined ? undefined : options.translations) === null || _c === undefined ? undefined : _c.labelDisplayedRows) || (({ from, to, count }) => `${from}-${to} of ${count}`) })] }) })));
358
+ };
359
+
360
+ export { MUITable as default };
361
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}