tp-react-elements 1.3.2 → 1.3.4

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.
Files changed (48) hide show
  1. package/dist/_virtual/index.esm10.js +2 -2
  2. package/dist/_virtual/index.esm4.js +2 -2
  3. package/dist/_virtual/index.esm5.js +2 -2
  4. package/dist/_virtual/index.esm7.js +2 -2
  5. package/dist/_virtual/index.esm9.js +2 -2
  6. package/dist/components/DateRangePicker/DateRangePicker.d.ts +0 -3
  7. package/dist/components/DateRangePicker/DateRangePicker.esm.js +4 -0
  8. package/dist/components/Form/FormRender.esm.js +12 -8
  9. package/dist/components/FormComponents/Select/SearchableSelect.d.ts +6 -0
  10. package/dist/components/FormComponents/Select/SearchableSelect.esm.js +78 -0
  11. package/dist/components/FormComponents/index.d.ts +1 -0
  12. package/dist/components/FormComponents/index.esm.js +13 -26
  13. package/dist/components/PaginatedGrid/DynamicFilterBar.d.ts +33 -7
  14. package/dist/components/PaginatedGrid/DynamicFilterBar.esm.js +101 -40
  15. package/dist/components/PaginatedGrid/GenericPaginatedGrid.d.ts +10 -3
  16. package/dist/components/PaginatedGrid/GenericPaginatedGrid.esm.js +49 -5
  17. package/dist/components/PaginatedGrid/columns.d.ts +2 -2
  18. package/dist/components/PaginatedGrid/types.d.ts +61 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.esm.css +1 -1
  21. package/dist/index.esm.js +1 -0
  22. package/dist/node_modules/prop-types/index.esm.js +1 -1
  23. package/dist/node_modules/react-date-range/dist/accessibility/index.esm.js +1 -1
  24. package/dist/node_modules/react-date-range/dist/components/Calendar/index.esm.js +1 -1
  25. package/dist/node_modules/react-date-range/dist/components/DateRange/index.esm.js +1 -1
  26. package/dist/node_modules/react-date-range/dist/components/DefinedRange/index.esm.js +1 -1
  27. package/dist/utils/Interface/FormInterface.d.ts +1 -1
  28. package/dist/utils/rdrStyles.d.ts +12 -0
  29. package/dist/utils/rdrStyles.esm.js +180 -0
  30. package/package.json +5 -1
  31. package/dist/node_modules/@tanstack/query-core/build/modern/environmentManager.esm.js +0 -22
  32. package/dist/node_modules/@tanstack/query-core/build/modern/focusManager.esm.js +0 -65
  33. package/dist/node_modules/@tanstack/query-core/build/modern/notifyManager.esm.js +0 -83
  34. package/dist/node_modules/@tanstack/query-core/build/modern/onlineManager.esm.js +0 -55
  35. package/dist/node_modules/@tanstack/query-core/build/modern/query.esm.js +0 -16
  36. package/dist/node_modules/@tanstack/query-core/build/modern/queryObserver.esm.js +0 -463
  37. package/dist/node_modules/@tanstack/query-core/build/modern/retryer.esm.js +0 -8
  38. package/dist/node_modules/@tanstack/query-core/build/modern/subscribable.esm.js +0 -24
  39. package/dist/node_modules/@tanstack/query-core/build/modern/thenable.esm.js +0 -34
  40. package/dist/node_modules/@tanstack/query-core/build/modern/timeoutManager.esm.js +0 -66
  41. package/dist/node_modules/@tanstack/query-core/build/modern/utils.esm.js +0 -115
  42. package/dist/node_modules/@tanstack/react-query/build/modern/IsRestoringProvider.esm.js +0 -7
  43. package/dist/node_modules/@tanstack/react-query/build/modern/QueryClientProvider.esm.js +0 -15
  44. package/dist/node_modules/@tanstack/react-query/build/modern/QueryErrorResetBoundary.esm.js +0 -21
  45. package/dist/node_modules/@tanstack/react-query/build/modern/errorBoundaryUtils.esm.js +0 -27
  46. package/dist/node_modules/@tanstack/react-query/build/modern/suspense.esm.js +0 -22
  47. package/dist/node_modules/@tanstack/react-query/build/modern/useBaseQuery.esm.js +0 -94
  48. package/dist/node_modules/@tanstack/react-query/build/modern/useQuery.esm.js +0 -8
@@ -1,3 +1,3 @@
1
- var propTypes = {exports: {}};
1
+ var accessibility = {};
2
2
 
3
- export { propTypes as __module };
3
+ export { accessibility as __exports };
@@ -1,3 +1,3 @@
1
- var DateRange = {};
1
+ var DefinedRange = {};
2
2
 
3
- export { DateRange as __exports };
3
+ export { DefinedRange as __exports };
@@ -1,3 +1,3 @@
1
- var Calendar = {};
1
+ var DateRange = {};
2
2
 
3
- export { Calendar as __exports };
3
+ export { DateRange as __exports };
@@ -1,3 +1,3 @@
1
- var DefinedRange = {};
1
+ var Calendar = {};
2
2
 
3
- export { DefinedRange as __exports };
3
+ export { Calendar as __exports };
@@ -1,3 +1,3 @@
1
- var accessibility = {};
1
+ var propTypes = {exports: {}};
2
2
 
3
- export { accessibility as __exports };
3
+ export { propTypes as __module };
@@ -1,7 +1,4 @@
1
- import 'react-date-range/dist/styles.css';
2
- import 'react-date-range/dist/theme/default.css';
3
1
  import { FormRenderProps, VariantProps } from '../../utils';
