tp-react-elements 2.0.7 → 3.0.1

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 (43) hide show
  1. package/dist/_virtual/index.esm11.js +2 -2
  2. package/dist/_virtual/index.esm12.js +2 -2
  3. package/dist/_virtual/index.esm13.js +2 -2
  4. package/dist/_virtual/index.esm14.js +2 -2
  5. package/dist/_virtual/index.esm4.js +2 -2
  6. package/dist/_virtual/index.esm5.js +2 -2
  7. package/dist/_virtual/index.esm6.js +2 -2
  8. package/dist/components/Button/Button.esm.js +9 -5
  9. package/dist/components/DatePicker/index.d.ts +1 -0
  10. package/dist/components/DatePicker/index.esm.js +2 -1
  11. package/dist/components/DeleteComponent/DeleteField.esm.js +20 -14
  12. package/dist/components/Form/FormActiveSwitch.esm.js +5 -1
  13. package/dist/components/Form/FormConstants.esm.js +1 -0
  14. package/dist/components/Form/FormRender.esm.js +14 -10
  15. package/dist/components/Form/FormRenderWrapper.esm.js +2 -2
  16. package/dist/components/FormComponents/DatePicker/MonthPickerMultiRender.d.ts +6 -0
  17. package/dist/components/FormComponents/DatePicker/MonthPickerMultiRender.esm.js +117 -0
  18. package/dist/components/FormComponents/DatePicker/Monthpickerrender.esm.js +6 -4
  19. package/dist/components/FormComponents/FileUpload/MultiFileWithPreview.esm.js +38 -6
  20. package/dist/components/FormComponents/FormCheckBoxGroup/FormCheckBoxGroup.esm.js +11 -8
  21. package/dist/components/FormComponents/FormRadioGroup/FormRadioGroup.esm.js +14 -12
  22. package/dist/components/FormComponents/Select/MultiSelectTable.esm.js +176 -160
  23. package/dist/components/FormComponents/Select/MultiSelectv1.esm.js +2 -2
  24. package/dist/components/FormComponents/index.esm.js +16 -22
  25. package/dist/components/ModalField/ConfirmationDialog.esm.js +37 -37
  26. package/dist/components/ModalField/ModalField.esm.js +19 -16
  27. package/dist/components/PaginatedGrid/DynamicFilterBar.d.ts +12 -4
  28. package/dist/components/PaginatedGrid/DynamicFilterBar.esm.js +48 -16
  29. package/dist/components/PaginatedGrid/GenericPaginatedGrid.d.ts +8 -1
  30. package/dist/components/PaginatedGrid/GenericPaginatedGrid.esm.js +80 -20
  31. package/dist/components/PaginatedGrid/types.d.ts +25 -0
  32. package/dist/components/SelectField/MultiSelectFieldComponent.esm.js +19 -21
  33. package/dist/node_modules/react-date-range/dist/components/Calendar/index.esm.js +2 -2
  34. package/dist/node_modules/react-date-range/dist/components/DateInput/index.esm.js +3 -3
  35. package/dist/node_modules/react-date-range/dist/components/DateRange/index.esm.js +3 -3
  36. package/dist/node_modules/react-date-range/dist/components/DateRangePicker/index.esm.js +3 -3
  37. package/dist/node_modules/react-date-range/dist/components/DayCell/index.esm.js +3 -3
  38. package/dist/node_modules/react-date-range/dist/components/DefinedRange/index.esm.js +3 -3
  39. package/dist/node_modules/react-date-range/dist/components/InputRangeField/index.esm.js +3 -3
  40. package/dist/node_modules/react-date-range/dist/components/Month/index.esm.js +3 -3
  41. package/dist/node_modules/react-list/react-list.esm.js +2 -2
  42. package/dist/utils/Interface/FormInterface.d.ts +1 -1
  43. package/package.json +2 -2
@@ -1,3 +1,3 @@
1
- var InputRangeField = {};
1
+ var DayCell = {};
2
2
 
3
- export { InputRangeField as __exports };
3
+ export { DayCell as __exports };
@@ -1,3 +1,3 @@
1
- var DayCell = {};
1
+ var InputRangeField = {};
2
2
 
3
- export { DayCell as __exports };
3
+ export { InputRangeField as __exports };
@@ -1,3 +1,3 @@
1
- var Month = {};
1
+ var DateInput = {};
2
2
 
3
- export { Month as __exports };
3
+ export { DateInput as __exports };
@@ -1,3 +1,3 @@
1
- var DateInput = {};
1
+ var Month = {};
2
2
 
