tp-react-elements 1.3.11 → 1.3.13
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 +23 -5
- package/dist/_virtual/index.esm13.js +2 -2
- package/dist/_virtual/index.esm14.js +2 -2
- package/dist/_virtual/index.esm4.js +2 -2
- package/dist/_virtual/index.esm5.js +2 -2
- package/dist/_virtual/index.esm6.js +2 -2
- package/dist/_virtual/index.esm7.js +2 -2
- package/dist/components/PaginatedGrid/DynamicFilterBar.d.ts +21 -1
- package/dist/components/PaginatedGrid/DynamicFilterBar.esm.js +71 -3
- package/dist/components/PaginatedGrid/GenericPaginatedGrid.d.ts +15 -1
- package/dist/components/PaginatedGrid/GenericPaginatedGrid.esm.js +64 -12
- package/dist/components/PaginatedGrid/mockData.d.ts +21 -0
- package/dist/components/PaginatedGrid/types.d.ts +22 -9
- package/dist/node_modules/react-date-range/dist/components/Calendar/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DateInput/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DateRange/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DateRangePicker/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DefinedRange/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/InputRangeField/index.esm.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -175,23 +175,41 @@ Refer to our Storybook documentation for detailed API documentation and examples
|
|
|
175
175
|
npm run storybook
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
- **Docs → Paginated Grid** —
|
|
179
|
-
- **Components → PaginatedGrid → Sample Grids** — mock grids (no API): header wrap
|
|
178
|
+
- **Docs → Paginated Grid** — API reference plus **four copy-paste examples** (embedded panel, wrapped headers, typography, invoice filters)
|
|
179
|
+
- **Components → PaginatedGrid → Sample Grids** — mock grids (no API): header wrap, compact layout, viewport fill
|
|
180
180
|
- **Components → PaginatedGrid → Example Grid** — live invoice API demo
|
|
181
181
|
|
|
182
|
-
Example
|
|
182
|
+
**Example — compact embedded grid with smaller fonts:**
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
<GenericPaginatedGrid
|
|
186
|
+
tableHeight="calc(100vh - 240px)"
|
|
187
|
+
autoFillViewport={false}
|
|
188
|
+
uiDensity="compact"
|
|
189
|
+
headerBgColor="#f5f5f5"
|
|
190
|
+
headerMaxHeight={64}
|
|
191
|
+
headerFontSize={11}
|
|
192
|
+
bodyFontSize={10}
|
|
193
|
+
{...gridProps}
|
|
194
|
+
/>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Example — wrapped header “Department / Business Unit”:**
|
|
183
198
|
|
|
184
199
|
```tsx
|
|
185
200
|
<GenericPaginatedGrid
|
|
186
201
|
headerBgColor="#e8eaf6"
|
|
187
202
|
headerMinHeight="48px"
|
|
188
203
|
headerMaxHeight="96px"
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
headerFontSize={12}
|
|
205
|
+
bodyFontSize={12}
|
|
206
|
+
layoutMode="grid"
|
|
191
207
|
{...gridProps}
|
|
192
208
|
/>
|
|
193
209
|
```
|
|
194
210
|
|
|
211
|
+
Full examples: Storybook **Docs → Paginated Grid → Examples**.
|
|
212
|
+
|
|
195
213
|
## 🏗️ Best Practices for Code Structure and Organization
|
|
196
214
|
|
|
197
215
|
### Component Organization
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var InputRangeField = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { InputRangeField as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateInput = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateInput as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateRangePicker = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateRangePicker as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var Calendar = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { Calendar as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DefinedRange = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DefinedRange as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateRange = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateRange as __exports };
|
|
@@ -10,6 +10,11 @@ import type { MRT_ColumnFiltersState } from 'material-react-table';
|
|
|
10
10
|
* quickly filter long option lists. Same `filterKey` / `options`
|
|
11
11
|
* shape as `dropdown`.
|
|
12
12
|
* - `date` — single date-picker chip. Applied client-side via MRT `columnFilters`.
|
|
13
|
+
* - `multiSelect` — checkbox list that allows picking multiple values at once.
|
|
14
|
+
* Selected values are joined as a comma-separated string in
|
|
15
|
+
* `filterValues[filterKey]` (e.g. `"Pending,Shipped"`).
|
|
16
|
+
* Your `fetchPage` function should split on `","` to build the
|
|
17
|
+
* correct server query.
|
|
13
18
|
*/
|
|
14
19
|
export type ServerFilterDef = {
|
|
15
20
|
type: 'dateRange';
|
|
@@ -55,6 +60,21 @@ export type ServerFilterDef = {
|
|
|
55
60
|
id: string;
|
|
56
61
|
label: string;
|
|
57
62
|
columnKey: string;
|
|
63
|
+
} | {
|
|
64
|
+
/**
|
|
65
|
+
* Multi-select checkbox chip — users can pick **multiple** values at once.
|
|
66
|
+
* Selected values are stored as a comma-separated string in
|
|
67
|
+
* `filterValues[filterKey]` (e.g. `"Pending,Shipped,Delivered"`).
|
|
68
|
+
* Split on `","` in your `fetchPage` handler to build the server query.
|
|
69
|
+
*/
|
|
70
|
+
type: 'multiSelect';
|
|
71
|
+
id: string;
|
|
72
|
+
label: string;
|
|
73
|
+
filterKey: string;
|
|
74
|
+
options: {
|
|
75
|
+
label: string;
|
|
76
|
+
value: string;
|
|
77
|
+
}[];
|
|
58
78
|
};
|
|
59
79
|
interface AvailableColumn {
|
|
60
80
|
id: string;
|
|
@@ -64,7 +84,7 @@ interface AvailableColumn {
|
|
|
64
84
|
* Sourced from the `filterType` prop on the column's `TPColumnDef`.
|
|
65
85
|
* Defaults to `'text'` when omitted.
|
|
66
86
|
*/
|
|
67
|
-
filterType?: 'text' | 'date' | 'select' | 'searchable-select' | 'number';
|
|
87
|
+
filterType?: 'text' | 'date' | 'select' | 'searchable-select' | 'number' | 'multi-select';
|
|
68
88
|
/** Options for `'select'` / `'searchable-select'` chip variants. */
|
|
69
89
|
filterOptions?: {
|
|
70
90
|
label: string;
|
|
@@ -9,7 +9,7 @@ import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
|
|
9
9
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
|
10
10
|
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
11
11
|
import SingleSelectNonAutoComplete from '../FormComponents/Select/SingleSelectNonAutoComplete.esm.js';
|
|
12
|
-
import { Box, Typography, Tooltip, Button, Popover, TextField, Stack, InputAdornment, List, ListItemButton, ListItemText, Divider } from '@mui/material';
|
|
12
|
+
import { Box, Typography, Tooltip, Button, Popover, TextField, Stack, InputAdornment, List, ListItemButton, ListItemText, Checkbox, Divider } from '@mui/material';
|
|
13
13
|
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
|
|
14
14
|
import TuneIcon from '@mui/icons-material/Tune';
|
|
15
15
|
import CloseIcon from '@mui/icons-material/Close';
|
|
@@ -29,6 +29,7 @@ const TYPE_PILL = {
|
|
|
29
29
|
text: { label: 'Text', color: '#2e7d32', bg: '#e8f5e9' },
|
|
30
30
|
dropdown: { label: 'Select', color: '#6a1b9a', bg: '#f3e5f5' },
|
|
31
31
|
searchableDropdown: { label: 'Search', color: '#bf360c', bg: '#fbe9e7' },
|
|
32
|
+
multiSelect: { label: 'Multi', color: '#0277bd', bg: '#e1f5fe' },
|
|
32
33
|
};
|
|
33
34
|
// Column filter type pills (used in COLUMN FILTERS section of Manage Filters)
|
|
34
35
|
const COLUMN_TYPE_PILL = {
|
|
@@ -37,6 +38,7 @@ const COLUMN_TYPE_PILL = {
|
|
|
37
38
|
select: { label: 'Select', color: '#bf360c', bg: '#fbe9e7' },
|
|
38
39
|
'searchable-select': { label: 'Search', color: '#bf360c', bg: '#fbe9e7' },
|
|
39
40
|
number: { label: 'Number', color: '#e65100', bg: '#fff3e0' },
|
|
41
|
+
'multi-select': { label: 'Multi', color: '#0277bd', bg: '#e1f5fe' },
|
|
40
42
|
};
|
|
41
43
|
const chipSx = (active, opts) => ({
|
|
42
44
|
borderRadius: 5,
|
|
@@ -244,6 +246,41 @@ const SearchableSelectChip = ({ label, value, options, onApply, onRemove, placeh
|
|
|
244
246
|
} }), isSelected && (jsx(CheckIcon, { sx: { fontSize: 15, color: 'primary.main', flexShrink: 0 } }))] }, opt.value));
|
|
245
247
|
}))] })] })] }));
|
|
246
248
|
};
|
|
249
|
+
const MultiSelectFilterChip = ({ label, selectedValues, options, onApply, onRemove, chipStyle, }) => {
|
|
250
|
+
const [anchor, setAnchor] = useState(null);
|
|
251
|
+
const active = selectedValues.length > 0;
|
|
252
|
+
const toggle = (value) => {
|
|
253
|
+
const next = selectedValues.includes(value)
|
|
254
|
+
? selectedValues.filter((v) => v !== value)
|
|
255
|
+
: [...selectedValues, value];
|
|
256
|
+
onApply(next);
|
|
257
|
+
};
|
|
258
|
+
const chipText = (() => {
|
|
259
|
+
if (!active)
|
|
260
|
+
return label;
|
|
261
|
+
if (selectedValues.length === 1) {
|
|
262
|
+
const found = options.find((o) => o.value === selectedValues[0]);
|
|
263
|
+
return `${label}: ${found?.label ?? selectedValues[0]}`;
|
|
264
|
+
}
|
|
265
|
+
if (selectedValues.length === 2) {
|
|
266
|
+
const names = selectedValues.map((v) => options.find((o) => o.value === v)?.label ?? v);
|
|
267
|
+
return `${label}: ${names.join(', ')}`;
|
|
268
|
+
}
|
|
269
|
+
return `${label}: ${selectedValues.length} selected`;
|
|
270
|
+
})();
|
|
271
|
+
return (jsxs(Fragment, { children: [jsx(Tooltip, { title: active ? chipText : '', placement: "top", arrow: true, children: jsx(Button, { variant: "outlined", size: "small", onClick: (e) => setAnchor(e.currentTarget), startIcon: jsx(FilterListIcon, { sx: { fontSize: '14px !important', mr: -0.25 } }), endIcon: jsx(CloseIcon, { sx: { fontSize: '12px !important' }, onClick: (e) => { e.stopPropagation(); onRemove(); } }), sx: chipSx(active, chipStyle), children: jsx(ChipLabel, { text: chipText }) }) }), jsxs(Popover, { open: Boolean(anchor), anchorEl: anchor, onClose: () => setAnchor(null), anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, PaperProps: { sx: { mt: 0.75, borderRadius: 2, boxShadow: 6, width: 260 } }, children: [active && (jsxs(Box, { sx: {
|
|
272
|
+
px: 2, py: 0.75,
|
|
273
|
+
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
|
274
|
+
borderBottom: '1px solid', borderColor: 'divider',
|
|
275
|
+
}, children: [jsxs(Typography, { sx: { fontSize: 11, color: 'primary.main', fontWeight: 600 }, children: [selectedValues.length, " selected"] }), jsx(Button, { size: "small", color: "inherit", sx: { fontSize: 11, py: 0, textTransform: 'none', minWidth: 'auto' }, onClick: () => onApply([]), children: "Clear all" })] })), jsx(List, { dense: true, disablePadding: true, sx: { maxHeight: 300, overflowY: 'auto', py: 0.5 }, children: options.map((opt) => {
|
|
276
|
+
const checked = selectedValues.includes(opt.value);
|
|
277
|
+
return (jsxs(ListItemButton, { onClick: () => toggle(opt.value), sx: { px: 1, py: 0.25 }, children: [jsx(Checkbox, { checked: checked, size: "small", disableRipple: true, sx: { p: 0.5, mr: 0.5, '& .MuiSvgIcon-root': { fontSize: 16 } } }), jsx(ListItemText, { primary: opt.label, primaryTypographyProps: {
|
|
278
|
+
fontSize: 13,
|
|
279
|
+
color: checked ? 'primary.main' : 'text.primary',
|
|
280
|
+
fontWeight: checked ? 600 : 400,
|
|
281
|
+
} })] }, opt.value));
|
|
282
|
+
}) })] })] }));
|
|
283
|
+
};
|
|
247
284
|
const ManageFiltersButton = ({ serverFilterDefs, visibleIds, availableColumns, onToggle, maxVisible = 4, chipStyle, }) => {
|
|
248
285
|
const [anchor, setAnchor] = useState(null);
|
|
249
286
|
const atLimit = visibleIds.length >= maxVisible;
|
|
@@ -296,11 +333,21 @@ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter,
|
|
|
296
333
|
minHeight: filterChipMinHeight ?? (uiDensity === 'compact' ? 26 : 30),
|
|
297
334
|
};
|
|
298
335
|
const labelFontSize = filterLabelFontSize ?? (uiDensity === 'compact' ? 11 : 12);
|
|
336
|
+
// ── Single-value column filter helpers ──────────────────────────────────────
|
|
299
337
|
const getColValue = (id) => columnFilters.find((f) => f.id === id)?.value ?? '';
|
|
300
338
|
const applyColFilter = (id, value) => {
|
|
301
339
|
const rest = columnFilters.filter((f) => f.id !== id);
|
|
302
340
|
onColumnFilterChange(value ? [...rest, { id, value }] : rest);
|
|
303
341
|
};
|
|
342
|
+
// ── Multi-value column filter helpers ────────────────────────────────────────
|
|
343
|
+
const getColValues = (id) => {
|
|
344
|
+
const val = columnFilters.find((f) => f.id === id)?.value;
|
|
345
|
+
return Array.isArray(val) ? val : [];
|
|
346
|
+
};
|
|
347
|
+
const applyColMultiFilter = (id, values) => {
|
|
348
|
+
const rest = columnFilters.filter((f) => f.id !== id);
|
|
349
|
+
onColumnFilterChange(values.length > 0 ? [...rest, { id, value: values }] : rest);
|
|
350
|
+
};
|
|
304
351
|
const removeFilter = (id) => {
|
|
305
352
|
const def = serverFilterDefs.find((d) => d.id === id);
|
|
306
353
|
if (def?.type === 'dateRange') {
|
|
@@ -308,14 +355,22 @@ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter,
|
|
|
308
355
|
}
|
|
309
356
|
else if (def?.type === 'text' ||
|
|
310
357
|
def?.type === 'dropdown' ||
|
|
311
|
-
def?.type === 'searchableDropdown'
|
|
358
|
+
def?.type === 'searchableDropdown' ||
|
|
359
|
+
def?.type === 'multiSelect') {
|
|
312
360
|
onFilterValuesChange({ ...filterValues, [def.filterKey]: '' });
|
|
313
361
|
}
|
|
314
362
|
else if (def?.type === 'date') {
|
|
315
363
|
applyColFilter(def.columnKey, '');
|
|
316
364
|
}
|
|
317
365
|
else {
|
|
318
|
-
|
|
366
|
+
// Column filter — multi-select stores string[], others store string
|
|
367
|
+
const colDef = availableColumns.find((c) => c.id === id);
|
|
368
|
+
if (colDef?.filterType === 'multi-select') {
|
|
369
|
+
applyColMultiFilter(id, []);
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
applyColFilter(id, '');
|
|
373
|
+
}
|
|
319
374
|
}
|
|
320
375
|
onToggleFilter(id);
|
|
321
376
|
};
|
|
@@ -333,6 +388,16 @@ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter,
|
|
|
333
388
|
if (def?.type === 'searchableDropdown') {
|
|
334
389
|
return (jsx(SearchableSelectChip, { label: def.label, value: filterValues[def.filterKey] ?? '', options: def.options, onApply: (v) => onFilterValuesChange({ ...filterValues, [def.filterKey]: v }), onRemove: () => removeFilter(id), placeholder: def.placeholder ?? 'Search…', chipStyle: chipStyle }, id));
|
|
335
390
|
}
|
|
391
|
+
if (def?.type === 'multiSelect') {
|
|
392
|
+
// filterValues[key] stores comma-separated values: "Pending,Shipped"
|
|
393
|
+
const selected = (filterValues[def.filterKey] ?? '')
|
|
394
|
+
.split(',')
|
|
395
|
+
.filter(Boolean);
|
|
396
|
+
return (jsx(MultiSelectFilterChip, { label: def.label, selectedValues: selected, options: def.options, onApply: (vals) => onFilterValuesChange({
|
|
397
|
+
...filterValues,
|
|
398
|
+
[def.filterKey]: vals.join(','),
|
|
399
|
+
}), onRemove: () => removeFilter(id), chipStyle: chipStyle }, id));
|
|
400
|
+
}
|
|
336
401
|
if (def?.type === 'date') {
|
|
337
402
|
return (jsx(DatePickerChip, { label: def.label, value: getColValue(def.columnKey), onApply: (v) => applyColFilter(def.columnKey, v), onRemove: () => removeFilter(id), chipStyle: chipStyle }, id));
|
|
338
403
|
}
|
|
@@ -346,6 +411,9 @@ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter,
|
|
|
346
411
|
if (col.filterType === 'select' || col.filterType === 'searchable-select') {
|
|
347
412
|
return (jsx(SearchableSelectChip, { label: col.label, value: getColValue(id), options: col.filterOptions ?? [], onApply: (v) => applyColFilter(id, v), onRemove: () => removeFilter(id), chipStyle: chipStyle }, id));
|
|
348
413
|
}
|
|
414
|
+
if (col.filterType === 'multi-select') {
|
|
415
|
+
return (jsx(MultiSelectFilterChip, { label: col.label, selectedValues: getColValues(id), options: col.filterOptions ?? [], onApply: (vals) => applyColMultiFilter(id, vals), onRemove: () => removeFilter(id), chipStyle: chipStyle }, id));
|
|
416
|
+
}
|
|
349
417
|
// Default: free-text input
|
|
350
418
|
return (jsx(TextFilterChip, { label: col.label, value: getColValue(id), onApply: (v) => applyColFilter(id, v), onRemove: () => removeFilter(id), placeholder: `Filter by ${col.label}`, chipStyle: chipStyle }, id));
|
|
351
419
|
}), jsx(ManageFiltersButton, { serverFilterDefs: serverFilterDefs, visibleIds: visibleFilterIds, availableColumns: availableColumns, onToggle: onToggleFilter, maxVisible: maxVisibleFilters, chipStyle: chipStyle })] }));
|
|
@@ -19,6 +19,10 @@ export interface GridUiDensityOptions {
|
|
|
19
19
|
filterChipMinHeight?: number;
|
|
20
20
|
filterLabelFontSize?: number;
|
|
21
21
|
toolbarIconFontSize?: number | string;
|
|
22
|
+
/** Header column title font size (px number). */
|
|
23
|
+
headerFontSize?: number;
|
|
24
|
+
/** Body cell data font size (px number). */
|
|
25
|
+
bodyFontSize?: number;
|
|
22
26
|
/** MRT built-in toolbar control density (show/hide columns, density toggle, etc.). */
|
|
23
27
|
mrtDensity?: 'comfortable' | 'compact' | 'spacious';
|
|
24
28
|
}
|
|
@@ -102,6 +106,16 @@ export interface GenericPaginatedGridProps<TData extends object> {
|
|
|
102
106
|
* Omit to allow text to use the full `headerMaxHeight` (recommended for tall headers).
|
|
103
107
|
*/
|
|
104
108
|
headerMaxLines?: number;
|
|
109
|
+
/**
|
|
110
|
+
* Font size for header column titles (CSS length or px number).
|
|
111
|
+
* Default: `12px` (`11px` when `uiDensity="compact"`). Overridden by `uiDensityOptions.headerFontSize`.
|
|
112
|
+
*/
|
|
113
|
+
headerFontSize?: string | number;
|
|
114
|
+
/**
|
|
115
|
+
* Font size for body/data cells (CSS length or px number).
|
|
116
|
+
* Default: `12px` (`11px` compact). Overridden by `uiDensityOptions.bodyFontSize`.
|
|
117
|
+
*/
|
|
118
|
+
bodyFontSize?: string | number;
|
|
105
119
|
/**
|
|
106
120
|
* Maximum number of filter chips that can be visible at once.
|
|
107
121
|
* Prevents the filter bar from wrapping to a second row on typical screens.
|
|
@@ -109,5 +123,5 @@ export interface GenericPaginatedGridProps<TData extends object> {
|
|
|
109
123
|
*/
|
|
110
124
|
maxVisibleFilters?: number;
|
|
111
125
|
}
|
|
112
|
-
declare function GenericPaginatedGrid<TData extends object>({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize, exportFilename, tableHeight, tableMinHeight, tableMaxHeight, autoFillViewport, layoutMode, uiDensity, uiDensityOptions, pageSizeOptions, headerBgColor, headerMinHeight, headerMaxHeight, headerMaxLines, maxVisibleFilters, }: GenericPaginatedGridProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
126
|
+
declare function GenericPaginatedGrid<TData extends object>({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize, exportFilename, tableHeight, tableMinHeight, tableMaxHeight, autoFillViewport, layoutMode, uiDensity, uiDensityOptions, pageSizeOptions, headerBgColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters, }: GenericPaginatedGridProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
113
127
|
export default GenericPaginatedGrid;
|
|
@@ -21,6 +21,8 @@ const UI_DENSITY_PRESETS = {
|
|
|
21
21
|
filterChipMinHeight: 30,
|
|
22
22
|
filterLabelFontSize: 12,
|
|
23
23
|
toolbarIconFontSize: 'small',
|
|
24
|
+
headerFontSize: 12,
|
|
25
|
+
bodyFontSize: 12,
|
|
24
26
|
mrtDensity: 'comfortable',
|
|
25
27
|
},
|
|
26
28
|
compact: {
|
|
@@ -32,6 +34,8 @@ const UI_DENSITY_PRESETS = {
|
|
|
32
34
|
filterChipMinHeight: 26,
|
|
33
35
|
filterLabelFontSize: 11,
|
|
34
36
|
toolbarIconFontSize: 'inherit',
|
|
37
|
+
headerFontSize: 11,
|
|
38
|
+
bodyFontSize: 11,
|
|
35
39
|
mrtDensity: 'compact',
|
|
36
40
|
},
|
|
37
41
|
};
|
|
@@ -44,7 +48,13 @@ const toCssSize = (value, fallback) => {
|
|
|
44
48
|
* Header overrides applied via GlobalStyles (literal prop values).
|
|
45
49
|
* MRT sets `backgroundColor: 'inherit'` and fixed density padding — must use !important.
|
|
46
50
|
*/
|
|
47
|
-
const
|
|
51
|
+
const buildBodyGlobalStyles = (bodyFont) => ({
|
|
52
|
+
'.tp-gpg-root .MuiTableBody-root .MuiTableCell-root': {
|
|
53
|
+
fontSize: `${bodyFont} !important`,
|
|
54
|
+
lineHeight: 1.35,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const buildHeaderGlobalStyles = (headerMin, headerMax, headerFont, headerBgColor) => {
|
|
48
58
|
const headCell = {
|
|
49
59
|
height: `${headerMax} !important`,
|
|
50
60
|
minHeight: `${headerMin} !important`,
|
|
@@ -69,7 +79,10 @@ const buildHeaderGlobalStyles = (headerMin, headerMax, headerBgColor) => {
|
|
|
69
79
|
alignItems: 'stretch !important',
|
|
70
80
|
...(headerBgColor ? { backgroundColor: headerBgColor } : {}),
|
|
71
81
|
},
|
|
72
|
-
'.tp-gpg-root .MuiTableHead-root .MuiTableCell-head':
|
|
82
|
+
'.tp-gpg-root .MuiTableHead-root .MuiTableCell-head': {
|
|
83
|
+
...headCell,
|
|
84
|
+
fontSize: `${headerFont} !important`,
|
|
85
|
+
},
|
|
73
86
|
'.tp-gpg-root .MuiTableHead-root .Mui-TableHeadCell-Content': {
|
|
74
87
|
height: '100% !important',
|
|
75
88
|
width: '100% !important',
|
|
@@ -92,6 +105,7 @@ const buildHeaderGlobalStyles = (headerMin, headerMax, headerBgColor) => {
|
|
|
92
105
|
overflowWrap: 'anywhere !important',
|
|
93
106
|
width: '100% !important',
|
|
94
107
|
maxWidth: '100% !important',
|
|
108
|
+
fontSize: `${headerFont} !important`,
|
|
95
109
|
display: 'block !important',
|
|
96
110
|
WebkitLineClamp: 'unset !important',
|
|
97
111
|
WebkitBoxOrient: 'unset !important',
|
|
@@ -101,11 +115,12 @@ const buildHeaderGlobalStyles = (headerMin, headerMax, headerBgColor) => {
|
|
|
101
115
|
wordBreak: 'break-word !important',
|
|
102
116
|
overflowWrap: 'anywhere !important',
|
|
103
117
|
width: '100% !important',
|
|
118
|
+
fontSize: `${headerFont} !important`,
|
|
104
119
|
},
|
|
105
120
|
};
|
|
106
121
|
};
|
|
107
122
|
/** Per-cell sx merged into MRT (via muiTableHeadCellProps). */
|
|
108
|
-
const buildHeaderCellSx = (align, headerMin, headerMax, headerBgColor, headerMaxLines) => {
|
|
123
|
+
const buildHeaderCellSx = (align, headerMin, headerMax, headerFont, headerBgColor, headerMaxLines) => {
|
|
109
124
|
const textAlign = align;
|
|
110
125
|
const flexJustify = align === 'right' ? 'flex-end' : align === 'center' ? 'center' : 'flex-start';
|
|
111
126
|
const wrapperClamp = headerMaxLines != null && headerMaxLines > 0
|
|
@@ -127,6 +142,7 @@ const buildHeaderCellSx = (align, headerMin, headerMax, headerBgColor, headerMax
|
|
|
127
142
|
height: `${headerMax} !important`,
|
|
128
143
|
minHeight: `${headerMin} !important`,
|
|
129
144
|
maxHeight: `${headerMax} !important`,
|
|
145
|
+
fontSize: headerFont,
|
|
130
146
|
boxSizing: 'border-box',
|
|
131
147
|
borderRight: '1px solid rgba(224,224,224,1)',
|
|
132
148
|
borderBottom: '2px solid rgba(0,0,0,0.15)',
|
|
@@ -153,6 +169,7 @@ const buildHeaderCellSx = (align, headerMin, headerMax, headerBgColor, headerMax
|
|
|
153
169
|
wordBreak: 'break-word',
|
|
154
170
|
overflowWrap: 'anywhere',
|
|
155
171
|
lineHeight: 1.25,
|
|
172
|
+
fontSize: headerFont,
|
|
156
173
|
width: '100%',
|
|
157
174
|
textAlign,
|
|
158
175
|
...wrapperClamp,
|
|
@@ -160,6 +177,7 @@ const buildHeaderCellSx = (align, headerMin, headerMax, headerBgColor, headerMax
|
|
|
160
177
|
'& .MuiTableSortLabel-root': {
|
|
161
178
|
whiteSpace: 'normal',
|
|
162
179
|
wordBreak: 'break-word',
|
|
180
|
+
fontSize: headerFont,
|
|
163
181
|
width: '100%',
|
|
164
182
|
margin: 0,
|
|
165
183
|
},
|
|
@@ -203,11 +221,22 @@ const PaginationFooter = ({ pageIndex, pageSize, totalRecords, isFetching, isLoa
|
|
|
203
221
|
}, children: pageIndex + 1 }), jsx(Tooltip, { title: "Next page", children: jsx("span", { children: jsx(IconButton, { 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, { size: "small", onClick: onLast, disabled: !canNext, sx: { p: 0.5 }, children: jsx(LastPageIcon, { sx: { fontSize: density.footerIconSize } }) }) }) })] })] }));
|
|
204
222
|
};
|
|
205
223
|
// ─── GenericPaginatedGrid ─────────────────────────────────────────────────────
|
|
206
|
-
function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize = 10000, exportFilename = 'export', tableHeight, tableMinHeight = '300px', tableMaxHeight, autoFillViewport = true, layoutMode = 'grid', uiDensity = 'default', uiDensityOptions, pageSizeOptions = [1000, 5000, 10000], headerBgColor, headerMinHeight, headerMaxHeight, headerMaxLines, maxVisibleFilters = 4, }) {
|
|
224
|
+
function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs, defaultFilters, defaultVisibleFilterIds, batchSize = 10000, exportFilename = 'export', tableHeight, tableMinHeight = '300px', tableMaxHeight, autoFillViewport = true, layoutMode = 'grid', uiDensity = 'default', uiDensityOptions, pageSizeOptions = [1000, 5000, 10000], headerBgColor, headerMinHeight, headerMaxHeight, headerMaxLines, headerFontSize, bodyFontSize, maxVisibleFilters = 4, }) {
|
|
207
225
|
const densityTokens = useMemo(() => ({ ...UI_DENSITY_PRESETS[uiDensity], ...uiDensityOptions }), [uiDensity, uiDensityOptions]);
|
|
208
226
|
const resolvedHeaderMinHeight = toCssSize(headerMinHeight, uiDensity === 'compact' ? '34px' : '40px');
|
|
209
227
|
const resolvedHeaderMaxHeight = toCssSize(headerMaxHeight, uiDensity === 'compact' ? '56px' : '72px');
|
|
210
|
-
const
|
|
228
|
+
const resolvedHeaderFontSize = toCssSize(headerFontSize ?? densityTokens.headerFontSize, uiDensity === 'compact' ? '11px' : '12px');
|
|
229
|
+
const resolvedBodyFontSize = toCssSize(bodyFontSize ?? densityTokens.bodyFontSize, uiDensity === 'compact' ? '11px' : '12px');
|
|
230
|
+
const tableStyleOverrides = useMemo(() => ({
|
|
231
|
+
...buildHeaderGlobalStyles(resolvedHeaderMinHeight, resolvedHeaderMaxHeight, resolvedHeaderFontSize, headerBgColor),
|
|
232
|
+
...buildBodyGlobalStyles(resolvedBodyFontSize),
|
|
233
|
+
}), [
|
|
234
|
+
resolvedHeaderMinHeight,
|
|
235
|
+
resolvedHeaderMaxHeight,
|
|
236
|
+
resolvedHeaderFontSize,
|
|
237
|
+
resolvedBodyFontSize,
|
|
238
|
+
headerBgColor,
|
|
239
|
+
]);
|
|
211
240
|
const defaultVisible = defaultVisibleFilterIds
|
|
212
241
|
?? (serverFilterDefs.length > 0 ? [serverFilterDefs[0].id] : []);
|
|
213
242
|
// ── State ──────────────────────────────────────────────────────────────────
|
|
@@ -286,6 +315,13 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
|
|
|
286
315
|
? row.getValue(columnId) === filterValue
|
|
287
316
|
: true;
|
|
288
317
|
}
|
|
318
|
+
else if (filterType === 'multi-select') {
|
|
319
|
+
// Inclusion check: pass when the cell value is one of the selected values.
|
|
320
|
+
// filterValue is string[] (stored directly in MRT columnFilters).
|
|
321
|
+
mrtCol.filterFn = (row, columnId, filterValue) => !filterValue || filterValue.length === 0
|
|
322
|
+
? true
|
|
323
|
+
: filterValue.includes(row.getValue(columnId) ?? '');
|
|
324
|
+
}
|
|
289
325
|
// 'text' and 'number' use MRT's built-in client-side filtering
|
|
290
326
|
}
|
|
291
327
|
return mrtCol;
|
|
@@ -474,16 +510,32 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
|
|
|
474
510
|
return {
|
|
475
511
|
align,
|
|
476
512
|
sx: [
|
|
477
|
-
buildHeaderCellSx(align, resolvedHeaderMinHeight, resolvedHeaderMaxHeight, headerBgColor, headerMaxLines),
|
|
513
|
+
buildHeaderCellSx(align, resolvedHeaderMinHeight, resolvedHeaderMaxHeight, resolvedHeaderFontSize, headerBgColor, headerMaxLines),
|
|
478
514
|
...(colSx ? [colSx] : []),
|
|
479
515
|
],
|
|
480
516
|
};
|
|
481
517
|
},
|
|
482
|
-
muiTableBodyCellProps: {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
518
|
+
muiTableBodyCellProps: ({ column }) => {
|
|
519
|
+
const colProps = column.columnDef.muiTableBodyCellProps;
|
|
520
|
+
let colSx;
|
|
521
|
+
if (typeof colProps === 'function') {
|
|
522
|
+
const resolved = colProps({ column });
|
|
523
|
+
colSx = resolved?.sx;
|
|
524
|
+
}
|
|
525
|
+
else if (colProps) {
|
|
526
|
+
colSx = colProps.sx;
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
sx: [
|
|
530
|
+
{
|
|
531
|
+
whiteSpace: 'nowrap',
|
|
532
|
+
fontSize: resolvedBodyFontSize,
|
|
533
|
+
lineHeight: 1.35,
|
|
534
|
+
borderRight: '1px solid rgba(224,224,224,1)',
|
|
535
|
+
},
|
|
536
|
+
...(colSx ? [colSx] : []),
|
|
537
|
+
],
|
|
538
|
+
};
|
|
487
539
|
},
|
|
488
540
|
muiToolbarAlertBannerProps: isError
|
|
489
541
|
? { color: 'error', children: 'Failed to load data. Please try again.' }
|
|
@@ -505,7 +557,7 @@ function GenericPaginatedGrid({ columns, fetchPage, queryKey, serverFilterDefs,
|
|
|
505
557
|
'.tp-gpg-root button[aria-label="Move"]': { display: 'none !important' },
|
|
506
558
|
'.tp-gpg-root [data-column-id="mrt-row-spacer"]': { display: 'none !important' },
|
|
507
559
|
'.tp-gpg-root .Mui-TableHeadCell-ColumnSpacer': { display: 'none !important' },
|
|
508
|
-
...
|
|
560
|
+
...tableStyleOverrides,
|
|
509
561
|
} }), 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 }) })] }));
|
|
510
562
|
}
|
|
511
563
|
|
|
@@ -31,3 +31,24 @@ export declare const productColumns: TPColumnDef<ProductRow>[];
|
|
|
31
31
|
export declare const productFilterDefs: ServerFilterDef[];
|
|
32
32
|
export declare const productDefaultFilters: Record<string, string>;
|
|
33
33
|
export declare function fetchProductPage(filters: Record<string, string>, pageIndex: number, pageSize?: number): Promise<PagedResult<ProductRow>>;
|
|
34
|
+
export interface OrderRow {
|
|
35
|
+
orderId: string;
|
|
36
|
+
customer: string;
|
|
37
|
+
category: string;
|
|
38
|
+
status: string;
|
|
39
|
+
priority: string;
|
|
40
|
+
amount: number;
|
|
41
|
+
qty: number;
|
|
42
|
+
orderDate: string;
|
|
43
|
+
}
|
|
44
|
+
export declare const orderColumns: TPColumnDef<OrderRow>[];
|
|
45
|
+
/**
|
|
46
|
+
* Server filter definitions for the Orders demo grid.
|
|
47
|
+
*
|
|
48
|
+
* `multiSelect` chips for Status and Category send comma-separated values
|
|
49
|
+
* in `filterValues` (e.g. `status: "Pending,Shipped"`).
|
|
50
|
+
* `fetchOrderPage` splits on `","` to apply the server-side filter.
|
|
51
|
+
*/
|
|
52
|
+
export declare const orderFilterDefs: ServerFilterDef[];
|
|
53
|
+
export declare const orderDefaultFilters: Record<string, string>;
|
|
54
|
+
export declare function fetchOrderPage(filters: Record<string, string>, pageIndex: number, pageSize?: number): Promise<PagedResult<OrderRow>>;
|
|
@@ -2,18 +2,19 @@ import type { MRT_ColumnDef } from 'material-react-table';
|
|
|
2
2
|
/**
|
|
3
3
|
* Filter chip variant shown in Manage Filters → Column Filters.
|
|
4
4
|
*
|
|
5
|
-
* | Value | Chip rendered
|
|
6
|
-
*
|
|
7
|
-
* | `'text'` | Free-text input (default)
|
|
8
|
-
* | `'date'` | Calendar date picker
|
|
9
|
-
* | `'select'` | Searchable option list
|
|
10
|
-
* | `'searchable-select'` | Searchable option list
|
|
11
|
-
* | `'number'` | Numeric input
|
|
5
|
+
* | Value | Chip rendered |
|
|
6
|
+
* |---------------------|------------------------------------------------------|
|
|
7
|
+
* | `'text'` | Free-text input (default) |
|
|
8
|
+
* | `'date'` | Calendar date picker |
|
|
9
|
+
* | `'select'` | Searchable single-select option list |
|
|
10
|
+
* | `'searchable-select'` | Searchable single-select option list |
|
|
11
|
+
* | `'number'` | Numeric input |
|
|
12
|
+
* | `'multi-select'` | Checkbox list — pick many values, filters client-side|
|
|
12
13
|
*
|
|
13
14
|
* Set `enableColumnFilter: false` to hide the column from Manage Filters
|
|
14
15
|
* entirely (e.g. for computed or numeric columns that don't need filtering).
|
|
15
16
|
*/
|
|
16
|
-
export type ColumnFilterType = 'text' | 'date' | 'select' | 'searchable-select' | 'number';
|
|
17
|
+
export type ColumnFilterType = 'text' | 'date' | 'select' | 'searchable-select' | 'number' | 'multi-select';
|
|
17
18
|
/**
|
|
18
19
|
* Drop-in replacement for `MRT_ColumnDef` that adds top-level
|
|
19
20
|
* `filterType` and `options` so you never have to write `meta` blocks
|
|
@@ -27,7 +28,7 @@ export type ColumnFilterType = 'text' | 'date' | 'select' | 'searchable-select'
|
|
|
27
28
|
* // Date picker chip
|
|
28
29
|
* { accessorKey: 'invoiceDate', header: 'Invoice Date', filterType: 'date' },
|
|
29
30
|
*
|
|
30
|
-
* // Searchable
|
|
31
|
+
* // Searchable single-select chip
|
|
31
32
|
* {
|
|
32
33
|
* accessorKey: 'status',
|
|
33
34
|
* header: 'Status',
|
|
@@ -38,6 +39,18 @@ export type ColumnFilterType = 'text' | 'date' | 'select' | 'searchable-select'
|
|
|
38
39
|
* ],
|
|
39
40
|
* },
|
|
40
41
|
*
|
|
42
|
+
* // Multi-select checkbox chip — pick many values, client-side
|
|
43
|
+
* {
|
|
44
|
+
* accessorKey: 'priority',
|
|
45
|
+
* header: 'Priority',
|
|
46
|
+
* filterType: 'multi-select',
|
|
47
|
+
* options: [
|
|
48
|
+
* { label: 'Low', value: 'Low' },
|
|
49
|
+
* { label: 'Medium', value: 'Medium' },
|
|
50
|
+
* { label: 'High', value: 'High' },
|
|
51
|
+
* ],
|
|
52
|
+
* },
|
|
53
|
+
*
|
|
41
54
|
* // Hidden from Manage Filters (numeric column)
|
|
42
55
|
* { accessorKey: 'qty', header: 'Qty', enableColumnFilter: false },
|
|
43
56
|
* ];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as Calendar } from '../../../../../_virtual/index.
|
|
1
|
+
import { __exports as Calendar } from '../../../../../_virtual/index.esm5.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';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as DateInput } from '../../../../../_virtual/index.
|
|
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 DateRange } from '../../../../../_virtual/index.
|
|
1
|
+
import { __exports as DateRange } from '../../../../../_virtual/index.esm7.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 requireCalendar } from '../Calendar/index.esm.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as DateRangePicker } from '../../../../../_virtual/index.
|
|
1
|
+
import { __exports as DateRangePicker } from '../../../../../_virtual/index.esm4.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 requireDateRange } from '../DateRange/index.esm.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as DefinedRange } from '../../../../../_virtual/index.
|
|
1
|
+
import { __exports as DefinedRange } from '../../../../../_virtual/index.esm6.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 requireStyles } from '../../styles.esm.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as InputRangeField } from '../../../../../_virtual/index.
|
|
1
|
+
import { __exports as InputRangeField } 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
|
|