4
- import './DateRangePicker.scss';
5
2
  declare const DateRangePickerComponent: ({ item, control, watch, setValue, getValues, clearErrors, errors, register, variant, ...rest }: FormRenderProps & {
6
3
  variant: VariantProps;
7
4
  }) => import("react/jsx-runtime").JSX.Element;
@@ -2,6 +2,7 @@ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { Controller } from 'react-hook-form';
4
4
  import { d as distExports } from '../../_virtual/index.esm.js';
5
+ import { useRdrStyles } from '../../utils/rdrStyles.esm.js';
5
6
  import dayjs from 'dayjs';
6
7
  import { renderLabel } from '../../utils/Constants/FormConstants.esm.js';
7
8
  import FormBottomField from '../FormComponents/FormBottomField/FormBottomField.esm.js';
@@ -11,6 +12,9 @@ import { parse } from '../../node_modules/date-fns/parse.esm.js';
11
12
  import { isValid } from '../../node_modules/date-fns/isValid.esm.js';
12
13
 
13
14
  const DateRangePickerComponent = ({ item, control, watch, setValue, getValues, clearErrors, errors, register, variant, ...rest }) => {
15
+ // Inject react-date-range CSS via JS (not CSS import) to prevent it from being
16
+ // bundled into the library's extracted CSS file and leaking into consumer apps.
17
+ useRdrStyles();
14
18
  // Using a generic type for anchorEl to avoid environment-specific linting issues.
15
19
  // eslint-disable-next-line react/prop-types
16
20
  const [anchorEl, setAnchorEl] = useState(null);
@@ -1,22 +1,23 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import React__default, { lazy, Suspense } from 'react';
3
- import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
4
- import FormCheckBoxGroup from '../FormComponents/FormCheckBoxGroup/FormCheckBoxGroup.esm.js';
5
- import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.esm.js';
6
- import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
7
- import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
8
- import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
9
- import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
10
- import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
11
3
  import SingleSelect from '../FormComponents/Select/SingleSelect.esm.js';
12
4
  import MultiSelectAutocomplete from '../FormComponents/Select/MultiSelectAutocomplete.esm.js';
13
5
  import SingleSelectNonAutoComplete from '../FormComponents/Select/SingleSelectNonAutoComplete.esm.js';
6
+ import SearchableSelect from '../FormComponents/Select/SearchableSelect.esm.js';
14
7
  import FormActiveSwitch from './FormActiveSwitch.esm.js';
15
8
  import FormTextFieldWithSelect from '../FormComponents/FormTextFieldWithSelect/FormTextFieldWithSelect.esm.js';
16
9
  import DateRangePickerComponent from '../DateRangePicker/DateRangePicker.esm.js';
17
10
  import RichTextEditorWrapper from '../FormComponents/RichTextEditor/RichTextEditorWrapper.esm.js';
18
11
  import SingleSelectSearchApi from '../FormComponents/Select/SingleSelectSearchApi.esm.js';
19
12
  import DatetimepickerWrapper from '../FormComponents/DatePicker/DatetimepickerWrapper.esm.js';
13
+ import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
14
+ import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
15
+ import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
16
+ import FormCheckBoxGroup from '../FormComponents/FormCheckBoxGroup/FormCheckBoxGroup.esm.js';
17
+ import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
18
+ import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.esm.js';
19
+ import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
20
+ import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
20
21
 
21
22
  // Lazy-loaded heavy components
22
23
  // const RichTextEditorWrapper = lazy(() =>
@@ -92,6 +93,9 @@ const RenderForm = (props) => {
92
93
  case 'select-v2':
93
94
  case 'basic-select': // new alias
94
95
  return jsx(SingleSelectNonAutoComplete, { props: props, variant: variant });
96
+ // Searchable select — read-only TextField trigger + search-filtered Popover
97
+ case 'searchable-select':
98
+ return jsx(SearchableSelect, { props: props, variant: variant });
95
99
  case 'input-select':
96
100
  return jsx(FormTextFieldWithSelect, { props: props, variant: variant });
97
101
  // Numeric inputs (integer, pincode, phone)
@@ -0,0 +1,6 @@
1
+ import { FormRenderProps, VariantProps } from '../../../utils';
2
+ declare const SearchableSelect: ({ props, variant, }: {
3
+ props: FormRenderProps;
4
+ variant: VariantProps;
5
+ }) => import("react/jsx-runtime").JSX.Element;
6
+ export default SearchableSelect;
@@ -0,0 +1,78 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { FormControl, Box, TextField, InputAdornment, Popover, List, Typography, ListItemButton, ListItemText } from '@mui/material';
4
+ import SearchIcon from '@mui/icons-material/Search';
5
+ import CheckIcon from '@mui/icons-material/Check';
6
+ import { useWatch } from 'react-hook-form';
7
+ import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
8
+ import 'dayjs';
9
+ import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
10
+
11
+ const SearchableSelect = ({ props, variant, }) => {
12
+ const [anchorEl, setAnchorEl] = useState(null);
13
+ const [search, setSearch] = useState('');
14
+ const value = useWatch({ control: props.control, name: props.item.name });
15
+ const isError = !!props.errors?.[props.item.name];
16
+ const options = props.item.options ?? [];
17
+ // Find the label that corresponds to the currently selected value
18
+ const selectedOption = options.find((o) => String(o.value) === String(value ?? ''));
19
+ const displayValue = selectedOption ? String(selectedOption.label) : '';
20
+ const handleOpen = (e) => {
21
+ if (props.item.disable)
22
+ return;
23
+ setSearch('');
24
+ setAnchorEl(e.currentTarget);
25
+ };
26
+ const handleClose = () => setAnchorEl(null);
27
+ const handleSelect = (val) => {
28
+ props.setValue(props.item.name, val);
29
+ props.item.onChangeFn && props.item.onChangeFn(val);
30
+ props.clearErrors && props.clearErrors(props.item.name);
31
+ handleClose();
32
+ };
33
+ // Case-insensitive substring filter across option labels
34
+ const filtered = search
35
+ ? options.filter((o) => String(o.label).toLowerCase().includes(search.toLowerCase()))
36
+ : options;
37
+ // Label shown inside the MUI TextField (suppressed in 'standard' variant)
38
+ const muiLabel = variant !== 'standard'
39
+ ? `${props.item.label}${props.item.required ? ' *' : ''}`
40
+ : '';
41
+ return (jsxs(FormControl, { fullWidth: true, sx: { position: 'relative' }, disabled: props.item.disable, children: [renderLabel(variant, props), jsx(Box, { onClick: handleOpen, sx: { cursor: props.item.disable ? 'default' : 'pointer' }, children: jsx(TextField, { fullWidth: true, value: displayValue, label: muiLabel, placeholder: props.item.placeholder || 'Select…', error: isError, size: "small", InputProps: {
42
+ readOnly: true,
43
+ endAdornment: (jsx(InputAdornment, { position: "end", children: jsx(SearchIcon, { sx: { fontSize: 16, color: 'text.disabled' } }) })),
44
+ sx: { cursor: 'inherit' },
45
+ }, inputProps: {
46
+ 'aria-labelledby': `${props.item.name}-label`,
47
+ 'aria-describedby': `${props.item.name}-helper ${props.item.name}-error`,
48
+ 'aria-required': props.item.required ? true : undefined,
49
+ style: { cursor: 'inherit' },
50
+ }, sx: {
51
+ '& .MuiFormLabel-root': { top: -10 },
52
+ '& .MuiInputLabel-shrink': { top: 0 },
53
+ } }) }), jsxs(Popover, { open: Boolean(anchorEl), anchorEl: anchorEl, onClose: handleClose, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, PaperProps: {
54
+ sx: {
55
+ mt: 0.5,
56
+ borderRadius: 2,
57
+ boxShadow: 6,
58
+ // Match the width of the trigger field
59
+ width: anchorEl?.offsetWidth ?? 280,
60
+ display: 'flex',
61
+ flexDirection: 'column',
62
+ },
63
+ }, children: [jsx(Box, { sx: { px: 1.5, pt: 1.5, pb: 0.75 }, children: jsx(TextField, { size: "small", fullWidth: true, autoFocus: true, placeholder: "Search\u2026", value: search, onChange: (e) => setSearch(e.target.value), onKeyDown: (e) => {
64
+ if (e.key === 'Escape')
65
+ handleClose();
66
+ }, InputProps: {
67
+ startAdornment: (jsx(InputAdornment, { position: "start", children: jsx(SearchIcon, { sx: { fontSize: 16, color: 'text.disabled' } }) })),
68
+ }, sx: { '& .MuiOutlinedInput-root': { borderRadius: 2, fontSize: 13 } } }) }), jsx(List, { dense: true, disablePadding: true, sx: { maxHeight: 260, overflowY: 'auto', pb: 0.75 }, children: filtered.length === 0 ? (jsx(Box, { sx: { px: 2, py: 1.5 }, children: jsxs(Typography, { variant: "caption", color: "text.disabled", children: ["No options match \"", search, "\""] }) })) : (filtered.map((opt) => {
69
+ const isSelected = String(value ?? '') === String(opt.value);
70
+ return (jsxs(ListItemButton, { onClick: () => handleSelect(opt.value), selected: isSelected, disabled: opt.disable, sx: { px: 2, py: 0.5 }, children: [jsx(ListItemText, { primary: String(opt.label), primaryTypographyProps: {
71
+ fontSize: 13,
72
+ color: isSelected ? 'primary.main' : 'text.primary',
73
+ fontWeight: isSelected ? 600 : 400,
74
+ } }), isSelected && (jsx(CheckIcon, { sx: { fontSize: 15, color: 'primary.main', flexShrink: 0 } }))] }, String(opt.value)));
75
+ })) })] }), jsx(FormBottomField, { ...props })] }, props.item.name));
76
+ };
77
+
78
+ export { SearchableSelect as default };
@@ -8,6 +8,7 @@ export { default as SingleSelectNonAutoComplete } from './Select/SingleSelectNon
8
8
  export { default as SingleSelectSearchApi } from './Select/SingleSelectSearchApi';
9
9
  export { default as MultiSelectV1 } from './Select/MultiSelectv1';
10
10
  export { default as MultiSelectTable } from './Select/MultiSelectTable';
11
+ export { default as SearchableSelect } from './Select/SearchableSelect';
11
12
  export { FormCheckBoxGroup } from '../CheckBoxGroup';
12
13
  export { FormRadioGroup } from '../RadioGroup';
13
14
  export { DatepickerWrapperV2, Monthpickerrender, YearPickerField, } from '../DatePicker';
@@ -1,38 +1,25 @@
1
- export { default as FormNumberField } from './FormNumberField/FormNumberField.esm.js';
2
- export { default as FormNumberFieldDecimal } from './FormNumberField/FormNumberFieldDecimal.esm.js';
3
- export { default as FormTextAreaField } from './FormTextAreaField/FormTextAreaField.esm.js';
4
- export { default as FormTextField } from './FormTextField/FormTextField.esm.js';
5
- export { default as PasswordField } from './PasswordField/PasswordField.esm.js';
6
- export { default as FormCheckBox } from './FormCheckBox/FormCheckBox.esm.js';
7
1
  export { default as SingleSelect } from './Select/SingleSelect.esm.js';
8
2
  export { default as SingleSelectNonAutoComplete } from './Select/SingleSelectNonAutoComplete.esm.js';
9
3
  export { default as SingleSelectSearchApi } from './Select/SingleSelectSearchApi.esm.js';
10
4
  export { default as MultiSelectV1 } from './Select/MultiSelectv1.esm.js';
11
5
  export { default as MultiSelectTable } from './Select/MultiSelectTable.esm.js';
6
+ export { default as SearchableSelect } from './Select/SearchableSelect.esm.js';
7
+ export { default as DatepickerWrapperV2 } from './DatePicker/DatepickerWrapperV2.esm.js';
8
+ export { default as FormBottomField } from './FormBottomField/FormBottomField.esm.js';
9
+ export { default as FormCheckBox } from './FormCheckBox/FormCheckBox.esm.js';
12
10
  export { default as FormCheckBoxGroup } from './FormCheckBoxGroup/FormCheckBoxGroup.esm.js';
11
+ export { default as FormErrorField } from './FormErrorField/FormErrorField.esm.js';
12
+ export { default as FormNumberField } from './FormNumberField/FormNumberField.esm.js';
13
+ export { default as FormNumberFieldDecimal } from './FormNumberField/FormNumberFieldDecimal.esm.js';
13
14
  export { default as FormRadioGroup } from './FormRadioGroup/FormRadioGroup.esm.js';
14
- export { default as DatepickerWrapperV2 } from './DatePicker/DatepickerWrapperV2.esm.js';
15
- export { default as Monthpickerrender } from './DatePicker/Monthpickerrender.esm.js';
16
- export { default as YearPickerField } from './YearPickerField/YearPickerField.esm.js';
17
- export { default as TimePicker } from './TimePicker/TimePicker.esm.js';
18
15
  export { default as FormRenderFileUpload } from './FileUpload/FormRenderFileUpload.esm.js';
19
16
  export { default as FormRenderMultiFileUpload } from './FileUpload/FormRenderMultiFileUpload.esm.js';
20
- import 'react/jsx-runtime';
21
- import '@mui/material';
22
- import 'react';
23
- import 'react-hook-form';
24
- import 'dayjs';
25
- export { default as FormBottomField } from './FormBottomField/FormBottomField.esm.js';
26
- import '@mui/icons-material/Visibility';
27
- import '@mui/icons-material/Close';
28
- import '@mui/icons-material/NavigateBefore';
29
- import '@mui/icons-material/NavigateNext';
30
- import '@mui/icons-material/Delete';
31
- import '@mui/icons-material/UploadFile';
32
- import '@mui/icons-material/PictureAsPdf';
33
- import '@mui/icons-material/TableChart';
34
- import '@mui/icons-material/Description';
35
- export { default as FormErrorField } from './FormErrorField/FormErrorField.esm.js';
17
+ export { default as FormTextAreaField } from './FormTextAreaField/FormTextAreaField.esm.js';
18
+ export { default as FormTextField } from './FormTextField/FormTextField.esm.js';
36
19
  export { default as HelperText } from './HelperText/HelperText.esm.js';
20
+ export { default as Monthpickerrender } from './DatePicker/Monthpickerrender.esm.js';
21
+ export { default as PasswordField } from './PasswordField/PasswordField.esm.js';
37
22
  export { default as RichTextEditor } from './RichTextEditor/RichTextEditor.esm.js';
38
23
  export { default as RichTextEditorWrapper } from './RichTextEditor/RichTextEditorWrapper.esm.js';
24
+ export { default as TimePicker } from './TimePicker/TimePicker.esm.js';
25
+ export { default as YearPickerField } from './YearPickerField/YearPickerField.esm.js';
@@ -1,14 +1,15 @@
1
- import 'react-date-range/dist/styles.css';
2
- import 'react-date-range/dist/theme/default.css';
3
1
  import type { MRT_ColumnFiltersState } from 'material-react-table';
4
2
  /**
5
3
  * Describes one server-side filter chip.
6
4
  *
7
- * - `dateRange` — two-date popover. `fromKey` / `toKey` map to `filterValues`.
8
- * - `text` — single text-input popover. `filterKey` maps to `filterValues`.
9
- * - `dropdown` — single-select popover with a fixed list of `options`.
10
- * `filterKey` maps to `filterValues`.
11
- * - `date` — single date-picker chip. Applied client-side via MRT `columnFilters`.
5
+ * - `dateRange` — two-date popover. `fromKey` / `toKey` map to `filterValues`.
6
+ * - `text` — single text-input popover. `filterKey` maps to `filterValues`.
7
+ * - `dropdown` — single-select popover with a fixed list of `options`.
8
+ * `filterKey` maps to `filterValues`.
9
+ * - `searchableDropdown` — like `dropdown` but the popover has a search box so users can
10
+ * quickly filter long option lists. Same `filterKey` / `options`
11
+ * shape as `dropdown`.
12
+ * - `date` — single date-picker chip. Applied client-side via MRT `columnFilters`.
12
13
  */
13
14
  export type ServerFilterDef = {
14
15
  type: 'dateRange';
@@ -31,6 +32,20 @@ export type ServerFilterDef = {
31
32
  label: string;
32
33
  value: string;
33
34
  }[];
35
+ } | {
36
+ /**
37
+ * Like `dropdown` but adds a search input at the top of the popover so users
38
+ * can filter through long option lists by typing.
39
+ */
40
+ type: 'searchableDropdown';
41
+ id: string;
42
+ label: string;
43
+ filterKey: string;
44
+ options: {
45
+ label: string;
46
+ value: string;
47
+ }[];
48
+ placeholder?: string;
34
49
  } | {
35
50
  /**
36
51
  * Single-date picker chip — applied client-side against the MRT column
@@ -44,6 +59,17 @@ export type ServerFilterDef = {
44
59
  interface AvailableColumn {
45
60
  id: string;
46
61
  label: string;
62
+ /**
63
+ * Filter chip variant used in the "Column Filters" section of Manage Filters.
64
+ * Sourced from the `filterType` prop on the column's `TPColumnDef`.
65
+ * Defaults to `'text'` when omitted.
66
+ */
67
+ filterType?: 'text' | 'date' | 'select' | 'searchable-select' | 'number';
68
+ /** Options for `'select'` / `'searchable-select'` chip variants. */
69
+ filterOptions?: {
70
+ label: string;
71
+ value: string;
72
+ }[];
47
73
  }
48
74
  export interface DynamicFilterBarProps {
49
75
  serverFilterDefs: ServerFilterDef[];
@@ -4,25 +4,39 @@ import { useForm } from 'react-hook-form';
4
4
  import dayjs from 'dayjs';
5
5
  import customParseFormat from 'dayjs/plugin/customParseFormat';
6
6
  import { d as distExports } from '../../_virtual/index.esm.js';
7
- import DatepickerWrapperV2 from '../FormComponents/DatePicker/DatepickerWrapperV2.esm.js';
7
+ import { useRdrStyles } from '../../utils/rdrStyles.esm.js';
8
+ import { DatePicker } from '@mui/x-date-pickers/DatePicker';
9
+ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
10
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
8
11
  import SingleSelectNonAutoComplete from '../FormComponents/Select/SingleSelectNonAutoComplete.esm.js';
9
- import { Box, Typography, Button, Popover, TextField, Stack, Tooltip, List, ListItemButton, ListItemText, Divider } from '@mui/material';
12
+ import { Box, Typography, Button, Popover, TextField, Stack, InputAdornment, List, ListItemButton, ListItemText, Tooltip, Divider } from '@mui/material';
10
13
  import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
11
14
  import TuneIcon from '@mui/icons-material/Tune';
12
15
  import CloseIcon from '@mui/icons-material/Close';
13
16
  import FilterListIcon from '@mui/icons-material/FilterList';
14
17
  import CheckIcon from '@mui/icons-material/Check';
15
18
  import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
19
+ import SearchIcon from '@mui/icons-material/Search';
16
20
 
17
21
  // Enable strict-format parsing so dayjs can handle the DD/MM/YYYY strings
18
22
  // that DatepickerWrapperV2 passes to onChangeFn
19
23
  dayjs.extend(customParseFormat);
20
24
  // ─── Type pill metadata ───────────────────────────────────────────────────────
25
+ // Server-side filter pills (used in SERVER FILTERS section of Manage Filters)
21
26
  const TYPE_PILL = {
22
27
  dateRange: { label: 'Range', color: '#1565c0', bg: '#e3f2fd' },
23
28
  date: { label: 'Date', color: '#00695c', bg: '#e0f2f1' },
24
29
  text: { label: 'Text', color: '#2e7d32', bg: '#e8f5e9' },
25
30
  dropdown: { label: 'Select', color: '#6a1b9a', bg: '#f3e5f5' },
31
+ searchableDropdown: { label: 'Search', color: '#bf360c', bg: '#fbe9e7' },
32
+ };
33
+ // Column filter type pills (used in COLUMN FILTERS section of Manage Filters)
34
+ const COLUMN_TYPE_PILL = {
35
+ text: { label: 'Text', color: '#2e7d32', bg: '#e8f5e9' },
36
+ date: { label: 'Date', color: '#00695c', bg: '#e0f2f1' },
37
+ select: { label: 'Select', color: '#bf360c', bg: '#fbe9e7' },
38
+ 'searchable-select': { label: 'Search', color: '#bf360c', bg: '#fbe9e7' },
39
+ number: { label: 'Number', color: '#e65100', bg: '#fff3e0' },
26
40
  };
27
41
  // ─── Chip shared style ────────────────────────────────────────────────────────
28
42
  const chipSx = (active) => ({
@@ -49,6 +63,9 @@ const fmtDate = (iso) => {
49
63
  return `${Number(d)} ${months[Number(m) - 1]}${yearSuffix}`;
50
64
  };
51
65
  const DateRangeChip = ({ label, fromDate, toDate, onApply, onRemove }) => {
66
+ // Inject react-date-range CSS via JS (not CSS import) to prevent it from being
67
+ // bundled into the library's extracted CSS file and leaking into consumer apps.
68
+ useRdrStyles();
52
69
  const [anchor, setAnchor] = useState(null);
53
70
  const active = Boolean(fromDate || toDate);
54
71
  const chipLabel = active ? `${fmtDate(fromDate)} – ${fmtDate(toDate)}` : label;
@@ -103,41 +120,35 @@ const TextFilterChip = ({ label, value, onApply, onRemove, placeholder }) => {
103
120
  } }), jsxs(Stack, { direction: "row", spacing: 1, justifyContent: "flex-end", mt: 1.5, children: [active && (jsx(Button, { size: "small", color: "inherit", onClick: () => { onApply(''); setAnchor(null); }, children: "Clear" })), jsx(Button, { size: "small", variant: "contained", disableElevation: true, onClick: () => { onApply(draft); setAnchor(null); }, children: "Apply" })] })] }) })] }));
