tp-react-elements 2.0.4 → 2.0.5

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 (38) 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.esm14.js +2 -2
  4. package/dist/_virtual/index.esm4.js +2 -2
  5. package/dist/_virtual/index.esm6.js +2 -2
  6. package/dist/_virtual/index.esm7.js +2 -2
  7. package/dist/components/DateRangePicker/DateRangePicker.esm.js +49 -72
  8. package/dist/components/Form/FormRender.esm.js +1 -1
  9. package/dist/components/FormComponents/DatePicker/DatepickerWrapperV2.esm.js +10 -15
  10. package/dist/components/FormComponents/DatePicker/DatetimepickerWrapper.esm.js +53 -61
  11. package/dist/components/FormComponents/FileUpload/FormRenderFileUpload.esm.js +54 -68
  12. package/dist/components/FormComponents/FileUpload/FormRenderMultiFileUpload.esm.js +61 -76
  13. package/dist/components/FormComponents/FileUpload/MultiFileWithPreview.esm.js +103 -132
  14. package/dist/components/FormComponents/FormNumberField/FormNumberField.d.ts +0 -38
  15. package/dist/components/FormComponents/FormNumberField/FormNumberField.esm.js +32 -97
  16. package/dist/components/FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js +44 -38
  17. package/dist/components/FormComponents/FormTextField/FormTextField.esm.js +3 -3
  18. package/dist/components/FormComponents/FormTextFieldWithSelect/FormTextFieldWithSelect.esm.js +3 -2
  19. package/dist/components/FormComponents/PasswordField/PasswordField.esm.js +4 -4
  20. package/dist/components/FormComponents/RichTextEditor/RichTextEditor.esm.js +23 -22
  21. package/dist/components/FormComponents/Select/SearchableSelect.esm.js +9 -9
  22. package/dist/components/FormComponents/TimePicker/TimePicker.esm.js +19 -19
  23. package/dist/components/FormComponents/index.esm.js +2 -2
  24. package/dist/components/ModalField/ConfirmationDialog.esm.js +3 -3
  25. package/dist/components/ModalField/ModalField.esm.js +5 -12
  26. package/dist/components/PaginatedGrid/DynamicFilterBar.d.ts +1 -1
  27. package/dist/components/PaginatedGrid/DynamicFilterBar.esm.js +27 -24
  28. package/dist/components/PaginatedGrid/GenericPaginatedGrid.d.ts +13 -1
  29. package/dist/components/PaginatedGrid/GenericPaginatedGrid.esm.js +102 -85
  30. package/dist/components/SelectField/MultiSelectFieldComponent.esm.js +3 -3
  31. package/dist/components/SessionTimeOut/SessionTimeOut.esm.js +33 -34
  32. package/dist/node_modules/react-date-range/dist/components/Calendar/index.esm.js +1 -1
  33. package/dist/node_modules/react-date-range/dist/components/DateRange/index.esm.js +1 -1
  34. package/dist/node_modules/react-date-range/dist/components/DateRangePicker/index.esm.js +1 -1
  35. package/dist/node_modules/react-date-range/dist/components/DayCell/index.esm.js +1 -1
  36. package/dist/node_modules/react-date-range/dist/components/InputRangeField/index.esm.js +1 -1
  37. package/dist/node_modules/react-date-range/dist/components/Month/index.esm.js +1 -1
  38. package/package.json +1 -1
@@ -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 DayCell = {};
2
2
 
3
- export { Month as __exports };
3
+ export { DayCell as __exports };
@@ -1,3 +1,3 @@
1
- var InputRangeField = {};
1
+ var Month = {};
2
2
 
3
- export { InputRangeField as __exports };
3
+ export { Month as __exports };
@@ -1,3 +1,3 @@
1
- var DateRange = {};
1
+ var DateRangePicker = {};
2
2
 