3
- export { DateInput as __exports };
3
+ export { Month as __exports };
@@ -1,3 +1,3 @@
1
- var DateRangePicker = {};
1
+ var DateRange = {};
2
2
 
3
- export { DateRangePicker as __exports };
3
+ export { DateRange as __exports };
@@ -1,3 +1,3 @@
1
- var DefinedRange = {};
1
+ var DateRangePicker = {};
2
2
 
3
- export { DefinedRange as __exports };
3
+ export { DateRangePicker 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,15 +1,19 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { useAuth } from '../../auth/authContext.esm.js';
3
3
  import { Button } from '@mui/material';
4
+ import { useMemo, useCallback } from 'react';
4
5
 
5
6
  const ButtonStyled = ({ name }) => {
6
7
  const { projectTheme } = useAuth();
7
8
  const ButtonConfig = projectTheme?.Styles.Component.TPButton.find((item) => item.name === name);
8
- return (jsx(Button, { sx: { ...projectTheme?.Styles.Component.TPButtonCommonSx }, onClick: () => {
9
- if (ButtonConfig?.isEnableConfirmDialog) {
10
- alert('Congirm');
11
- }
12
- }, children: name }));
9
+ const commonSx = projectTheme?.Styles.Component.TPButtonCommonSx;
10
+ const sx = useMemo(() => ({ ...commonSx }), [commonSx]);
11
+ const handleClick = useCallback(() => {
12
+ if (ButtonConfig?.isEnableConfirmDialog) {
13
+ alert('Congirm');
14
+ }
15
+ }, [ButtonConfig?.isEnableConfirmDialog]);
16
+ return (jsx(Button, { sx: sx, onClick: handleClick, children: name }));
13
17
  };
14
18
 
15
19
  export { ButtonStyled as default };
@@ -1,3 +1,4 @@
1
1
  export { default as DatepickerWrapperV2 } from '../FormComponents/DatePicker/DatepickerWrapperV2';
2
2
  export { default as Monthpickerrender } from '../FormComponents/DatePicker/Monthpickerrender';
3
+ export { default as MonthPickerMultiRender } from '../FormComponents/DatePicker/MonthPickerMultiRender';
3
4
  export { default as YearPickerField } from '../FormComponents/YearPickerField/YearPickerField';
@@ -1,7 +1,8 @@
1
1
  import DatepickerWrapperV2 from '../FormComponents/DatePicker/DatepickerWrapperV2.esm.js';
2
2
  import Monthpickerrender from '../FormComponents/DatePicker/Monthpickerrender.esm.js';
3
+ import MonthPickerMultiRender from '../FormComponents/DatePicker/MonthPickerMultiRender.esm.js';
3
4
  export { default as YearPickerField } from '../FormComponents/YearPickerField/YearPickerField.esm.js';
4
5
 
5
6
 
6
7
 
7
- export { DatepickerWrapperV2, Monthpickerrender };
8
+ export { DatepickerWrapperV2, MonthPickerMultiRender, Monthpickerrender };
@@ -1,24 +1,30 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import { Tooltip, Grid, Typography } from '@mui/material';
3
3
  import ConfirmationDialog from '../ModalField/ConfirmationDialog.esm.js';
4
- import { useState } from 'react';
4
+ import { useState, useCallback } from 'react';
5
5
 
6
+ // ─── Module-level static style objects ─────────────────────────────────────
7
+ const TOOLTIP_TEXT_SX = { fontSize: '8px' };
8
+ const GRID_SX = { ':hover': { cursor: 'pointer' } };
9
+ const TRASH_ICON_STYLE = {
10
+ lineHeight: 1,
11
+ fontSize: '11px',
12
+ color: 'var(--mui-palette-error-main, #B3261E)',
13
+ marginTop: '10px',
14
+ };
6
15
  const DeleteField = ({ onClickFn, tooltip = 'Delete', text }) => {
7
16
  const [openDialog, setOpenDialog] = useState(false);
8
- const handleCancel = () => {
17
+ const handleCancel = useCallback(() => {
9
18
  setOpenDialog(false);
10
- };
11
- return (jsxs(Fragment, { children: [jsx(Tooltip, { title: jsx(Typography, { sx: { fontSize: '8px' }, children: tooltip }), children: jsx(Grid, { sx: { ':hover': { cursor: 'pointer' } }, onClick: () => {
12
- setOpenDialog(true);
13
- }, children: jsx("i", { className: "flaticon-trash", style: {
14
- lineHeight: 1,
15
- fontSize: '11px',
16
- color: 'var(--mui-palette-error-main, #B3261E)',
17
- marginTop: '10px',
18
- } }) }) }), jsx(ConfirmationDialog, { openConfirmDialog: openDialog, handleCancel: handleCancel, onClickSubmit: () => {
19
- handleCancel();
20
- onClickFn();
21
- }, text: text })] }));
19
+ }, []);
20
+ const handleOpen = useCallback(() => {
21
+ setOpenDialog(true);
22
+ }, []);
23
+ const handleSubmit = useCallback(() => {
24
+ handleCancel();
25
+ onClickFn();
26
+ }, [handleCancel, onClickFn]);
27
+ return (jsxs(Fragment, { children: [jsx(Tooltip, { title: jsx(Typography, { sx: TOOLTIP_TEXT_SX, children: tooltip }), children: jsx(Grid, { sx: GRID_SX, onClick: handleOpen, children: jsx("i", { className: "flaticon-trash", style: TRASH_ICON_STYLE }) }) }), jsx(ConfirmationDialog, { openConfirmDialog: openDialog, handleCancel: handleCancel, onClickSubmit: handleSubmit, text: text })] }));
22
28
  };
23
29
 
24
30
  export { DeleteField as default };
@@ -1,11 +1,15 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { Controller } from 'react-hook-form';
3
3
 
4
+ // Static — hoisted so they aren't rebuilt on every render.
5
+ const FIELDSET_STYLE = { border: 'none', padding: 0, margin: 0 };
6
+ const LEGEND_STYLE = { fontSize: '12px', paddingRight: 10 };
7
+ const LABEL_STYLE = { textTransform: 'none' };
4
8
  const FormActiveSwitch = ({ props }) => {
5
9
  const { control, item } = props;
6
10
  const valueActive = item.value1 ?? 'A';
7
11
  const valueInactive = item.value2 ?? 'I';
8
- return (jsxs("fieldset", { className: "m-form__input", style: { border: 'none', padding: 0, margin: 0 }, children: [item.label && (jsx("legend", { id: `${item.name}-label`, style: { fontSize: '12px', paddingRight: 10 }, children: item.label })), jsx(Controller, { name: item.name, control: control, render: ({ field }) => (jsxs("span", { className: "switch prestashop-switch fixed-width-lg", role: "radiogroup", "aria-labelledby": `${item.name}-label`, children: [jsx("input", { id: `${item.name}_on`, name: item.name, type: "radio", value: valueActive, checked: field.value === valueActive, onChange: () => field.onChange(valueActive), "aria-checked": field.value === valueActive }), jsx("label", { htmlFor: `${item.name}_on`, style: { textTransform: 'none' }, children: item.label1 ?? 'Active' }), jsx("input", { id: `${item.name}_off`, name: item.name, type: "radio", value: valueInactive, checked: field.value === valueInactive, onChange: () => field.onChange(valueInactive), "aria-checked": field.value === valueInactive }), jsx("label", { htmlFor: `${item.name}_off`, style: { textTransform: 'none' }, children: item.label2 ?? 'In Active' }), jsx("span", { className: "slide-button btn", "aria-hidden": "true" })] })) })] }));
12
+ return (jsxs("fieldset", { className: "m-form__input", style: FIELDSET_STYLE, children: [item.label && (jsx("legend", { id: `${item.name}-label`, style: LEGEND_STYLE, children: item.label })), jsx(Controller, { name: item.name, control: control, render: ({ field }) => (jsxs("span", { className: "switch prestashop-switch fixed-width-lg", role: "radiogroup", "aria-labelledby": `${item.name}-label`, children: [jsx("input", { id: `${item.name}_on`, name: item.name, type: "radio", value: valueActive, checked: field.value === valueActive, onChange: () => field.onChange(valueActive), "aria-checked": field.value === valueActive }), jsx("label", { htmlFor: `${item.name}_on`, style: LABEL_STYLE, children: item.label1 ?? 'Active' }), jsx("input", { id: `${item.name}_off`, name: item.name, type: "radio", value: valueInactive, checked: field.value === valueInactive, onChange: () => field.onChange(valueInactive), "aria-checked": field.value === valueInactive }), jsx("label", { htmlFor: `${item.name}_off`, style: LABEL_STYLE, children: item.label2 ?? 'In Active' }), jsx("span", { className: "slide-button btn", "aria-hidden": "true" })] })) })] }));
9
13
  };
10
14
 
11
15
  export { FormActiveSwitch as default };
@@ -282,6 +282,7 @@ const useFormValidatingContext = (formArray) => {
282
282
  renderCustomError(field);
283
283
  break;
284
284
  case 'monthpicker':
285
+ case 'monthpicker-multi':
285
286
  initialValues[field.name] = null;
286
287
  if (field.required && field.errorMessage) {
287
288
  validationShape[field.name] = (validationShape[field.name] || Yup.string())
@@ -1,13 +1,6 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
- import React__default, { lazy, Suspense } from 'react';
3
- import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
2
+ import require$$0__default, { lazy, Suspense } from 'react';
4
3
  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
4
  import SingleSelect from '../FormComponents/Select/SingleSelect.esm.js';
12
5
  import MultiSelectAutocomplete from '../FormComponents/Select/MultiSelectAutocomplete.esm.js';
13
6
  import SingleSelectNonAutoComplete from '../FormComponents/Select/SingleSelectNonAutoComplete.esm.js';
@@ -18,6 +11,13 @@ import DateRangePickerComponent from '../DateRangePicker/DateRangePicker.esm.js'
18
11
  import RichTextEditorWrapper from '../FormComponents/RichTextEditor/RichTextEditorWrapper.esm.js';
19
12
  import SingleSelectSearchApi from '../FormComponents/Select/SingleSelectSearchApi.esm.js';
20
13
  import DatetimepickerWrapper from '../FormComponents/DatePicker/DatetimepickerWrapper.esm.js';
14
+ import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
15
+ import FormCheckBox from '../FormComponents/FormCheckBox/FormCheckBox.esm.js';
16
+ import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.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';
21
21
 
22
22
  // Lazy-loaded heavy components
23
23
  // const RichTextEditorWrapper = lazy(() =>
@@ -30,6 +30,7 @@ const FormRenderMultiFileUpload = lazy(() => import('../FileUpload/index.esm.js'
30
30
  const MultiFileWithPreview = lazy(() => import('../FileUpload/index.esm.js').then((m) => ({ default: m.MultiFileWithPreview })));
31
31
  const DatepickerWrapperV2 = lazy(() => import('../DatePicker/index.esm.js').then((m) => ({ default: m.DatepickerWrapperV2 })));
32
32
  const Monthpickerrender = lazy(() => import('../DatePicker/index.esm.js').then((m) => ({ default: m.Monthpickerrender })));
33
+ const MonthPickerMultiRender = lazy(() => import('../DatePicker/index.esm.js').then((m) => ({ default: m.MonthPickerMultiRender })));
33
34
  const TimePickerFieldWrapper = lazy(() => import('../TimePicker/index.esm.js').then((m) => ({ default: m.TimePicker })));
34
35
  // Loading fallback component
35
36
  const LoadingFallback = () => jsx("div", { style: { minHeight: '36px' }, children: "Loading..." });
@@ -119,9 +120,12 @@ const RenderForm = (props) => {
119
120
  // Time picker
120
121
  case 'timepicker':
121
122
  return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(TimePickerFieldWrapper, { props: props, variant: variant }) }));
122
- // Month picker
123
+ // Month picker (single month)
123
124
  case 'monthpicker':
124
125
  return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(Monthpickerrender, { props: props, variant: variant }) }));
