tp-react-elements 2.0.4 → 2.0.7
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.
- package/dist/_virtual/index.esm11.js +2 -2
- package/dist/_virtual/index.esm12.js +2 -2
- package/dist/_virtual/index.esm13.js +2 -2
- package/dist/_virtual/index.esm14.js +2 -2
- package/dist/_virtual/index.esm4.js +2 -2
- package/dist/_virtual/index.esm6.js +2 -2
- package/dist/_virtual/index.esm7.js +2 -2
- package/dist/components/DateRangePicker/DateRangePicker.esm.js +49 -72
- package/dist/components/FormComponents/DatePicker/DatepickerWrapperV2.esm.js +10 -15
- package/dist/components/FormComponents/DatePicker/DatetimepickerWrapper.esm.js +53 -61
- package/dist/components/FormComponents/FileUpload/FormRenderFileUpload.esm.js +54 -68
- package/dist/components/FormComponents/FileUpload/FormRenderMultiFileUpload.esm.js +61 -76
- package/dist/components/FormComponents/FileUpload/MultiFileWithPreview.esm.js +103 -132
- package/dist/components/FormComponents/FormNumberField/FormNumberField.d.ts +0 -38
- package/dist/components/FormComponents/FormNumberField/FormNumberField.esm.js +32 -97
- package/dist/components/FormComponents/FormNumberField/FormNumberFieldDecimal.esm.js +44 -38
- package/dist/components/FormComponents/FormTextField/FormTextField.esm.js +3 -3
- package/dist/components/FormComponents/FormTextFieldWithSelect/FormTextFieldWithSelect.esm.js +3 -2
- package/dist/components/FormComponents/PasswordField/PasswordField.esm.js +4 -4
- package/dist/components/FormComponents/RichTextEditor/RichTextEditor.esm.js +23 -22
- package/dist/components/FormComponents/Select/SearchableSelect.esm.js +9 -9
- package/dist/components/FormComponents/TimePicker/TimePicker.esm.js +19 -19
- package/dist/components/ModalField/ConfirmationDialog.esm.js +3 -3
- package/dist/components/ModalField/ModalField.esm.js +5 -12
- package/dist/components/PaginatedGrid/DynamicFilterBar.d.ts +1 -1
- package/dist/components/PaginatedGrid/DynamicFilterBar.esm.js +27 -24
- package/dist/components/PaginatedGrid/GenericPaginatedGrid.d.ts +36 -2
- package/dist/components/PaginatedGrid/GenericPaginatedGrid.esm.js +196 -105
- package/dist/components/SelectField/MultiSelectFieldComponent.esm.js +3 -3
- package/dist/components/SessionTimeOut/SessionTimeOut.esm.js +33 -34
- package/dist/node_modules/react-date-range/dist/components/Calendar/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DateInput/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DateRange/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DateRangePicker/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/DayCell/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/InputRangeField/index.esm.js +1 -1
- package/dist/node_modules/react-date-range/dist/components/Month/index.esm.js +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var InputRangeField = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { InputRangeField as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DayCell = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DayCell as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var Month = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { Month as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateInput = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateInput as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateRangePicker = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateRangePicker as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var DateRange = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { DateRange as __exports };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var Calendar = {};
|
|
2
2
|
|
|
3
|
-
export {
|
|
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(
|
|
44
|
-
endDate: getValidDate(
|
|
40
|
+
startDate: getValidDate(fromDateValue),
|
|
41
|
+
endDate: getValidDate(toDateValue),
|
|
45
42
|
key: 'selection',
|
|
46
43
|
},
|
|
47
44
|
]);
|
|
48
|
-
}, [fromDateValue, toDateValue
|
|
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
|
-
|
|
66
|
-
|
|
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 };
|
|
@@ -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
|
-
|
|
26
|
-
|
|
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(!
|
|
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:
|
|
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:
|
|
61
|
+
error: isError,
|
|
63
62
|
sx: {
|
|
64
|
-
'& .MuiFormLabel-root': {
|
|
65
|
-
|
|
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(
|
|
26
|
+
console.error('DateTimePicker component resolved to undefined.');
|
|
36
27
|
}
|
|
37
28
|
setDateTimePicker(() => component);
|
|
38
29
|
}
|
|
39
30
|
catch (err) {
|
|
40
|
-
console.error(
|
|
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
|
-
|
|
39
|
+
// Stable toggle handler
|
|
40
|
+
const handleToggle = useCallback(() => {
|
|
49
41
|
if (!open)
|
|
50
42
|
inputTextRef.current?.blur();
|
|
51
43
|
document.activeElement?.blur();
|
|
52
|
-
setOpen(!
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
}
|
|
90
|
+
},
|
|
91
|
+
desktopPaper: {
|
|
92
|
+
sx: {
|
|
93
|
+
'& .MuiPickersLayout-root': { flexDirection: 'column' },
|
|
102
94
|
},
|
|
103
|
-
}
|
|
104
|
-
|
|
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
|
-
|
|
17
|
-
inputRef.current.value = '';
|
|
23
|
+
if ((watched === null || watched === undefined) && inputRef.current) {
|
|
24
|
+
inputRef.current.value = '';
|
|
18
25
|
}
|
|
19
26
|
}, [watched]);
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
32
|
-
}
|
|
30
|
+
if (!fileType)
|
|
31
|
+
return EXTENSION_MAP.all;
|
|
33
32
|
if (Array.isArray(fileType)) {
|
|
34
|
-
|
|
35
|
-
const hasPredefinedKeys = fileType.some((item) => extensionMap[item]);
|
|
33
|
+
const hasPredefinedKeys = fileType.some((t) => EXTENSION_MAP[t]);
|
|
36
34
|
if (hasPredefinedKeys) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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(
|
|
42
|
+
return Array.from(exts);
|
|
49
43
|
}
|
|
50
|
-
// All items are direct extensions
|
|
51
44
|
return fileType;
|
|
52
45
|
}
|
|
53
|
-
return
|
|
54
|
-
};
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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:
|
|
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 };
|