3
- export { DateRange as __exports };
3
+ export { DateRangePicker 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 DateRangePicker = {};
1
+ var Calendar = {};
2
2
 
3
- export { DateRangePicker as __exports };
3
+ export { Calendar as __exports };
@@ -1,5 +1,5 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { useState, useEffect } from 'react';
2
+ import { useState, useEffect, useCallback, useMemo } from 'react';
3
3
  import { Controller } from 'react-hook-form';
4
4
  import { d as distExports } from '../../_virtual/index.esm.js';
5
5
  import { useRdrStyles } from '../../utils/rdrStyles.esm.js';
@@ -11,23 +11,20 @@ import { format } from '../../node_modules/date-fns/format.esm.js';
11
11
  import { parse } from '../../node_modules/date-fns/parse.esm.js';
12
12
  import { isValid } from '../../node_modules/date-fns/isValid.esm.js';
13
13
 
14
+ // Pure utility — no component state dependency, defined at module level
15
+ const getValidDate = (dateString) => {
16
+ if (!dateString)
17
+ return undefined;
18
+ const date = parse(dateString, 'dd/MM/yyyy', new Date());
19
+ return isValid(date) ? date : undefined;
20
+ };
14
21
  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
22
  useRdrStyles();
18
- // Using a generic type for anchorEl to avoid environment-specific linting issues.
19
- // eslint-disable-next-line react/prop-types
20
23
  const [anchorEl, setAnchorEl] = useState(null);
21
24
  const fromDateName = item.name;
22
25
  const toDateName = `${item.name}_to`;
23
26
  const fromDateValue = watch(fromDateName);
24
27
  const toDateValue = watch(toDateName);
25
- const getValidDate = (dateString) => {
26
- if (!dateString)
27
- return undefined;
28
- const date = parse(dateString, 'dd/MM/yyyy', new Date());
29
- return isValid(date) ? date : undefined;
30
- };
31
28
  const [state, setState] = useState([
32
29
  {
33
30
  startDate: getValidDate(fromDateValue),
@@ -35,23 +32,52 @@ const DateRangePickerComponent = ({ item, control, watch, setValue, getValues, c
35
32
  key: 'selection',
36
33
  },
37
34
  ]);
35
+ // Sync calendar state when watched field values change.
36
+ // Uses fromDateValue/toDateValue directly — no need to call watch() again inside.
38
37
  useEffect(() => {
39
- const fromDate = watch(fromDateName);
40
- const toDate = watch(toDateName);
41
38
  setState([
42
39
  {
43
- startDate: getValidDate(fromDate),
44
- endDate: getValidDate(toDate),
40
+ startDate: getValidDate(fromDateValue),
41
+ endDate: getValidDate(toDateValue),
45
42
  key: 'selection',
46
43
  },
47
44
  ]);
48
- }, [fromDateValue, toDateValue, watch, fromDateName, toDateName]);
49
- const handleClick = (event) => {
45
+ }, [fromDateValue, toDateValue]);
46
+ const handleClick = useCallback((event) => {
50
47
  setAnchorEl(event.currentTarget);
51
- };
52
- const handleClose = () => {
48
+ }, []);
49
+ const handleClose = useCallback(() => {
53
50
  setAnchorEl(null);
54
- };
51
+ }, []);
52
+ // Stable range-change handler — only recreated when field names or callbacks change
53
+ const handleRangeChange = useCallback((range) => {
54
+ const selection = range.selection;
55
+ setState([selection]);
56
+ if (selection.startDate && selection.endDate) {
57
+ setValue(fromDateName, format(selection.startDate, 'dd/MM/yyyy'), {
58
+ shouldValidate: true,
59
+ shouldDirty: true,
60
+ });
61
+ setValue(toDateName, format(selection.endDate, 'dd/MM/yyyy'), {
62
+ shouldValidate: true,
63
+ shouldDirty: true,
64
+ });
65
+ }
66
+ if (selection.startDate &&
67
+ selection.endDate &&
68
+ selection.startDate.getTime() !== selection.endDate.getTime()) {
69
+ const dateObject = {
70
+ startDate: format(selection.startDate, 'dd/MM/yyyy'),
71
+ endDate: format(selection.endDate, 'dd/MM/yyyy'),
72
+ };
73
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
74
+ item.onChangeFn && item.onChangeFn(dateObject || null);
75
+ setAnchorEl(null);
76
+ }
77
+ }, [fromDateName, toDateName, setValue, item.onChangeFn]);
78
+ // Memoize optional min/max date conversions so they don't re-create each render
79
+ const minDate = useMemo(() => (item?.minDate ? dayjs(item.minDate, item.DateFormat || 'DD/MM/YYYY').toDate() : undefined), [item?.minDate, item?.DateFormat]);
80
+ const maxDate = useMemo(() => (item?.maxDate ? dayjs(item.maxDate, item.DateFormat || 'DD/MM/YYYY').toDate() : undefined), [item?.maxDate, item?.DateFormat]);
55
81
  const open = Boolean(anchorEl);
56
82
  const id = open ? 'date-range-popover' : undefined;
57
83
  const displayValue = state[0].startDate && state[0].endDate
@@ -61,58 +87,9 @@ const DateRangePickerComponent = ({ item, control, watch, setValue, getValues, c
61
87
  return (jsxs(Fragment, { children: [jsx(Box, { onClick: handleClick, sx: { width: '100%' }, children: jsx(Controller, { name: fromDateName, control: control, render: ({ fieldState }) => (jsxs(Fragment, { children: [renderLabel(variant, {
62
88
  item,
63
89
  ...rest,
64
- }), ' ', jsx(TextField, { fullWidth: true, value: displayValue, placeholder: item.placeholder || 'DD/MM/YYYY - DD/MM/YYYY', error: !!fieldState.error, InputProps: {
65
- readOnly: true,
66
- }, label: label, size: "small" })] })) }) }), jsx(Popover, { id: id, open: open, anchorEl: anchorEl, onClose: handleClose, anchorOrigin: {
67
- vertical: 'bottom',
68
- horizontal: 'left',
69
- }, PaperProps: {
70
- style: {
71
- transform: 'scale(0.9)',
72
- transformOrigin: 'top left',
73
- },
74
- }, children: jsx(distExports.DateRange, { editableDateInputs: false, onChange: (range) => {
75
- const selection = range.selection;
76
- setState([selection]);
77
- // if (selection.startDate) {
78
- // setValue(fromDateName, format(selection.startDate, 'dd/MM/yyyy'), {
79
- // shouldValidate: true,
80
- // shouldDirty: true,
81
- // });
82
- // }
83
- if (selection.startDate && selection.endDate) {
84
- setValue(fromDateName, format(selection.startDate, 'dd/MM/yyyy'), {
85
- shouldValidate: true,
86
- shouldDirty: true,
87
- });
88
- setValue(toDateName, format(selection.endDate, 'dd/MM/yyyy'), {
89
- shouldValidate: true,
90
- shouldDirty: true,
91
- });
92
- }
93
- if (selection.startDate &&
94
- selection.endDate &&
95
- selection.startDate?.getTime() !== selection.endDate?.getTime()) {
96
- const dateObject = {
97
- startDate: format(selection.startDate, 'dd/MM/yyyy'),
98
- endDate: format(selection.endDate, 'dd/MM/yyyy'),
99
- };
100
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
101
- item.onChangeFn && item.onChangeFn(dateObject || null);
102
- handleClose();
103
- }
104
- }, moveRangeOnFirstSelection: false, ranges: state, minDate: item?.minDate
105
- ? dayjs(item?.minDate, item.DateFormat || 'DD/MM/YYYY').toDate()
106
- : undefined, maxDate: item?.maxDate
107
- ? dayjs(item?.maxDate, item.DateFormat || 'DD/MM/YYYY').toDate()
108
- : undefined, months: 2, direction: "horizontal", showDateDisplay: false, dateDisplayFormat: "dd/MM/yyyy", rangeColors: ['#4d73b1ff'] }) }), jsx(FormBottomField, { item,
109
- control,
110
- errors,
111
- getValues,
112
- setValue,
113
- clearErrors,
114
- register,
115
- watch })] }));
90
+ }), ' ', jsx(TextField, { fullWidth: true, value: displayValue, placeholder: item.placeholder || 'DD/MM/YYYY - DD/MM/YYYY', error: !!fieldState.error, InputProps: { readOnly: true }, label: label, size: "small" })] })) }) }), jsx(Popover, { id: id, open: open, anchorEl: anchorEl, onClose: handleClose, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, PaperProps: {
91
+ style: { transform: 'scale(0.9)', transformOrigin: 'top left' },
92
+ }, children: jsx(distExports.DateRange, { editableDateInputs: false, onChange: handleRangeChange, moveRangeOnFirstSelection: false, ranges: state, minDate: minDate, maxDate: maxDate, months: 2, direction: "horizontal", showDateDisplay: false, dateDisplayFormat: "dd/MM/yyyy", rangeColors: ['#4d73b1ff'] }) }), jsx(FormBottomField, { item, control, errors, getValues, setValue, clearErrors, register, watch })] }));
116
93
  };