126
+ // Month picker (multiple months)
127
+ case 'monthpicker-multi':
128
+ return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(MonthPickerMultiRender, { props: props, variant: variant }) }));
125
129
  // Multi-select dropdown
126
130
  case 'multiselect':
127
131
  return (jsx(Suspense, { fallback: jsx(LoadingFallback, {}), children: jsx(MultiSelectV1, { props: props, variant: variant }) }));
@@ -161,6 +165,6 @@ const RenderForm = (props) => {
161
165
  *
162
166
  * @see FormRenderWrapper
163
167
  */
164
- var RenderForm$1 = React__default.memo(RenderForm);
168
+ var RenderForm$1 = require$$0__default.memo(RenderForm);
165
169
 
166
170
  export { RenderForm$1 as default };
@@ -1,5 +1,5 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
- import React__default from 'react';
2
+ import require$$0__default from 'react';
3
3
  import { useFormState } from 'react-hook-form';
4
4
  import { FormComponent, Formitem } from './Form.styles.esm.js';
5
5
  import RenderForm from './FormRender.esm.js';
@@ -28,6 +28,6 @@ const FormRenderWrapper = ({ formArray, name = '', numberOfColumns = 3, form, va
28
28
  * Export the FormRenderWrapper component for use in form containers
29
29
  * This component is the main entry point for rendering form fields
30
30
  */
31
- var FormRenderWrapper_default = React__default.memo(FormRenderWrapper);
31
+ var FormRenderWrapper_default = require$$0__default.memo(FormRenderWrapper);
32
32
 
33
33
  export { FormRenderWrapper_default as default };
@@ -0,0 +1,6 @@
1
+ import { FormRenderProps, VariantProps } from '../../../utils';
2
+ declare const MonthPickerMultiRender: ({ props, variant, }: {
3
+ props: FormRenderProps;
4
+ variant: VariantProps;
5
+ }) => import("react/jsx-runtime").JSX.Element;
6
+ export default MonthPickerMultiRender;
@@ -0,0 +1,117 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState, useMemo, useCallback } from 'react';
3
+ import { FormControl, Box, TextField, InputAdornment, Popover, IconButton, Typography, Button } from '@mui/material';
4
+ import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
5
+ import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
6
+ import ChevronRightIcon from '@mui/icons-material/ChevronRight';
7
+ import { useWatch } from 'react-hook-form';
8
+ import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
9
+ import 'dayjs';
10
+ import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
11
+
12
+ const MONTH_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
13
+ const parseMY = (s) => {
14
+ const m = /^(\d{2})\/(\d{4})$/.exec(s.trim());
15
+ return m ? [Number(m[1]), Number(m[2])] : null; // [month, year]
16
+ };
17
+ const toKey = (month, year) => year * 100 + month; // sortable comparable int
18
+ const toMY = (month, year) => `${String(month).padStart(2, '0')}/${year}`;
19
+ const parseValue = (value) => value ? value.split(',').map((s) => s.trim()).filter(Boolean) : [];
20
+ const MonthPickerMultiRender = ({ props, variant, }) => {
21
+ const [anchorEl, setAnchorEl] = useState(null);
22
+ const [viewYear, setViewYear] = useState(() => new Date().getFullYear());
23
+ const [draft, setDraft] = useState([]);
24
+ const value = useWatch({ control: props.control, name: props.item.name });
25
+ const committed = useMemo(() => parseValue(value), [value]);
26
+ const isError = !!props.errors?.[props.item.name];
27
+ const minKey = useMemo(() => {
28
+ const parsed = props.item.minDate ? parseMY(props.item.minDate) : null;
29
+ return parsed ? toKey(parsed[0], parsed[1]) : null;
30
+ }, [props.item.minDate]);
31
+ const maxKey = useMemo(() => {
32
+ const parsed = props.item.maxDate ? parseMY(props.item.maxDate) : null;
33
+ return parsed ? toKey(parsed[0], parsed[1]) : null;
34
+ }, [props.item.maxDate]);
35
+ const handleOpen = useCallback((e) => {
36
+ if (props.item.disable)
37
+ return;
38
+ const committedNow = parseValue(value);
39
+ setDraft(committedNow);
40
+ if (committedNow.length > 0) {
41
+ const years = committedNow.map((s) => Number(s.split('/')[1]));
42
+ setViewYear(Math.min(...years));
43
+ }
44
+ setAnchorEl(e.currentTarget);
45
+ }, [props.item.disable, value]);
46
+ const handleClose = useCallback(() => setAnchorEl(null), []);
47
+ const toggleMonth = useCallback((my) => {
48
+ setDraft((prev) => (prev.includes(my) ? prev.filter((v) => v !== my) : [...prev, my]));
49
+ }, []);
50
+ const handleApply = useCallback(() => {
51
+ const sorted = [...draft].sort((a, b) => {
52
+ const [am, ay] = a.split('/').map(Number);
53
+ const [bm, by] = b.split('/').map(Number);
54
+ return toKey(am, ay) - toKey(bm, by);
55
+ });
56
+ const next = sorted.join(',');
57
+ props.setValue(props.item.name, next);
58
+ props.item.onChangeFn && props.item.onChangeFn(next);
59
+ props.clearErrors && props.clearErrors(props.item.name);
60
+ setAnchorEl(null);
61
+ }, [draft, props]);
62
+ const displayValue = useMemo(() => {
63
+ if (committed.length === 0)
64
+ return '';
65
+ if (committed.length <= 3) {
66
+ return committed
67
+ .map((my) => {
68
+ const [m, y] = my.split('/').map(Number);
69
+ return `${MONTH_LABELS[m - 1]} ${y}`;
70
+ })
71
+ .join(', ');
72
+ }
73
+ return `${committed.length} months selected`;
74
+ }, [committed]);
75
+ const muiLabel = variant !== 'standard' ? `${props.item.label}${props.item.required ? ' *' : ''}` : '';
76
+ 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 month(s)…', error: isError, size: "small", InputProps: {
77
+ readOnly: true,
78
+ endAdornment: (jsx(InputAdornment, { position: "end", children: jsx(CalendarTodayIcon, { sx: { fontSize: 15, color: 'text.disabled' } }) })),
79
+ sx: { cursor: 'inherit' },
80
+ }, inputProps: {
81
+ 'aria-labelledby': `${props.item.name}-label`,
82
+ 'aria-describedby': `${props.item.name}-helper ${props.item.name}-error`,
83
+ 'aria-required': props.item.required ? true : undefined,
84
+ style: { cursor: 'inherit' },
85
+ }, sx: {
86
+ '& .MuiFormLabel-root': { top: -10 },
87
+ '& .MuiInputLabel-shrink': { top: 0 },
88
+ } }) }), jsxs(Popover, { open: Boolean(anchorEl), anchorEl: anchorEl, onClose: handleClose, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, PaperProps: { sx: { mt: 0.5, borderRadius: 2, boxShadow: 6, width: 280 } }, children: [jsxs(Box, { sx: {
89
+ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
90
+ px: 1.5, py: 0.75,
91
+ borderBottom: '1px solid', borderColor: 'divider',
92
+ }, children: [jsx(IconButton, { size: "small", onClick: () => setViewYear((y) => y - 1), children: jsx(ChevronLeftIcon, { sx: { fontSize: 18 } }) }), jsx(Typography, { fontWeight: 600, sx: { fontSize: 14, userSelect: 'none' }, children: viewYear }), jsx(IconButton, { size: "small", onClick: () => setViewYear((y) => y + 1), children: jsx(ChevronRightIcon, { sx: { fontSize: 18 } }) })] }), jsx(Box, { sx: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 0.75, p: 1.5 }, children: MONTH_LABELS.map((mon, idx) => {
93
+ const monthNum = idx + 1;
94
+ const my = toMY(monthNum, viewYear);
95
+ const key = toKey(monthNum, viewYear);
96
+ const outOfRange = (minKey !== null && key < minKey) || (maxKey !== null && key > maxKey);
97
+ const isSelected = draft.includes(my);
98
+ return (jsx(Button, { variant: isSelected ? 'contained' : 'outlined', size: "small", disableElevation: true, disabled: outOfRange, onClick: () => toggleMonth(my), sx: {
99
+ fontSize: 12,
100
+ textTransform: 'none',
101
+ borderRadius: 2,
102
+ py: 0.75,
103
+ borderColor: isSelected ? 'primary.main' : 'divider',
104
+ fontWeight: isSelected ? 700 : 400,
105
+ }, children: mon }, my));
106
+ }) }), draft.length > 0 && (jsxs(Box, { sx: {
107
+ px: 2, py: 0.75,
108
+ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
109
+ borderTop: '1px solid', borderColor: 'divider',
110
+ }, children: [jsxs(Typography, { sx: { fontSize: 11, color: 'primary.main', fontWeight: 600 }, children: [draft.length, " month", draft.length > 1 ? 's' : '', " selected"] }), jsx(Button, { size: "small", color: "inherit", sx: { fontSize: 11, py: 0, textTransform: 'none', minWidth: 'auto' }, onClick: () => setDraft([]), children: "Clear" })] })), jsx(Box, { sx: {
111
+ px: 1.5, py: 1,
112
+ display: 'flex', justifyContent: 'flex-end',
113
+ borderTop: '1px solid', borderColor: 'divider',
114
+ }, children: jsx(Button, { size: "small", variant: "contained", disableElevation: true, onClick: handleApply, children: "Apply" }) })] }), jsx(FormBottomField, { ...props })] }, props.item.name));
115
+ };
116
+
117
+ export { MonthPickerMultiRender as default };
@@ -1,5 +1,5 @@
1
1
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
- import { useRef, useState, useEffect } from 'react';
2
+ import { useRef, useState, useEffect, useCallback } from 'react';
3
3
  import { getDatePicker } from '../../../utils/Constants/date-pickers.esm.js';
