@pagamio/frontend-commons-lib 0.8.310 → 0.8.312
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,37 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Input, MultiSelect, Select } from '@mantine/core';
|
|
3
2
|
import { IconSearch } from '@tabler/icons-react';
|
|
4
3
|
import { endOfDay, startOfDay } from 'date-fns';
|
|
5
|
-
import
|
|
4
|
+
import { cn } from '../../helpers';
|
|
6
5
|
import { isDefaultFilterValue } from '../../shared/utils/filterUtils';
|
|
7
6
|
import Button from './Button';
|
|
8
7
|
import DatePicker from './DatePicker';
|
|
9
8
|
import DateRangePickerWithPresets from './DateRangePickerWithPresets';
|
|
10
9
|
import FilterWrapper from './FilterWrapper';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
borderRadius: '6px',
|
|
14
|
-
},
|
|
15
|
-
label: {
|
|
16
|
-
fontSize: '13px',
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
const selectInputStyles = {
|
|
20
|
-
input: {
|
|
21
|
-
borderRadius: '6px',
|
|
22
|
-
fontSize: '0.8rem',
|
|
23
|
-
height: '39px',
|
|
24
|
-
},
|
|
25
|
-
dropdown: {
|
|
26
|
-
fontSize: '0.75rem',
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
const commonProps = {
|
|
30
|
-
size: 'md',
|
|
31
|
-
style: { width: 240 },
|
|
32
|
-
};
|
|
10
|
+
import MultiSelect from './MultiSelect';
|
|
11
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './Select';
|
|
33
12
|
const FilterComponent = ({ filters, selectedFilters, showApplyFilterButton = true, showClearFilters, searctInputPlaceHolder = 'Search...', showApplyFilters = true, showSearch = false, searchQuery = '', children, isNarrow, handleFilterChange, handleApplyFilters, resetFilters, onSearch = () => { }, }) => {
|
|
34
|
-
const [activeRangePicker, setActiveRangePicker] = React.useState(null);
|
|
35
13
|
const hasSelectedActiveFilters = Object.entries(selectedFilters).some(([key, v]) => !isDefaultFilterValue(v, key));
|
|
36
14
|
const shouldShowActionButtons = hasSelectedActiveFilters || (showSearch && searchQuery.length > 0);
|
|
37
15
|
const parseDateValue = (value) => {
|
|
@@ -44,7 +22,6 @@ const FilterComponent = ({ filters, selectedFilters, showApplyFilterButton = tru
|
|
|
44
22
|
const parsed = new Date(value);
|
|
45
23
|
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
46
24
|
};
|
|
47
|
-
// Keyboard handler for search input
|
|
48
25
|
const handleSearchKeyDown = (e) => {
|
|
49
26
|
if (e.key === 'Enter') {
|
|
50
27
|
handleApplyFilters();
|
|
@@ -54,28 +31,16 @@ const FilterComponent = ({ filters, selectedFilters, showApplyFilterButton = tru
|
|
|
54
31
|
resetFilters();
|
|
55
32
|
}
|
|
56
33
|
};
|
|
57
|
-
|
|
58
|
-
const handleFilterKeyDown = (e, name) => {
|
|
59
|
-
if (e.key === 'Enter') {
|
|
60
|
-
handleApplyFilters();
|
|
61
|
-
}
|
|
62
|
-
else if (e.key === 'Escape') {
|
|
63
|
-
handleFilterChange(name, '');
|
|
64
|
-
resetFilters();
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
return (_jsxs(FilterWrapper, { isNarrow: isNarrow, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [showSearch && (_jsx("div", { className: "w-full sm:w-[300px]", children: _jsx(Input, { leftSection: _jsx(IconSearch, { size: 16 }), placeholder: searctInputPlaceHolder, value: searchQuery, onChange: (event) => onSearch(event), onKeyDown: handleSearchKeyDown, className: "w-full", styles: { input: { height: 39, backgroundColor: 'hsl(var(--muted))' } }, "aria-label": "Search" }) })), filters.map((filter) => {
|
|
34
|
+
return (_jsxs(FilterWrapper, { isNarrow: isNarrow, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [showSearch && (_jsxs("div", { className: "relative w-full sm:w-[300px]", children: [_jsx(IconSearch, { size: 16, className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { type: "text", placeholder: searctInputPlaceHolder, value: searchQuery, onChange: onSearch, onKeyDown: handleSearchKeyDown, "aria-label": "Search", className: cn('h-9 w-full rounded-md border border-border bg-background pl-9 pr-3 text-sm text-foreground', 'placeholder:text-muted-foreground', 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring', 'disabled:cursor-not-allowed disabled:opacity-50') })] })), filters.map((filter) => {
|
|
68
35
|
const { name, type, options } = filter;
|
|
69
36
|
const value = selectedFilters[name];
|
|
70
37
|
const renderFilterInput = () => {
|
|
71
38
|
if (type === 'date-range') {
|
|
72
39
|
const rangeKeys = filter.rangeKeys;
|
|
73
|
-
if (!rangeKeys)
|
|
74
|
-
console.warn('Date range filter requires rangeKeys', filter);
|
|
40
|
+
if (!rangeKeys)
|
|
75
41
|
return null;
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const endDateValue = rangeKeys ? selectedFilters[rangeKeys.end] : undefined;
|
|
42
|
+
const startDateValue = selectedFilters[rangeKeys.start];
|
|
43
|
+
const endDateValue = selectedFilters[rangeKeys.end];
|
|
79
44
|
const startDate = parseDateValue(startDateValue);
|
|
80
45
|
const endDate = parseDateValue(endDateValue);
|
|
81
46
|
const dateRange = startDate && endDate
|
|
@@ -84,8 +49,6 @@ const FilterComponent = ({ filters, selectedFilters, showApplyFilterButton = tru
|
|
|
84
49
|
? { from: startDate, to: startDate }
|
|
85
50
|
: undefined;
|
|
86
51
|
const handleRangeChange = (range) => {
|
|
87
|
-
if (!rangeKeys)
|
|
88
|
-
return;
|
|
89
52
|
if (range?.from) {
|
|
90
53
|
handleFilterChange(rangeKeys.start, startOfDay(range.from));
|
|
91
54
|
handleFilterChange(rangeKeys.end, range.to ? endOfDay(range.to) : endOfDay(range.from));
|
|
@@ -101,16 +64,13 @@ const FilterComponent = ({ filters, selectedFilters, showApplyFilterButton = tru
|
|
|
101
64
|
return (_jsx(DatePicker, { value: value || null, onChange: (date) => handleFilterChange(name, date), placeholder: filter.placeholder ?? `Select ${name}` }));
|
|
102
65
|
}
|
|
103
66
|
if (type === 'multi-select') {
|
|
104
|
-
return (_jsx(MultiSelect, {
|
|
105
|
-
...sharedStyles,
|
|
106
|
-
}, ...commonProps, onKeyDown: (e) => handleFilterKeyDown(e, name), "aria-label": filter.placeholder ?? name }));
|
|
67
|
+
return (_jsx(MultiSelect, { options: options ?? [], placeholder: filter.placeholder ?? `Select ${name}`, value: value || [], onChange: (val) => handleFilterChange(name, val), className: "w-full" }));
|
|
107
68
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}, onKeyDown: (e) => handleFilterKeyDown(e, name), "aria-label": filter.placeholder ?? name }));
|
|
69
|
+
// Default: select
|
|
70
|
+
const currentValue = value || '';
|
|
71
|
+
return (_jsxs(Select, { value: currentValue || undefined, onValueChange: (val) => handleFilterChange(name, val === '__clear__' ? '' : val), children: [_jsx(SelectTrigger, { className: "h-9 w-full border-border bg-background text-sm text-foreground", "aria-label": filter.placeholder ?? name, children: _jsx(SelectValue, { placeholder: filter.placeholder ?? `Select ${name}` }) }), _jsxs(SelectContent, { children: [currentValue && (_jsx(SelectItem, { value: "__clear__", className: "text-muted-foreground", children: filter.placeholder ?? 'All' })), (options ?? []).map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)))] })] }));
|
|
112
72
|
};
|
|
113
73
|
return (_jsx("div", { className: "w-full sm:w-[240px]", children: renderFilterInput() }, name));
|
|
114
|
-
}), showApplyFilterButton && shouldShowActionButtons && (_jsx(Button, { onClick: handleApplyFilters, variant: "primary", className: "w-full sm:w-auto", style: { height:
|
|
74
|
+
}), showApplyFilterButton && shouldShowActionButtons && (_jsx(Button, { onClick: handleApplyFilters, variant: "primary", className: "w-full sm:w-auto", style: { height: 36 }, tabIndex: 0, "aria-label": "Apply Filters", children: "Apply Filters" })), showClearFilters && shouldShowActionButtons && (_jsx(Button, { onClick: resetFilters, variant: "outline-primary", className: "w-full sm:w-auto", style: { height: 36 }, tabIndex: 0, "aria-label": "Clear Filters", children: "Clear Filters" }))] }), children] }));
|
|
115
75
|
};
|
|
116
76
|
export default FilterComponent;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';
|
|
3
3
|
import { HiPlusSm } from 'react-icons/hi';
|
|
4
|
-
import { useMemo, useState } from 'react';
|
|
4
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { FilterComponent, IconButton } from '../../components';
|
|
6
6
|
import { useMediaQueries } from '../../shared';
|
|
7
7
|
import { getDefaultTableOptions } from './Defaults';
|
|
@@ -62,6 +62,32 @@ function CustomToolbar({ filters, appliedFilters, onFilterChange, onApply, onCle
|
|
|
62
62
|
// ─── PagamioTable Component ──────────────────────────────────────────
|
|
63
63
|
const PagamioTable = ({ columns, data, isLoading = false, rowCount, sorting, pagination, filtering, search, onRowClick, rowClassName, expandable = false, renderDetailPanel, toolbar, toolbarMode = 'custom', enableColumnResizing, enableColumnPinning, enableColumnOrdering, enableColumnFilters, enableHiding, enableRowSelection, enableRowActions, enableRowVirtualization, enableGrouping, enableEditing, enableDensityToggle, enableFullScreenToggle, enableClickToCopy, enableRowNumbers, enableMultiSort, enableStickyHeader, enableStickyFooter, editDisplayMode, onEditingRowSave, onEditingRowCancel, renderRowActions, renderRowActionMenuItems, positionActionsColumn, renderTopToolbarCustomActions, renderBottomToolbarCustomActions, layoutMode, defaultColumn, mantineTableOptions, }) => {
|
|
64
64
|
const [expanded, setExpanded] = useState({});
|
|
65
|
+
const tableRef = useRef(null);
|
|
66
|
+
const applyTableHeight = (el) => {
|
|
67
|
+
const mrtContainer = el.querySelector('.mrt-table-container');
|
|
68
|
+
if (!mrtContainer)
|
|
69
|
+
return;
|
|
70
|
+
const containerTop = mrtContainer.getBoundingClientRect().top;
|
|
71
|
+
const offset = Math.round(containerTop + 68); // 52 MRT pagination + 16 gap
|
|
72
|
+
mrtContainer.style.setProperty('max-height', `calc(100vh - ${offset}px)`, 'important');
|
|
73
|
+
};
|
|
74
|
+
// Runs after every render (no deps), always after MRT's own layout effect.
|
|
75
|
+
useLayoutEffect(() => {
|
|
76
|
+
if (tableRef.current)
|
|
77
|
+
applyTableHeight(tableRef.current);
|
|
78
|
+
});
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const el = tableRef.current;
|
|
81
|
+
if (!el)
|
|
82
|
+
return;
|
|
83
|
+
const resizeObserver = new ResizeObserver(() => applyTableHeight(el));
|
|
84
|
+
resizeObserver.observe(document.documentElement);
|
|
85
|
+
window.addEventListener('resize', () => applyTableHeight(el));
|
|
86
|
+
return () => {
|
|
87
|
+
resizeObserver.disconnect();
|
|
88
|
+
window.removeEventListener('resize', () => applyTableHeight(el));
|
|
89
|
+
};
|
|
90
|
+
}, []);
|
|
65
91
|
// Process columns (handle showHeader: false)
|
|
66
92
|
const processedColumns = useMemo(() => processColumns(columns), [columns]);
|
|
67
93
|
// Convert our SortConfig to MRT's SortingState
|
|
@@ -168,6 +194,12 @@ const PagamioTable = ({ columns, data, isLoading = false, rowCount, sorting, pag
|
|
|
168
194
|
// Layout
|
|
169
195
|
...(layoutMode && { layoutMode }),
|
|
170
196
|
...(defaultColumn && { defaultColumn }),
|
|
197
|
+
mantineTableContainerProps: {
|
|
198
|
+
style: {
|
|
199
|
+
border: 'transparent',
|
|
200
|
+
borderRadius: '4px',
|
|
201
|
+
},
|
|
202
|
+
},
|
|
171
203
|
// Row interaction styling
|
|
172
204
|
mantineTableBodyRowProps: ({ row }) => ({
|
|
173
205
|
onClick: onRowClick ? (event) => handleRowClick(row, event) : undefined,
|
|
@@ -179,6 +211,6 @@ const PagamioTable = ({ columns, data, isLoading = false, rowCount, sorting, pag
|
|
|
179
211
|
});
|
|
180
212
|
// ── Toolbar rendering ──────────────────────────────────────────────
|
|
181
213
|
const showCustomToolbar = toolbarMode === 'custom' && (!!toolbar || !!search || !!filtering);
|
|
182
|
-
return (_jsxs("div", { children: [showCustomToolbar && (_jsx(CustomToolbar, { filters: toolbar?.filters ?? [], appliedFilters: filtering?.appliedFilters ?? {}, onFilterChange: filtering?.onFilterChange ?? (() => { }), onApply: filtering?.onApply ?? (() => { }), onClearFilters: toolbar?.onClearFilters, showClearFilters: toolbar?.showClearFilters ?? false, showApplyFilterButton: toolbar?.showApplyFilterButton ?? true, searchEnabled: !!search, searchQuery: search?.query ?? '', onSearch: search?.onChange ?? (() => { }), searchPlaceholder: search?.placeholder, exportConfig: toolbar?.export, columns: columns, data: data, addButton: toolbar?.addButton, addText: toolbar?.addText, onAdd: toolbar?.onAdd })), _jsx("div", { className: "border border-border rounded-md overflow-hidden", children: _jsx(MantineReactTable, { table: table }) })] }));
|
|
214
|
+
return (_jsxs("div", { children: [showCustomToolbar && (_jsx(CustomToolbar, { filters: toolbar?.filters ?? [], appliedFilters: filtering?.appliedFilters ?? {}, onFilterChange: filtering?.onFilterChange ?? (() => { }), onApply: filtering?.onApply ?? (() => { }), onClearFilters: toolbar?.onClearFilters, showClearFilters: toolbar?.showClearFilters ?? false, showApplyFilterButton: toolbar?.showApplyFilterButton ?? true, searchEnabled: !!search, searchQuery: search?.query ?? '', onSearch: search?.onChange ?? (() => { }), searchPlaceholder: search?.placeholder, exportConfig: toolbar?.export, columns: columns, data: data, addButton: toolbar?.addButton, addText: toolbar?.addText, onAdd: toolbar?.onAdd })), _jsx("div", { ref: tableRef, className: "border border-border rounded-md overflow-hidden", children: _jsx(MantineReactTable, { table: table }) })] }));
|
|
183
215
|
};
|
|
184
216
|
export default PagamioTable;
|
package/lib/styles.css
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagamio/frontend-commons-lib",
|
|
3
3
|
"description": "Pagamio library for Frontend reusable components like the form engine and table container",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.312",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"provenance": false
|