104
121
  };
105
122
  const DatePickerChip = ({ label, value, onApply, onRemove }) => {
106
- const [anchor, setAnchor] = useState(null);
123
+ const [open, setOpen] = useState(false);
124
+ const [anchorEl, setAnchorEl] = useState(null);
107
125
  const active = Boolean(value);
108
- const { register, control, getValues, clearErrors, setValue, watch } = useForm({
109
- defaultValues: { date: value || null },
110
- });
111
- // Sync form value whenever the external value prop changes (e.g. on Clear All)
112
- useEffect(() => {
113
- setValue('date', value || null);
114
- }, [value, setValue]);
115
- const item = {
116
- name: 'date',
117
- label,
118
- inputType: 'datepicker',
119
- onChangeFn: (val) => {
120
- // DatepickerWrapperV2 calls onChangeFn with one of:
121
- // • a dayjs-like object (has .format()) — use it directly
122
- // • a "DD/MM/YYYY" formatted string — parse with strict mode
123
- // • null / undefined — clear the filter
124
- let iso = '';
125
- if (!val) {
126
- iso = '';
127
- }
128
- else if (typeof val.format === 'function') {
129
- iso = val.format('YYYY-MM-DD');
130
- }
131
- else if (typeof val === 'string' && val.trim()) {
132
- const parsed = dayjs(val.trim(), 'DD/MM/YYYY', true);
133
- iso = parsed.isValid() ? parsed.format('YYYY-MM-DD') : '';
134
- }
135
- onApply(iso);
136
- // Auto-close the popover as soon as a date is picked — no need to click "Done"
137
- setAnchor(null);
138
- },
126
+ // dayjs value expected by MUI X DatePicker (null = no selection)
127
+ const dayjsValue = value ? dayjs(value, 'YYYY-MM-DD') : null;
128
+ const handleChipClick = (e) => {
129
+ setAnchorEl(e.currentTarget);
130
+ setOpen(true);
131
+ };
132
+ const handleChange = (newVal) => {
133
+ if (newVal && dayjs.isDayjs(newVal) && newVal.isValid()) {
134
+ onApply(newVal.format('YYYY-MM-DD'));
135
+ }
136
+ else {
137
+ onApply('');
138
+ }
139
+ setOpen(false);
139
140
  };
140
- return (jsxs(Fragment, { children: [jsx(Button, { variant: "outlined", size: "small", onClick: (e) => setAnchor(e.currentTarget), startIcon: jsx(CalendarTodayIcon, { sx: { fontSize: 13 } }), endIcon: jsx(CloseIcon, { sx: { fontSize: '12px !important' }, onClick: (e) => { e.stopPropagation(); onRemove(); } }), sx: chipSx(active), children: active ? `${label}: ${fmtDate(value)}` : label }), jsxs(Popover, { open: Boolean(anchor), anchorEl: anchor, onClose: () => setAnchor(null), anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, PaperProps: { sx: { mt: 0.75, borderRadius: 2, boxShadow: 6, p: 2, minWidth: 260 } }, children: [jsx(Typography, { variant: "caption", fontWeight: 600, color: "text.secondary", display: "block", mb: 1.5, children: label }), jsx(DatepickerWrapperV2, { props: { item, register, control, errors: {}, getValues, clearErrors, setValue, watch }, variant: "outlined" }), jsxs(Stack, { direction: "row", spacing: 1, justifyContent: "flex-end", mt: 1.5, children: [active && (jsx(Button, { size: "small", color: "inherit", onClick: () => { onApply(''); setValue('date', null); setAnchor(null); }, children: "Clear" })), jsx(Button, { size: "small", variant: "contained", disableElevation: true, onClick: () => setAnchor(null), children: "Done" })] })] })] }));
141
+ return (
142
+ // LocalizationProvider must wrap DatePicker — scoped here so it only
143
+ // loads when the chip is actually used (DatePickerChip is tree-shaken
144
+ // if the 'date' filter type is never included in serverFilterDefs).
145
+ jsxs(LocalizationProvider, { dateAdapter: AdapterDayjs, children: [jsx(Button, { variant: "outlined", size: "small", onClick: handleChipClick, startIcon: jsx(CalendarTodayIcon, { sx: { fontSize: 13 } }), endIcon: jsx(CloseIcon, { sx: { fontSize: '12px !important' }, onClick: (e) => { e.stopPropagation(); onRemove(); } }), sx: chipSx(active), children: active ? `${label}: ${fmtDate(value)}` : label }), jsx(DatePicker, { value: dayjsValue, open: open, onClose: () => setOpen(false), onChange: handleChange, slotProps: {
146
+ textField: { sx: { display: 'none' }, size: 'small' },
147
+ popper: {
148
+ anchorEl,
149
+ placement: 'bottom-start',
150
+ },
151
+ } })] }));
141
152
  };
142
153
  const DropdownFilterChip = ({ label, value, options, onApply, onRemove }) => {
143
154
  const [anchor, setAnchor] = useState(null);
@@ -180,6 +191,41 @@ const DropdownFilterChip = ({ label, value, options, onApply, onRemove }) => {
180
191
  watch,
181
192
  }, variant: "outlined" }) })] }));
