mui-datatables-updated 1.0.2 → 1.0.3

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,340 +0,0 @@
1
- import { Close, CloudDownload, Print } from "@mui/icons-material";
2
- import FilterListIcon from "@mui/icons-material/FilterList";
3
- import SearchIcon from "@mui/icons-material/Search";
4
- import { Box, Button, Checkbox, IconButton, Popover, Slider, Stack, TextField, Toolbar, Tooltip, Typography } from "@mui/material";
5
- import { alpha } from "@mui/material/styles";
6
- import React, { useEffect, useState } from "react";
7
- import { Column, Options } from "./MUITable";
8
- import { UseReactToPrintFn } from "react-to-print";
9
-
10
- interface Filter {
11
- key: string;
12
- type: "number" | "string" | "boolean";
13
- value?: string | boolean | number[];
14
- }
15
-
16
- interface FilterConfig {
17
- key: string;
18
- type: "number" | "string" | "boolean";
19
- min?: number;
20
- max?: number;
21
- }
22
-
23
- export interface CustomSelectedToolbarProps<T> {
24
- selected?: readonly T[];
25
- data?: T[];
26
- }
27
-
28
- interface EnhancedTableToolbarProps<T> {
29
- title: string;
30
- numSelected: number;
31
- selected: readonly T[];
32
- onFilterChange: (filterFunc: (row: T) => boolean) => void;
33
- onSearch: (query: string) => void;
34
- printFn: UseReactToPrintFn;
35
- columns: Column[];
36
- CustomToolbar?: React.FC;
37
- CustomSelectedToolbar?: React.FC<CustomSelectedToolbarProps<T>>;
38
- data?: T[];
39
- options?: Options;
40
- }
41
-
42
- export function EnhancedTableToolbar<T>(props: EnhancedTableToolbarProps<T>) {
43
- const { title, numSelected, selected, onFilterChange, onSearch, printFn, columns, CustomToolbar, CustomSelectedToolbar, data, options } = props;
44
-
45
- const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
46
- const [filters, setFilters] = useState<Filter[]>([]);
47
- const [filterConfig, setFilterConfig] = useState<FilterConfig[]>([]);
48
- // rest value to force re-render of filters
49
- const [resetCounter, setResetCounter] = useState(0);
50
- const [openSearch, setOpenSearch] = useState(false);
51
-
52
- function downloadCSV(data: Record<string, any>[], filename: string = "data.csv"): void {
53
- // Base case
54
- if (data.length === 0) {
55
- console.warn("No data to export.");
56
- return;
57
- }
58
-
59
- // Create CSV content
60
- const headers = Object.keys(data[0] as Record<string, any>);
61
- const csvRows = data.map(obj =>
62
- headers.map(field => JSON.stringify(obj[field] ?? "")).join(",")
63
- );
64
- const csvContent = [headers.join(","), ...csvRows].join("\n");
65
-
66
- // Create Blob and download
67
- const blob = new Blob([csvContent], { type: "text/csv" });
68
- const link = document.createElement("a");
69
- link.href = URL.createObjectURL(blob);
70
- link.download = filename;
71
- document.body.appendChild(link);
72
- link.click();
73
- document.body.removeChild(link);
74
- URL.revokeObjectURL(link.href);
75
- }
76
-
77
- useEffect(() => {
78
- if (data && data.length > 0) {
79
- const inferredConfig = columns.map((column) => {
80
- const key = column.name;
81
- const values = data.map((row) => (row as Record<string, any>)[key]);
82
- const isNumber = values.every((val) => typeof val === "number");
83
- const inferredType = isNumber ? "number" : typeof values[0] === "boolean" ? "boolean" : "string";
84
- return {
85
- key,
86
- type: inferredType as "number" | "string" | "boolean",
87
- min: isNumber ? Math.min(...values) : undefined,
88
- max: isNumber ? Math.max(...values) : undefined,
89
- };
90
- });
91
- setFilterConfig(inferredConfig);
92
- }
93
- }, [data, columns]);
94
-
95
- useEffect(() => {
96
- const newFilterFunc = (row: T) => {
97
- if (filters.length === 0) return true;
98
- return filters.every((filter) => {
99
- const rowValue = (row as Record<string, any>)[filter.key];
100
- if (filter.type === "number") {
101
- const [min, max] = filter.value as number[];
102
- return rowValue >= min && rowValue <= max;
103
- }
104
- if (filter.type === "string") {
105
- return rowValue.toString().toLowerCase().includes((filter.value as string).toLowerCase());
106
- }
107
- if (filter.type === "boolean") {
108
- return rowValue === filter.value;
109
- }
110
- return true;
111
- });
112
- };
113
- onFilterChange(newFilterFunc);
114
- }, [filters, onFilterChange]);
115
-
116
- const handleOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
117
- setAnchorEl(event.currentTarget);
118
- };
119
-
120
- const handleClose = () => {
121
- setAnchorEl(null);
122
- };
123
-
124
- const handleSearchChange = () => {
125
- setOpenSearch((prev) => !prev);
126
- };
127
-
128
- const open = Boolean(anchorEl);
129
-
130
- const getFilter = (key: string): Filter | undefined =>
131
- filters.find((filter) => filter.key === key);
132
-
133
- const updateFilter = (updatedFilter: Filter) => {
134
- setFilters((prevFilters) => {
135
- const existingIndex = prevFilters.findIndex((filter) => filter.key === updatedFilter.key);
136
- if (existingIndex !== -1) {
137
- // Update existing filter
138
- const newFilters = [...prevFilters];
139
- newFilters[existingIndex] = updatedFilter;
140
- return newFilters;
141
- }
142
- // Add new filter
143
- return [...prevFilters, updatedFilter];
144
- });
145
- };
146
-
147
- const removeFilter = (key: string) => {
148
- setFilters((prevFilters) => prevFilters.filter((filter) => filter.key !== key));
149
- };
150
-
151
- const handleFilterChange = (key: string, value: any) => {
152
- if (value === undefined || value === null || value === "") {
153
- removeFilter(key);
154
- } else {
155
- const config = filterConfig.find((config) => config.key === key);
156
- if (!config) return;
157
-
158
- updateFilter({
159
- key,
160
- type: config.type,
161
- value,
162
- });
163
- }
164
- };
165
-
166
- const resetFilters = () => {
167
- setFilters([]);
168
- setResetCounter((prev) => prev + 1);
169
- };
170
-
171
- return (
172
- <Toolbar
173
- sx={[
174
- { px: { sm: 2 }, borderBottom: 1, borderColor: "divider" },
175
- numSelected > 0 && { bgcolor: (theme) => alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity) },
176
- ]}
177
- >
178
- {numSelected > 0 ? (
179
- <>
180
- <Typography
181
- sx={{ flex: "1 1 100%" }}
182
- color="inherit"
183
- variant="subtitle1"
184
- component="div"
185
- >
186
- {options?.translations?.selectedTextRenderer
187
- ? options.translations.selectedTextRenderer(numSelected)
188
- : `${numSelected} selected`}
189
- </Typography>
190
- {CustomSelectedToolbar && (
191
- <CustomSelectedToolbar data={data} selected={selected} />
192
- )}
193
- </>
194
- ) : (
195
- <Stack
196
- direction="row"
197
- justifyContent="space-between"
198
- width="100%"
199
- alignItems="center"
200
- >
201
- {openSearch ? (
202
- <Stack direction="row" alignItems="center">
203
- <SearchIcon />
204
- <TextField
205
- placeholder={options?.translations?.searchPlaceholder || "Search..."}
206
- onChange={(e) => onSearch(e.target.value)}
207
- variant="standard"
208
- autoFocus
209
- fullWidth
210
- sx={{ marginLeft: 1 }}
211
- />
212
- <IconButton
213
- onClick={handleSearchChange}
214
- sx={{ '&:hover': { color: 'error.main' } }}
215
- >
216
- <Close />
217
- </IconButton>
218
- </Stack>
219
- ) : (
220
-
221
- <Typography
222
- sx={{ flex: "1 1 100%", alignContent: "center", paddingLeft: 1 }}
223
- variant="h6"
224
- id="tableTitle"
225
- component="div"
226
- >
227
- {title}
228
- </Typography>
229
- )}
230
- <Stack direction="row" spacing={0.5}>
231
- <Tooltip title={options?.translations?.searchTooltip || "Search"}>
232
- <IconButton onClick={handleSearchChange}>
233
- <SearchIcon />
234
- </IconButton>
235
- </Tooltip>
236
- <Tooltip title={options?.translations?.downloadTooltip || "Download CSV"}>
237
- <IconButton onClick={() => downloadCSV(data as Record<string, any>[], "data.csv")}>
238
- <CloudDownload />
239
- </IconButton>
240
- </Tooltip>
241
- <Tooltip title={options?.translations?.printTooltip || "Print"}>
242
- <IconButton onClick={() => printFn()}>
243
- <Print />
244
- </IconButton>
245
- </Tooltip>
246
- <Tooltip title={options?.translations?.filterTooltip || "Filter list"}>
247
- <IconButton onClick={handleOpen}>
248
- <FilterListIcon />
249
- </IconButton>
250
- </Tooltip>
251
- {CustomToolbar && <CustomToolbar />}
252
- </Stack>
253
- </Stack>
254
- )}
255
- <Popover
256
- open={open}
257
- anchorEl={anchorEl}
258
- onClose={handleClose}
259
- anchorOrigin={{
260
- vertical: "bottom",
261
- horizontal: "right",
262
- }}
263
- transformOrigin={{
264
- vertical: "top",
265
- horizontal: "right",
266
- }}
267
- slotProps={{
268
- paper: { sx: { width: "20%", padding: 2, boxShadow: 2 } }
269
- }}
270
- >
271
- <Stack direction="row" justifyContent="space-between">
272
- <Typography variant="h6" sx={{ marginBottom: 2 }}>
273
- {options?.translations?.filtersTitle || "Filters"}
274
- </Typography>
275
- <Button variant="contained" size="small" sx={{ height: "fit-content" }} onClick={resetFilters}>
276
- {options?.translations?.resetButtonText || "Reset"}
277
- </Button>
278
- </Stack>
279
- <Stack key={resetCounter}>
280
- {filterConfig.map(({ key, type, min, max }) => {
281
- const currentFilter = getFilter(key);
282
- return (
283
- <Box key={key}>
284
- <Typography variant="subtitle1">
285
- {columns.find((cell) => cell.name === key)?.label}
286
- </Typography>
287
- {type === "number" && min !== undefined && max !== undefined && (
288
- <Slider
289
- value={[
290
- (currentFilter?.value as number[] | undefined)?.[0] ?? min,
291
- (currentFilter?.value as number[] | undefined)?.[1] ?? max,
292
- ]}
293
- onChange={(_, newValue) => {
294
- const [newMin, newMax] = newValue as number[];
295
- handleFilterChange(key, [newMin, newMax]);
296
- }}
297
- valueLabelDisplay="auto"
298
- min={min || 0}
299
- max={max || 100}
300
- step={1}
301
- />
302
- )}
303
- {type === "string" && (
304
- <TextField
305
- placeholder={options?.translations?.searchPlaceholder || "Search..."}
306
- size="small"
307
- value={(currentFilter?.value as string) || ""}
308
- onChange={(e) => handleFilterChange(key, e.target.value)}
309
- sx={{
310
- marginBottom: 1,
311
- paddingY: 0.5,
312
- width: "100%",
313
- }}
314
- />
315
- )}
316
- {type === "boolean" && (
317
- <Box display="flex" alignItems="center">
318
- <Checkbox
319
- checked={currentFilter?.value === true}
320
- onChange={(e) =>
321
- handleFilterChange(key, e.target.checked)
322
- }
323
- />
324
- {currentFilter && (
325
- <Close
326
- color="error"
327
- sx={{ cursor: "pointer", fontSize: 15 }}
328
- onClick={() => handleFilterChange(key, undefined)}
329
- />
330
- )}
331
- </Box>
332
- )}
333
- </Box>
334
- );
335
- })}
336
- </Stack>
337
- </Popover>
338
- </Toolbar>
339
- );
340
- }
@@ -1,17 +0,0 @@
1
- export type Order = 'asc' | 'desc';
2
-
3
- export function getComparator<T, Key extends keyof T>(
4
- order: Order,
5
- orderBy: Key,
6
- ): (a: T, b: T) => number {
7
- return order === 'desc'
8
- ? (a, b) => descendingComparator(a[orderBy], b[orderBy])
9
- : (a, b) => -descendingComparator(a[orderBy], b[orderBy]);
10
- }
11
-
12
- function descendingComparator<T>(a: T, b: T): number {
13
- if (b < a) return -1;
14
- if (b > a) return 1;
15
- return 0;
16
- }
17
-
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export { MUITable as default } from './components/MUITable';
2
- export type { Column } from './components/MUITable';
3
- export type { CustomSelectedToolbarProps } from './components/Toolbar';
package/tsconfig.json DELETED
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2017",
4
- "module": "ESNext",
5
- "moduleResolution": "node",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "jsx": "react-jsx",
11
- "declaration": true,
12
- "declarationDir": "./dist/types",
13
- "outDir": "./dist",
14
- "isolatedModules": true
15
- },
16
- "include": [
17
- "src/**/*.ts",
18
- "src/**/*.tsx"
19
- ],
20
- "exclude": [
21
- "node_modules",
22
- "dist"
23
- ]
24
- }