117
94
 
118
95
  export { DateRangePickerComponent as default };
@@ -6,7 +6,6 @@ import FormNumberField from '../FormComponents/FormNumberField/FormNumberField.e
6
6
  import FormNumberFieldDecimal from '../FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js';
7
7
  import FormRadioGroup from '../FormComponents/FormRadioGroup/FormRadioGroup.esm.js';
8
8
  import FormTextAreaField from '../FormComponents/FormTextAreaField/FormTextAreaField.esm.js';
9
- import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
10
9
  import PasswordField from '../FormComponents/PasswordField/PasswordField.esm.js';
11
10
  import SingleSelect from '../FormComponents/Select/SingleSelect.esm.js';
12
11
  import MultiSelectAutocomplete from '../FormComponents/Select/MultiSelectAutocomplete.esm.js';
@@ -18,6 +17,7 @@ import DateRangePickerComponent from '../DateRangePicker/DateRangePicker.esm.js'
18
17
  import RichTextEditorWrapper from '../FormComponents/RichTextEditor/RichTextEditorWrapper.esm.js';
19
18
  import SingleSelectSearchApi from '../FormComponents/Select/SingleSelectSearchApi.esm.js';
20
19
  import DatetimepickerWrapper from '../FormComponents/DatePicker/DatetimepickerWrapper.esm.js';
