UNPKG

@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
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;