@pagamio/frontend-commons-lib
Version:
Pagamio library for Frontend reusable components like the form engine and table container
129 lines (128 loc) • 9.13 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { Input, MultiSelect, Select } from '@mantine/core';
import { IconCalendar, IconSearch } from '@tabler/icons-react';
import { format } from 'date-fns';
import React from 'react';
import { isDefaultFilterValue } from '../../shared/utils/filterUtils';
import Button from './Button';
import DatePicker from './DatePicker';
import FilterWrapper from './FilterWrapper';
import { Popover, PopoverContent, PopoverTrigger } from './Popover';
import RangeDatePicker from './RangeDatePicker';
const sharedStyles = {
input: {
borderRadius: '6px',
},
label: {
fontSize: '13px',
},
width: '100%',
};
const selectComponentProps = {
input: {
'&[type="search"]': {
borderRadius: '6px',
fontSize: '0.8rem',
height: '39px',
},
},
dropdown: {
'& .mantine-Select-item': {
fontSize: '0.75rem',
},
},
};
const commonProps = {
size: 'md',
style: { width: 240 },
};
const FilterComponent = ({ filters, selectedFilters, showApplyFilterButton = true, showClearFilters, searctInputPlaceHolder = 'Search...', showApplyFilters = true, showSearch = false, searchQuery = '', children, isNarrow, handleFilterChange, handleApplyFilters, resetFilters, onSearch = () => { }, }) => {
const [activeRangePicker, setActiveRangePicker] = React.useState(null);
const hasSelectedActiveFilters = Object.entries(selectedFilters).some(([key, v]) => !isDefaultFilterValue(v, key));
const shouldShowActionButtons = hasSelectedActiveFilters || (showSearch && searchQuery.length > 0);
const parseDateValue = (value) => {
if (!value)
return null;
if (value instanceof Date)
return value;
if (Array.isArray(value))
return null;
const parsed = new Date(value);
return Number.isNaN(parsed.getTime()) ? null : parsed;
};
// Keyboard handler for search input
const handleSearchKeyDown = (e) => {
if (e.key === 'Enter') {
handleApplyFilters();
}
else if (e.key === 'Escape') {
onSearch({ target: { value: '' } });
resetFilters();
}
};
// Keyboard handler for select/multiselect (dropdown) filters
const handleFilterKeyDown = (e, name) => {
if (e.key === 'Enter') {
handleApplyFilters();
}
else if (e.key === 'Escape') {
handleFilterChange(name, '');
resetFilters();
}
};
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, { icon: _jsx(IconSearch, { size: 16 }), placeholder: searctInputPlaceHolder, value: searchQuery, onChange: (event) => onSearch(event), onKeyDown: handleSearchKeyDown, className: "w-full", sx: { input: { height: 39 } }, "aria-label": "Search" }) })), filters.map((filter) => {
const { name, type, options } = filter;
const value = selectedFilters[name];
const renderFilterInput = () => {
if (type === 'date-range') {
const rangeKeys = filter.rangeKeys;
if (!rangeKeys) {
console.warn('Date range filter requires rangeKeys', filter);
return null;
}
const startDateValue = rangeKeys ? selectedFilters[rangeKeys.start] : undefined;
const endDateValue = rangeKeys ? selectedFilters[rangeKeys.end] : undefined;
const startDate = parseDateValue(startDateValue);
const endDate = parseDateValue(endDateValue);
const displayValue = startDate && endDate
? `${format(startDate, 'dd MMM yyyy')} - ${format(endDate, 'dd MMM yyyy')}`
: (filter.placeholder ?? 'Select date range');
const applyRange = (start, end) => {
if (!rangeKeys)
return;
handleFilterChange(rangeKeys.start, start);
handleFilterChange(rangeKeys.end, end);
};
const rangeSelection = {
startDate: startDate ?? new Date(),
endDate: endDate ?? startDate ?? new Date(),
key: name,
};
return (_jsxs(Popover, { open: activeRangePicker === name, onOpenChange: (open) => setActiveRangePicker(open ? name : null), children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "flex w-full items-center justify-between rounded-md border border-gray-300 bg-white px-3 py-2 text-left text-sm text-gray-700 shadow-sm transition hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(IconCalendar, { size: 16, className: "text-gray-500" }), _jsx("span", { className: !startDate || !endDate ? 'text-gray-400' : '', children: displayValue })] }) }) }), _jsx(PopoverContent, { align: "start", className: "w-auto border border-gray-200 bg-white p-0 shadow-2xl", children: _jsxs("div", { className: "p-4", children: [_jsx(RangeDatePicker, { dateRange: rangeSelection, rangeKey: name, onChange: (updatedRange) => {
if (updatedRange.startDate) {
applyRange(updatedRange.startDate, updatedRange.endDate ?? updatedRange.startDate);
}
}, months: 2, showPreview: false }), _jsxs("div", { className: "flex justify-end gap-2 pt-3", children: [_jsx(Button, { variant: "outline-primary", type: "button", onClick: () => {
applyRange(null, null);
setActiveRangePicker(null);
}, className: "h-9 px-3", children: "Clear" }), _jsx(Button, { variant: "primary", type: "button", onClick: () => setActiveRangePicker(null), className: "h-9 px-4", children: "Done" })] })] }) })] }));
}
if (type === 'date') {
return (_jsx(DatePicker, { value: value || null, onChange: (date) => handleFilterChange(name, date), placeholder: filter.placeholder ?? `Select ${name}` }));
}
if (type === 'multi-select') {
return (_jsx(MultiSelect, { data: options || [], placeholder: filter.placeholder ?? `Select ${name}`, value: value || [], onChange: (val) => handleFilterChange(name, val), searchable: true, clearable: true, className: "w-full", styles: { ...sharedStyles }, sx: {
'input[type="search"]': {
fontSize: '0.9rem',
},
}, ...commonProps, onKeyDown: (e) => handleFilterKeyDown(e, name), "aria-label": filter.placeholder ?? name }));
}
return (_jsx(Select, { data: options || [], placeholder: filter.placeholder ?? `Select ${name}`, value: value || null, onChange: (val) => handleFilterChange(name, val), searchable: true, clearable: true, className: "w-full", styles: {
...sharedStyles,
...selectComponentProps,
}, onKeyDown: (e) => handleFilterKeyDown(e, name), "aria-label": filter.placeholder ?? name }));
};
return (_jsx("div", { className: "w-full sm:w-[240px]", children: renderFilterInput() }, name));
}), showApplyFilterButton && shouldShowActionButtons && (_jsx(Button, { onClick: handleApplyFilters, variant: "primary", className: "w-full sm:w-auto", style: { height: 39 }, 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: 39 }, tabIndex: 0, "aria-label": "Clear Filters", children: "Clear Filters" }))] }), children] }));
};
export default FilterComponent;