20
+ import FormTextField from '../FormComponents/FormTextField/FormTextField.esm.js';
21
21
 
22
22
  // Lazy-loaded heavy components
23
23
  // const RichTextEditorWrapper = lazy(() =>
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } 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';
@@ -22,15 +22,14 @@ const DatepickerWrapperV2 = ({ props, variant, }) => {
22
22
  };
23
23
  loadComponents();
24
24
  }, []);
25
- const handleToggle = () => {
26
- if (!open) {
25
+ // Stable toggle — does not change between renders
26
+ const handleToggle = useCallback(() => {
27
+ if (!open)
27
28
  inputTextRef.current?.blur();
28
- }
29
29
  document.activeElement?.blur();
30
- setOpen(!open);
31
- };
30
+ setOpen((prev) => !prev);
31
+ }, [open]);
32
32
  const isError = !!props.errors?.[props.item.name];
33
- const isShowBorderError = isError;
34
33
  if (isLoading) {
35
34
  return jsx("div", { style: { minHeight: '36px' }, children: "Loading..." });
36
35
  }
@@ -48,7 +47,7 @@ const DatepickerWrapperV2 = ({ props, variant, }) => {
48
47
  props?.clearErrors && props?.clearErrors(props.item.name);
49
48
  }, slotProps: {
50
49
  textField: {
51
- onClick: () => handleToggle(),
50
+ onClick: handleToggle,
52
51
  inputRef: inputTextRef,
53
52
  onBlur: (e) => {
54
53
  props?.item?.onBlurFn && props?.item?.onBlurFn(e);
@@ -59,14 +58,10 @@ const DatepickerWrapperV2 = ({ props, variant, }) => {
59
58
  'aria-describedby': `${props.item.name}-helper ${props.item.name}-error`,
60
59
  'aria-required': props.item.required ? true : undefined,
61
60
  },
62
- error: isShowBorderError,
61
+ error: isError,
63
62
  sx: {
64
- '& .MuiFormLabel-root': {
65
- top: -10,
66
- },
67
- '& .MuiInputLabel-shrink': {
68
- top: 0,
69
- },
63
+ '& .MuiFormLabel-root': { top: -10 },
64
+ '& .MuiInputLabel-shrink': { top: 0 },
70
65
  },
71
66
  },
72
67
  } })] })), jsx(FormBottomField, { ...props })] }));