182
193
  };
194
+ const SearchableSelectChip = ({ label, value, options, onApply, onRemove, placeholder = 'Search…', }) => {
195
+ const [anchor, setAnchor] = useState(null);
196
+ const [search, setSearch] = useState('');
197
+ const active = Boolean(value);
198
+ const selectedLabel = options.find((o) => o.value === value)?.label ?? '';
199
+ const open = (e) => {
200
+ setSearch(''); // always start with all options visible
201
+ setAnchor(e.currentTarget);
202
+ };
203
+ const select = (val) => {
204
+ onApply(val);
205
+ setAnchor(null);
206
+ };
207
+ // Case-insensitive filter; always show "All" row on top
208
+ const filtered = search
209
+ ? options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))
210
+ : options;
211
+ return (jsxs(Fragment, { children: [jsx(Button, { variant: "outlined", size: "small", onClick: open, startIcon: jsx(SearchIcon, { sx: { fontSize: '14px !important', mr: -0.25 } }), endIcon: jsx(CloseIcon, { sx: { fontSize: '12px !important' }, onClick: (e) => { e.stopPropagation(); onRemove(); } }), sx: chipSx(active), children: active ? `${label}: ${selectedLabel}` : label }), jsxs(Popover, { open: Boolean(anchor), anchorEl: anchor, onClose: () => setAnchor(null), anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, PaperProps: {
212
+ sx: { mt: 0.75, borderRadius: 2, boxShadow: 6, width: 280, display: 'flex', flexDirection: 'column' },
213
+ }, children: [jsx(Box, { sx: { px: 1.5, pt: 1.5, pb: 0.75 }, children: jsx(TextField, { size: "small", fullWidth: true, autoFocus: true, placeholder: placeholder, value: search, onChange: (e) => setSearch(e.target.value), onKeyDown: (e) => { if (e.key === 'Escape')
214
+ setAnchor(null); }, InputProps: {
215
+ startAdornment: (jsx(InputAdornment, { position: "start", children: jsx(SearchIcon, { sx: { fontSize: 16, color: 'text.disabled' } }) })),
216
+ }, sx: { '& .MuiOutlinedInput-root': { borderRadius: 2, fontSize: 13 } } }) }), jsxs(List, { dense: true, disablePadding: true, sx: { maxHeight: 260, overflowY: 'auto', pb: 0.75 }, children: [!search && (jsxs(ListItemButton, { onClick: () => select(''), selected: !active, sx: { px: 2, py: 0.5 }, children: [jsx(ListItemText, { primary: "All", primaryTypographyProps: {
217
+ fontSize: 13,
218
+ color: !active ? 'primary.main' : 'text.secondary',
219
+ fontStyle: 'italic',
220
+ } }), !active && jsx(CheckIcon, { sx: { fontSize: 15, color: 'primary.main', flexShrink: 0 } })] })), filtered.length === 0 ? (jsx(Box, { sx: { px: 2, py: 1.5 }, children: jsxs(Typography, { variant: "caption", color: "text.disabled", children: ["No options match \"", search, "\""] }) })) : (filtered.map((opt) => {
221
+ const isSelected = value === opt.value;
222
+ return (jsxs(ListItemButton, { onClick: () => select(opt.value), selected: isSelected, sx: { px: 2, py: 0.5 }, children: [jsx(ListItemText, { primary: opt.label, primaryTypographyProps: {
223
+ fontSize: 13,
224
+ color: isSelected ? 'primary.main' : 'text.primary',
225
+ fontWeight: isSelected ? 600 : 400,
226
+ } }), isSelected && (jsx(CheckIcon, { sx: { fontSize: 15, color: 'primary.main', flexShrink: 0 } }))] }, opt.value));
227
+ }))] })] })] }));
228
+ };
183
229
  const ManageFiltersButton = ({ serverFilterDefs, visibleIds, availableColumns, onToggle, }) => {
184
230
  const [anchor, setAnchor] = useState(null);
185
231
  return (jsxs(Fragment, { children: [jsx(Tooltip, { title: "Add or remove filter chips", children: jsx(Button, { size: "small", variant: "outlined", startIcon: jsx(TuneIcon, { sx: { fontSize: 14 } }), onClick: (e) => setAnchor(e.currentTarget), sx: {
@@ -206,17 +252,20 @@ const ManageFiltersButton = ({ serverFilterDefs, visibleIds, availableColumns, o
206
252
  } }), selected && jsx(CheckIcon, { sx: { fontSize: 16, color: 'primary.main', flexShrink: 0 } })] }, def.id));
207
253
  }) })] })), availableColumns.length > 0 && (jsxs(Fragment, { children: [jsx(Divider, { sx: { my: 1 } }), jsx(Typography, { variant: "caption", fontWeight: 700, color: "text.disabled", sx: { px: 2, pb: 0.5, display: 'block', textTransform: 'uppercase', letterSpacing: 0.5 }, children: "Column Filters" }), jsx(List, { dense: true, disablePadding: true, sx: { maxHeight: 260, overflowY: 'auto' }, children: availableColumns.map((col) => {
208
254
  const selected = visibleIds.includes(col.id);
255
+ // Pick the type pill based on the column's declared filter type
256
+ const pill = COLUMN_TYPE_PILL[col.filterType ?? 'text'] ??
257
+ COLUMN_TYPE_PILL.text;
209
258
  return (jsxs(ListItemButton, { onClick: () => onToggle(col.id), sx: { px: 2, py: 0.4 }, children: [jsx(Box, { component: "span", sx: {
210
259
  mr: 1.25, flexShrink: 0,
211
260
  fontSize: 10, fontWeight: 700,
212
261
  px: 0.75, py: '2px',
213
262
  borderRadius: '4px',
214
- bgcolor: TYPE_PILL.text.bg,
215
- color: TYPE_PILL.text.color,
263
+ bgcolor: pill.bg,
264
+ color: pill.color,
216
265
  textTransform: 'uppercase',
217
266
  letterSpacing: 0.4,
218
267
  lineHeight: 1.6,
219
- }, children: "Text" }), jsx(ListItemText, { primary: col.label, primaryTypographyProps: {
268
+ }, children: pill.label }), jsx(ListItemText, { primary: col.label, primaryTypographyProps: {
220
269
  fontSize: 13,
221
270
  color: selected ? 'primary.main' : 'text.primary',
222
271
  } }), selected && jsx(CheckIcon, { sx: { fontSize: 16, color: 'primary.main', flexShrink: 0 } })] }, col.id));
@@ -233,7 +282,9 @@ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter,
233
282
  if (def?.type === 'dateRange') {
234
283
  onFilterValuesChange({ ...filterValues, [def.fromKey]: '', [def.toKey]: '' });
235
284
  }
236
- else if (def?.type === 'text' || def?.type === 'dropdown') {
285
+ else if (def?.type === 'text' ||
286
+ def?.type === 'dropdown' ||
287
+ def?.type === 'searchableDropdown') {
237
288
  onFilterValuesChange({ ...filterValues, [def.filterKey]: '' });
238
289
  }
239
290
  else if (def?.type === 'date') {
@@ -255,13 +306,23 @@ const DynamicFilterBar = ({ serverFilterDefs, visibleFilterIds, onToggleFilter,
255
306
  if (def?.type === 'dropdown') {
256
307
  return (jsx(DropdownFilterChip, { label: def.label, value: filterValues[def.filterKey] ?? '', options: def.options, onApply: (v) => onFilterValuesChange({ ...filterValues, [def.filterKey]: v }), onRemove: () => removeFilter(id) }, id));
257
308
  }
309
+ if (def?.type === 'searchableDropdown') {
310
+ 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…' }, id));
311
+ }
258
312
  if (def?.type === 'date') {
259
313
  return (jsx(DatePickerChip, { label: def.label, value: getColValue(def.columnKey), onApply: (v) => applyColFilter(def.columnKey, v), onRemove: () => removeFilter(id) }, id));
260
314
  }
261
- // Column filter chip
315
+ // Column filter chip — render the variant that matches the column's filterType
262
316
  const col = availableColumns.find((c) => c.id === id);
263
317
  if (!col)
264
318
  return null;
319
+ if (col.filterType === 'date') {
320
+ return (jsx(DatePickerChip, { label: col.label, value: getColValue(id), onApply: (v) => applyColFilter(id, v), onRemove: () => removeFilter(id) }, id));
321
+ }
322
+ if (col.filterType === 'select' || col.filterType === 'searchable-select') {
323
+ return (jsx(SearchableSelectChip, { label: col.label, value: getColValue(id), options: col.filterOptions ?? [], onApply: (v) => applyColFilter(id, v), onRemove: () => removeFilter(id) }, id));
324
+ }
325
+ // Default: free-text input
265
326
  return (jsx(TextFilterChip, { label: col.label, value: getColValue(id), onApply: (v) => applyColFilter(id, v), onRemove: () => removeFilter(id), placeholder: `Filter by ${col.label}` }, id));
266
327
  }), jsx(ManageFiltersButton, { serverFilterDefs: serverFilterDefs, visibleIds: visibleFilterIds, availableColumns: availableColumns, onToggle: onToggleFilter })] }));
267
328
  };
@@ -1,14 +1,21 @@
1
- import { type MRT_ColumnDef } from 'material-react-table';
1
+ import type { TPColumnDef } from './types';
2
2
  import { type ServerFilterDef } from './DynamicFilterBar';
3
3
  export type { ServerFilterDef };
4
+ export type { TPColumnDef } from './types';
4
5
  /** Shape that fetchPage must return. */
5
6
  export interface PagedResult<TData> {
6
7
  data: TData[];
7
8
  totalRecords: number;
8
9
  }
9
10
  export interface GenericPaginatedGridProps<TData extends object> {
10
- /** MRT column definitions typed to your data shape. */
11
- columns: MRT_ColumnDef<TData>[];
11
+ /**
12
+ * Column definitions. Use `TPColumnDef<TData>` (a superset of `MRT_ColumnDef`)
13
+ * to get top-level `filterType` and `options` support with no `meta` boilerplate.
14
+ *
15
+ * The grid automatically injects the right `filterFn` for each `filterType`,
16
+ * and wires up the correct chip in Manage Filters → Column Filters.
17
+ */
18
+ columns: TPColumnDef<TData>[];
12
19
  /**
13
20
  * Async function called on every page/filter change.
14
21
  * Receives the current flat filter map and the 0-based page index.