4
4
  import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
5
5
  import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
@@ -23,13 +23,15 @@ const Monthpickerrender = ({ props, variant, }) => {
23
23
  };
24
24
  loadComponents();
25
25
  }, []);
26
- const handleToggle = () => {
26
+ // Stable toggle — mirrors the DatepickerWrapperV2 pattern so onClick/onOpen
27
+ // don't hand the picker a new function reference on every render.
28
+ const handleToggle = useCallback(() => {
27
29
  if (!open) {
28
30
  ref.current?.blur();
29
31
  }
30
32
  document.activeElement?.blur();
31
33
  setOpen(!open);
32
- };
34
+ }, [open]);
33
35
  const isError = !!props.errors?.[props.item.name];
34
36
  const isShowBorderError = isError;
35
37
  if (isLoading) {
@@ -38,7 +40,7 @@ const Monthpickerrender = ({ props, variant, }) => {
38
40
  return (jsx(Fragment, { children: DatePicker && (jsx(Controller, { control: props.control, name: props.item.name, render: ({ field }) => (jsxs(Fragment, { children: [jsxs(LocalizationProvider, { dateAdapter: AdapterDayjs, children: [renderLabel(variant, props), jsx(DatePicker, { ref: ref, label: variant !== 'standard' &&
39
41
  `${props.item.label}${props.item.required ? ' *' : ''}`, views: ['year', 'month'], openTo: "month", view: view, onViewChange: (newView) => setView(newView), disabled: props.item.disable, value: field.value ? dayjs(formatDateMonthAndYear(field.value || null)) : null, slotProps: {
40
42
  textField: {
41
- onClick: () => handleToggle(),
43
+ onClick: handleToggle,
42
44
  inputRef: ref,
43
45
  error: isShowBorderError,
44
46
  inputProps: {
@@ -1,6 +1,6 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import { Box, Button, IconButton, Modal, Typography } from '@mui/material';
3
- import { useRef, useState, useEffect, useMemo, useCallback } from 'react';
3
+ import { useRef, useState, useCallback, useEffect, useMemo } from 'react';
4
4
  import { useWatch } from 'react-hook-form';
5
5
  import 'dayjs';
6
6
  import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
@@ -51,11 +51,6 @@ const isWordFile = (file) => {
51
51
  const name = typeof file === 'string' ? file : (file.name || '');
52
52
  return /\.(doc|docx)$/.test(name.toLowerCase());
53
53
  };
54
- const getObjectUrl = (file) => {
55
- if (!file)
56
- return '';
57
- return typeof file === 'string' ? file : URL.createObjectURL(file);
58
- };
59
54
  // ─── Component ────────────────────────────────────────────────────────────────
60
55
  const MultiFileWithPreview = ({ props, variant, }) => {
61
56
  const inputRef = useRef(null);
@@ -64,6 +59,43 @@ const MultiFileWithPreview = ({ props, variant, }) => {
64
59
  const [open, setOpen] = useState(false);
65
60
  const [selectedFiles, setSelectedFiles] = useState([]);
66
61
  const [currentFileIndex, setCurrentFileIndex] = useState(0);
62
+ // Blob URL cache, keyed by File reference — createObjectURL was previously
63
+ // called fresh on every render for the same File, leaking a blob URL each
64
+ // time (never revoked). Now a URL is created once per File and reused;
65
+ // stale/unmounted entries are revoked below.
66
+ const objectUrlCacheRef = useRef(new Map());
67
+ const getObjectUrl = useCallback((file) => {
68
+ if (!file)
69
+ return '';
70
+ if (typeof file === 'string')
71
+ return file;
72
+ const cache = objectUrlCacheRef.current;
73
+ const cached = cache.get(file);
74
+ if (cached)
75
+ return cached;
76
+ const url = URL.createObjectURL(file);
77
+ cache.set(file, url);
78
+ return url;
79
+ }, []);
80
+ // Revoke blob URLs for files that are no longer selected (removed/replaced).
81
+ useEffect(() => {
82
+ const cache = objectUrlCacheRef.current;
83
+ const currentFiles = new Set(selectedFiles.filter((f) => f instanceof File));
84
+ cache.forEach((url, file) => {
85
+ if (!currentFiles.has(file)) {
86
+ URL.revokeObjectURL(url);
87
+ cache.delete(file);
88
+ }
89
+ });
90
+ }, [selectedFiles]);
91
+ // Revoke everything on unmount.
92
+ useEffect(() => {
93
+ const cache = objectUrlCacheRef.current;
94
+ return () => {
95
+ cache.forEach((url) => URL.revokeObjectURL(url));
96
+ cache.clear();
97
+ };
98
+ }, []);
67
99
  // Clamp preview index when selection changes
68
100
  useEffect(() => {
69
101
  if (selectedFiles.length === 0) {
@@ -5,6 +5,16 @@ import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
5
5
  import 'dayjs';
6
6
  import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
7
7
 
8
+ // Identical for every checkbox in the group — hoisted so it isn't rebuilt
9
+ // per option on every render.
10
+ const CHECKBOX_LABEL_SX = {
11
+ '.MuiCheckbox-root': {
12
+ padding: '6px 2px 6px 8px',
13
+ '.css-imrjgg-MuiButtonBase-root-MuiCheckbox-root': {
14
+ color: 'rgb(0, 0, 0) !important',
15
+ },
16
+ },
17
+ };
8
18
  /**
9
19
  * FormCheckBoxGroup Component
10
20
  *
@@ -27,14 +37,7 @@ const FormCheckBoxGroup = ({ props, variant, }) => {
27
37
  return (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(FormControl, { component: "fieldset", children: jsx(FormGroup, { row: true, children: props.item?.settings &&
28
38
  props.item.settings.map((settings, i) => {
29
39
  return (jsx(Controller, { name: settings.name, control: props.control, render: ({ field }) => {
30
- return (jsx(FormControlLabel, { control: jsx(Checkbox, { ...field, checked: field.value }), sx: {
31
- '.MuiCheckbox-root': {
32
- padding: '6px 2px 6px 8px',
33
- '.css-imrjgg-MuiButtonBase-root-MuiCheckbox-root': {
34
- color: 'rgb(0, 0, 0) !important',
35
- },
36
- },
37
- }, label: jsx(Typography, { variant: "subtitle2", fontSize: '11px', fontWeight: 'normal !important', children: settings.label }), labelPlacement: "end" }));
40
+ return (jsx(FormControlLabel, { control: jsx(Checkbox, { ...field, checked: field.value }), sx: CHECKBOX_LABEL_SX, label: jsx(Typography, { variant: "subtitle2", fontSize: '11px', fontWeight: 'normal !important', children: settings.label }), labelPlacement: "end" }));
38
41
  } }, i));
39
42
  }) }) }), jsx(FormBottomField, { ...props })] }));
40
43
  };
@@ -5,6 +5,19 @@ import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
5
5
  import 'dayjs';
6
6
  import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
7
7
 
8
+ // Static — same for the group and for every radio option — hoisted so they
9
+ // aren't rebuilt on every render/option.
10
+ const RADIO_GROUP_SX = { gap: '8px' };
11
+ const RADIO_SX = {
12
+ padding: '6px 2px 6px 8px',
13
+ color: 'black',
14
+ '&.Mui-checked': {
15
+ color: '#1976d2', // blue when selected
16
+ },
17
+ '& .MuiSvgIcon-root': {
18
+ fontSize: 16, // controls the radio icon size
19
+ },
20
+ };
8
21
  /**
9
22
  * FormRadioGroup Component
10
23
  *
@@ -27,18 +40,7 @@ const FormRadioGroup = ({ props, variant }) => {
27
40
  return (jsxs(Fragment, { children: [renderLabel(variant, props), jsx(FormControl, { component: "fieldset", children: jsx(Controller, { name: props.item.name, control: props.control, render: ({ field }) => (jsx(RadioGroup, { value: field.value || '', onChange: (e) => {
28
41
  field.onChange(e.target.value); // update form state
29
42
  props.item.onChangeFn && props.item.onChangeFn(e.target.value); // optional handler
30
- }, row: true, sx: {
31
- gap: '8px',
32
- }, children: props.item?.settings?.map((option, i) => (jsx(FormControlLabel, { value: option.name, control: jsx(Radio, { sx: {
33
- padding: '6px 2px 6px 8px',
34
- color: 'black',
35
- '&.Mui-checked': {
36
- color: '#1976d2', // blue when selected
37
- },
38
- '& .MuiSvgIcon-root': {
39
- fontSize: 16, // controls the radio icon size
40
- },
41
- } }), label: jsx(Typography, { fontSize: '11px', fontWeight: 'normal !important', children: option.label }) }, i))) })) }) }), jsx(FormBottomField, { ...props })] }));
43
+ }, row: true, sx: RADIO_GROUP_SX, children: props.item?.settings?.map((option, i) => (jsx(FormControlLabel, { value: option.name, control: jsx(Radio, { sx: RADIO_SX }), label: jsx(Typography, { fontSize: '11px', fontWeight: 'normal !important', children: option.label }) }, i))) })) }) }), jsx(FormBottomField, { ...props })] }));
42
44
  };
43
45
 
44
46
  export { FormRadioGroup as default };