@@ -1,10 +1,10 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { useRef, useState, useEffect } from 'react';
2
+ import { useRef, useState, useEffect, useCallback } from 'react';
3
3
  import { getDateTimePicker } 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';
6
6
  import dayjs from 'dayjs';
7
- import { Controller } from 'react-hook-form';
7
+ import { useWatch, Controller } from 'react-hook-form';
8
8
  import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
9
9
  import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
10
10
 
@@ -17,27 +17,18 @@ const DatetimepickerWrapper = ({ props, variant, }) => {
17
17
  const timeFormat = isAmpm ? 'hh:mm A' : 'HH:mm';
18
18
  const defaultDateFormat = `DD/MM/YYYY ${timeFormat}`;
19
19
  const displayFormat = props.item.DateFormat || defaultDateFormat;
20
- // useEffect(() => {
21
- // const loadComponents = async () => {
22
- // setIsLoading(true);
23
- // const [dateTimePicker] = await Promise.all([getDateTimePicker()]);
24
- // setDateTimePicker(() => dateTimePicker);
25
- // setIsLoading(false);
26
- // };
27
- // loadComponents();
28
- // }, []);
29
20
  useEffect(() => {
30
21
  const loadComponents = async () => {
31
22
  try {
32
23
  setIsLoading(true);
33
24
  const component = await getDateTimePicker();
34
25
  if (!component) {
35
- console.error("DateTimePicker component resolved to undefined. Check your utility export.");
26
+ console.error('DateTimePicker component resolved to undefined.');
36
27
  }
37
28
  setDateTimePicker(() => component);
38
29
  }
39
30
  catch (err) {
40
- console.error("Failed to load component:", err);
31
+ console.error('Failed to load component:', err);
41
32
  }
42
33
  finally {
43
34
  setIsLoading(false);
@@ -45,63 +36,64 @@ const DatetimepickerWrapper = ({ props, variant, }) => {
45
36
  };
46
37
  loadComponents();
47
38
  }, []);
48
- const handleToggle = () => {
39
+ // Stable toggle handler
40
+ const handleToggle = useCallback(() => {
49
41
  if (!open)
50
42
  inputTextRef.current?.blur();
51
43
  document.activeElement?.blur();
52
- setOpen(!open);
53
- };
44
+ setOpen((prev) => !prev);
45
+ }, [open]);
46
+ // Watch field value at component level so the sync effect below
47
+ // can live here instead of inside the Controller render prop
48
+ // (hooks must not be called conditionally or inside render callbacks).
49
+ const fieldValue = useWatch({ control: props.control, name: props.item.name });
50
+ // Sync derived date/time sub-fields whenever the combined value changes.
51
+ // Previously this useEffect was erroneously placed inside the Controller
52
+ // render prop, violating the rules of hooks.
53
+ useEffect(() => {
54
+ if (fieldValue && props.setValue) {
55
+ const dateObj = dayjs(fieldValue, displayFormat);
56
+ if (dateObj.isValid()) {
57
+ props.setValue(`${props.item.name}Date`, dateObj.format('DD/MM/YYYY'));
58
+ props.setValue(`${props.item.name}Time`, dateObj.format(timeFormat));
59
+ }
60
+ }
61
+ }, [fieldValue, props.item.name, props.setValue, displayFormat, timeFormat]);
54
62
  const isError = !!props.errors?.[props.item.name];
55
63
  if (isLoading) {
56
64
  return jsx("div", { style: { minHeight: '36px' }, children: "Loading..." });
57
65
  }
58
- return (jsx(Controller, { control: props.control, name: props.item.name, render: ({ field }) => {
59
- // --- SYNC LOGIC FOR RESET / INITIAL VALUES ---
60
- // This effect runs whenever the value in the form state changes
61
- // eslint-disable-next-line react-hooks/rules-of-hooks
62
- useEffect(() => {
63
- if (field.value && props.setValue) {
64
- const dateObj = dayjs(field.value, displayFormat);
65
- if (dateObj.isValid()) {
66
- props.setValue(`${props.item.name}Date`, dateObj.format('DD/MM/YYYY'));
67
- props.setValue(`${props.item.name}Time`, dateObj.format(timeFormat));
68
- }
69
- }
70
- }, [field.value, props.item.name, props.setValue, displayFormat, timeFormat]);
71
- return (jsxs(Fragment, { children: [DateTimePicker && (jsxs(LocalizationProvider, { dateAdapter: AdapterDayjs, children: [renderLabel(variant, props), jsx(DateTimePicker, { views: ['year', 'month', 'day', 'hours', 'minutes'], ampm: isAmpm, format: displayFormat, label: variant !== 'standard' ? `${props.item.label}${props.item.required ? ' *' : ''}` : '', value: field.value
72
- ? dayjs(field.value, displayFormat).isValid()
73
- ? dayjs(field.value, displayFormat)
74
- : dayjs(field.value)
75
- : null, className: 'read-only', open: open, onOpen: handleToggle, onClose: handleToggle, minDateTime: props?.item?.minDate ? dayjs(props?.item?.minDate, displayFormat) : undefined, maxDateTime: props?.item?.maxDate ? dayjs(props?.item?.maxDate, displayFormat) : undefined, disabled: props.item.disable || false, onChange: (date) => {
76
- const formattedDateTime = dayjs(date).format(displayFormat);
77
- field.onChange(formattedDateTime);
78
- props?.item?.onChangeFn && props?.item?.onChangeFn(formattedDateTime);
79
- props?.clearErrors && props?.clearErrors(props.item.name);
80
- }, slotProps: {
81
- textField: {
82
- onClick: () => handleToggle(),
83
- inputRef: inputTextRef,
84
- onBlur: (e) => props?.item?.onBlurFn && props?.item?.onBlurFn(e),
85
- inputProps: {
86
- style: { cursor: 'pointer' },
87
- 'aria-labelledby': `${props.item.name}-label`,
88
- },
89
- readOnly: true,
90
- error: isError,
91
- sx: {
92
- '& .MuiFormLabel-root': { top: -10 },
93
- '& .MuiInputLabel-shrink': { top: 0 },
94
- },
66
+ return (jsx(Controller, { control: props.control, name: props.item.name, render: ({ field }) => (jsxs(Fragment, { children: [DateTimePicker && (jsxs(LocalizationProvider, { dateAdapter: AdapterDayjs, children: [renderLabel(variant, props), jsx(DateTimePicker, { views: ['year', 'month', 'day', 'hours', 'minutes'], ampm: isAmpm, format: displayFormat, label: variant !== 'standard' ? `${props.item.label}${props.item.required ? ' *' : ''}` : '', value: field.value
67
+ ? dayjs(field.value, displayFormat).isValid()
68
+ ? dayjs(field.value, displayFormat)
69
+ : dayjs(field.value)
70
+ : null, className: 'read-only', open: open, onOpen: handleToggle, onClose: handleToggle, minDateTime: props?.item?.minDate ? dayjs(props?.item?.minDate, displayFormat) : undefined, maxDateTime: props?.item?.maxDate ? dayjs(props?.item?.maxDate, displayFormat) : undefined, disabled: props.item.disable || false, onChange: (date) => {
71
+ const formattedDateTime = dayjs(date).format(displayFormat);
72
+ field.onChange(formattedDateTime);
73
+ props?.item?.onChangeFn && props?.item?.onChangeFn(formattedDateTime);
74
+ props?.clearErrors && props?.clearErrors(props.item.name);
75
+ }, slotProps: {
76
+ textField: {
77
+ onClick: handleToggle,
78
+ inputRef: inputTextRef,
79
+ onBlur: (e) => props?.item?.onBlurFn && props?.item?.onBlurFn(e),
80
+ inputProps: {
81
+ style: { cursor: 'pointer' },
82
+ 'aria-labelledby': `${props.item.name}-label`,
83
+ },
84
+ readOnly: true,
85
+ error: isError,
86
+ sx: {
87
+ '& .MuiFormLabel-root': { top: -10 },
88
+ '& .MuiInputLabel-shrink': { top: 0 },
95
89
  },
96
- desktopPaper: {
97
- sx: {
98
- '& .MuiPickersLayout-root': {
99
- flexDirection: 'column',
100
- }
101
- }
90
+ },
91
+ desktopPaper: {
92
+ sx: {
93
+ '& .MuiPickersLayout-root': { flexDirection: 'column' },
102
94
  },
103
- } })] })), jsx(FormBottomField, { ...props })] }));
104
- } }, props.item.name));
95
+ },
96
+ } })] })), jsx(FormBottomField, { ...props })] })) }, props.item.name));
105
97
  };
106
98
 
107
99
  export { DatetimepickerWrapper as default };
@@ -1,96 +1,82 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import { Box, TextField } from '@mui/material';
3
- import { useRef, useEffect } from 'react';
3
+ import { useRef, useEffect, useMemo, useCallback } from 'react';
4
4
  import { useWatch } from 'react-hook-form';
5
5
  import FormBottomField from '../FormBottomField/FormBottomField.esm.js';
6
6
  import 'dayjs';
7
7
  import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
8
8
 
9
+ // Static lookup — defined once at module level, never recreated
10
+ const EXTENSION_MAP = {
11
+ excel: ['xls', 'xlsx'],
12
+ pdf: ['pdf'],
13
+ zip: ['zip'],
14
+ image: ['jpg', 'jpeg', 'png'],
15
+ document: ['doc', 'docx', 'pdf'],
16
+ all: ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx', 'zip'],
17
+ };
9
18
  const FormRenderFileUpload = ({ props, variant, }) => {
10
19
  const isError = !!props.errors?.[props.item.name];
11
- const isShowBorderError = isError;
12
20
  const inputRef = useRef(null);
13
21
  const watched = useWatch({ control: props.control, name: props.item.name });
14
22
  useEffect(() => {
15
- if (watched === null || watched === undefined) {
16
- if (inputRef.current)
17
- inputRef.current.value = '';
23
+ if ((watched === null || watched === undefined) && inputRef.current) {
24
+ inputRef.current.value = '';
18
25
  }
19
26
  }, [watched]);
20
- const extensionMap = {
21
- excel: ['xls', 'xlsx'],
22
- pdf: ['pdf'],
23
- zip: ['zip'],
24
- image: ['jpg', 'jpeg', 'png'],
25
- document: ['doc', 'docx', 'pdf'],
26
- all: ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx', 'zip'],
27
- };
28
- const getValidExtensions = () => {
27
+ // Derived once per fileType change — not on every render
28
+ const validExtensions = useMemo(() => {
29
29
  const fileType = props.item.fileType;
30
- if (!fileType) {
31
- return extensionMap.all;
32
- }
30
+ if (!fileType)
31
+ return EXTENSION_MAP.all;
33
32
  if (Array.isArray(fileType)) {
34
- // Check if array contains predefined keys (like 'image', 'pdf') or actual extensions
35
- const hasPredefinedKeys = fileType.some((item) => extensionMap[item]);
33
+ const hasPredefinedKeys = fileType.some((t) => EXTENSION_MAP[t]);
36
34
  if (hasPredefinedKeys) {
37
- // Merge extensions from all predefined keys
38
- const extensions = new Set();
39
- fileType.forEach((item) => {
40
- if (extensionMap[item]) {
41
- extensionMap[item].forEach((ext) => extensions.add(ext));
42
- }
43
- else {
44
- // Treat as direct extension
45
- extensions.add(item);
46
- }
35
+ const exts = new Set();
36
+ fileType.forEach((t) => {
37
+ if (EXTENSION_MAP[t])
38
+ EXTENSION_MAP[t].forEach((e) => exts.add(e));
39
+ else
40
+ exts.add(t);
47
41
  });
48
- return Array.from(extensions);
42
+ return Array.from(exts);
49
43
  }
50
- // All items are direct extensions
51
44
  return fileType;
52
45
  }
53
- return extensionMap[fileType] || extensionMap.all;
54
- };
55
- const getAcceptString = () => {
56
- const extensions = getValidExtensions();
57
- return extensions.map((ext) => `.${ext}`).join(',');
58
- };
59
- const accept = getAcceptString();
60
- return (jsxs(Fragment, { children: [jsxs(Box, { paddingLeft: '4px', sx: { ...props.item.sx }, children: [props.item?.label && (jsx(Box, { sx: { fontSize: '10px;' }, children: renderLabel(variant, props, true) })), jsx(TextField, { type: "file", id: props.item.name, error: isShowBorderError, inputRef: inputRef, inputProps: {
46
+ return EXTENSION_MAP[fileType] ?? EXTENSION_MAP.all;
47
+ }, [props.item.fileType]);
48
+ const accept = useMemo(() => validExtensions.map((e) => `.${e}`).join(','), [validExtensions]);
49
+ const handleChange = useCallback((event) => {
50
+ const file = event.target?.files[0];
51
+ const fileName = file ? file.name : null;
52
+ const fileExtension = fileName ? fileName.split('.').pop().toLowerCase() : null;
53
+ if (props.item?.fileType && fileExtension && !validExtensions.includes(fileExtension)) {
54
+ props.item?.handleFileError?.(`Please upload ${validExtensions.join(', ')} files only`);
55
+ event.target.value = '';
56
+ props.setValue(props.item.name, null);
57
+ props.setValue(props.item.name + 'Name', '');
58
+ return;
59
+ }
60
+ if (file && file.size > (props.item.filesize || 20000000)) {
61
+ const maxSizeMB = ((props.item.filesize || 20000000) / (1024 * 1024)).toFixed(2);
62
+ props.item?.handleFileError?.(`File size should be less than ${maxSizeMB}MB`);
63
+ event.target.value = '';
64
+ props.setValue(props.item.name, null);
65
+ props.setValue(props.item.name + 'Name', '');
66
+ return;
67
+ }
68
+ console.log(props.item?.name, ':props.item?.name');
69
+ props.setValue(props.item.name, file);
70
+ props.setValue(props.item.name + 'Name', fileName);
71
+ props.clearErrors(props.item.name);
72
+ props.item.onChangeSetFileFn?.(file);
73
+ }, [validExtensions, props.item, props.setValue, props.clearErrors]);
74
+ return (jsxs(Fragment, { children: [jsxs(Box, { paddingLeft: '4px', sx: { ...props.item.sx }, children: [props.item?.label && (jsx(Box, { sx: { fontSize: '10px;' }, children: renderLabel(variant, props, true) })), jsx(TextField, { type: "file", id: props.item.name, error: isError, inputRef: inputRef, inputProps: {
61
75
  accept,
62
76
  'aria-labelledby': `${props.item.name}-label`,
63
77
  'aria-describedby': `${props.item.name}-helper ${props.item.name}-error`,
64
78
  'aria-required': props.item.required ? true : undefined,
65
- }, onChange: (event) => {
66
- const file = event.target?.files[0];
67
- const fileName = file ? file.name : null;
68
- const fileExtension = fileName ? fileName.split('.').pop().toLowerCase() : null;
69
- const validExtensions = getValidExtensions();
70
- if (props.item?.fileType && fileExtension && !validExtensions.includes(fileExtension)) {
71
- props.item?.handleFileError &&
72
- props.item?.handleFileError(`Please upload ${validExtensions.join(', ')} files only`);
73
- event.target.value = ''; // Clear the file input\
74
- props.setValue(props.item?.name, null);
75
- props.setValue(props.item?.name + 'Name', '');
76
- return;
77
- }
78
- else if (event.target.files[0].size > (props.item.filesize || 20000000)) {
79
- const maxSizeMB = ((props.item.filesize || 20000000) / (1024 * 1024)).toFixed(2);
80
- props.item?.handleFileError &&
81
- props.item?.handleFileError(`File size should be less than ${maxSizeMB}MB`);
82
- event.target.value = ''; // Clear the file input
83
- props.setValue(props.item?.name, null);
84
- props.setValue(props.item?.name + 'Name', '');
85
- return;
86
- }
87
- console.log(props.item?.name, ':props.item?.name');
88
- props.setValue(props.item?.name, file);
89
- props.setValue(props.item?.name + 'Name', fileName);
90
- props.clearErrors(props.item?.name);
91
- props.item.onChangeSetFileFn &&
92
- props.item.onChangeSetFileFn(file);
93
- }, sx: { width: '100%' } })] }), jsx(FormBottomField, { ...props })] }));
79
+ }, onChange: handleChange, sx: { width: '100%' } })] }), jsx(FormBottomField, { ...props })] }));
94
80
  };
95
81
 
96
82
  export { FormRenderFileUpload as default };