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,104 +1,89 @@
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 'dayjs';
6
6
  import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
7
7
  import FormBottomField from '../FormBottomField/FormBottomField.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 FormRenderMultiFileUpload = ({ props, variant, }) => {
10
- // const getDocument: Document = document;
11
19
  const inputRef = useRef(null);
12
20
  const watched = useWatch({ control: props.control, name: props.item.name });
13
21
  useEffect(() => {
14
- if (watched === null || watched === undefined) {
15
- if (inputRef.current)
16
- inputRef.current.value = '';
22
+ if ((watched === null || watched === undefined) && inputRef.current) {
23
+ inputRef.current.value = '';
17
24
  }
18
25
  }, [watched]);
19
26
  const isError = !!props.errors?.[props.item.name];
20
- const isShowBorderError = isError;
21
- const extensionMap = {
22
- excel: ['xls', 'xlsx'],
23
- pdf: ['pdf'],
24
- zip: ['zip'],
25
- image: ['jpg', 'jpeg', 'png'],
26
- document: ['doc', 'docx', 'pdf'],
27
- all: ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx', 'zip'],
28
- };
29
- const getValidExtensions = () => {
27
+ // Derived once per fileType change — not on every render
28
+ const validExtensions = useMemo(() => {
30
29
  const fileType = props.item.fileType;
31
- if (!fileType) {
32
- return extensionMap.all;
33
- }
30
+ if (!fileType)
31
+ return EXTENSION_MAP.all;
34
32
  if (Array.isArray(fileType)) {
35
- // Check if array contains predefined keys (like 'image', 'pdf') or actual extensions
36
- const hasPredefinedKeys = fileType.some((item) => extensionMap[item]);
33
+ const hasPredefinedKeys = fileType.some((t) => EXTENSION_MAP[t]);
37
34
  if (hasPredefinedKeys) {
38
- // Merge extensions from all predefined keys
39
- const extensions = new Set();
40
- fileType.forEach((item) => {
41
- if (extensionMap[item]) {
42
- extensionMap[item].forEach((ext) => extensions.add(ext));
43
- }
44
- else {
45
- // Treat as direct extension
46
- extensions.add(item);
47
- }
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);
48
41
  });
49
- return Array.from(extensions);
42
+ return Array.from(exts);
50
43
  }
51
- // All items are direct extensions
52
44
  return fileType;
53
45
  }
54
- return extensionMap[fileType] || extensionMap.all;
55
- };
56
- const getAcceptString = () => {
57
- const extensions = getValidExtensions();
58
- return extensions.map((ext) => `.${ext}`).join(',');
59
- };
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 files = event.target.files;
51
+ const fileName = files && files.length > 0
52
+ ? Array.from(files)
53
+ .map((item) => item.name)
54
+ .join(',')
55
+ : null;
56
+ let fileExtension = null;
57
+ if (fileName) {
58
+ const ext = fileName.split('.').pop();
59
+ if (ext)
60
+ fileExtension = ext.toLowerCase();
61
+ }
62
+ if (props.item?.fileType && fileExtension && !validExtensions.includes(fileExtension)) {
63
+ props.item?.handleFileError?.(`Please upload ${validExtensions.join(', ')} files only`);
64
+ event.target.value = '';
65
+ props.setValue(props.item.name, null);
66
+ props.setValue(props.item.name + 'Name', '');
67
+ return;
68
+ }
69
+ if (files && files[files.length - 1].size > 20000000) {
70
+ props.item?.handleFileError?.('File size should be less than 20MB');
71
+ props.setValue(props.item.name, null);
72
+ props.setValue(props.item.name + 'File', []);
73
+ return;
74
+ }
75
+ const fileArray = Array.from(files);
76
+ props.setValue(props.item.name, fileName);
77
+ props.setValue(props.item.name + 'File', fileArray);
78
+ props.clearErrors(props.item.name);
79
+ }, [validExtensions, props.item, props.setValue, props.clearErrors]);
80
+ 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
81
  multiple: true,
62
- accept: getAcceptString(),
82
+ accept,
63
83
  'aria-labelledby': `${props.item.name}-label`,
64
84
  'aria-describedby': `${props.item.name}-helper ${props.item.name}-error`,
65
85
  'aria-required': props.item.required ? true : undefined,
66
- }, onChange: (event) => {
67
- const file = event.target.files;
68
- const fileName = file && file.length > 0
69
- ? Array.from(file)
70
- .map((item) => item.name)
71
- .join(',')
72
- : null;
73
- let fileExtension = null;
74
- if (fileName) {
75
- const ext = fileName.split('.').pop();
76
- if (ext) {
77
- fileExtension = ext.toLowerCase();
78
- }
79
- }
80
- const validExtensions = getValidExtensions();
81
- if (props.item?.fileType && fileExtension && !validExtensions.includes(fileExtension)) {
82
- props.item?.handleFileError &&
83
- props.item?.handleFileError(`Please upload ${validExtensions.join(', ')} files only`);
84
- event.target.value = ''; // Clear the file input\
85
- props.setValue(props.item?.name, null);
86
- props.setValue(props.item?.name + 'Name', '');
87
- return;
88
- }
89
- else if (event.target.files[event.target.files.length - 1].size > 20000000) {
90
- props.item?.handleFileError &&
91
- props.item?.handleFileError(`File size should be less than 20MB`);
92
- event.target.files = null; // Clear the file input
93
- props.setValue(props.item?.name, null);
94
- props.setValue(props.item?.name + 'File', []);
95
- return;
96
- }
97
- const fileArray = Array.from(file);
98
- props.setValue(props.item?.name, fileName);
99
- props.setValue(props.item?.name + 'File', fileArray);
100
- props.clearErrors(props.item?.name);
101
- }, sx: { width: '100%' } })] }), jsx(FormBottomField, { ...props })] }));
86
+ }, onChange: handleChange, sx: { width: '100%' } })] }), jsx(FormBottomField, { ...props })] }));
102
87
  };
103
88
 
104
89
  export { FormRenderMultiFileUpload as default };
@@ -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 } from 'react';
3
+ import { useRef, useState, useEffect, useMemo, useCallback } from 'react';
4
4
  import { useWatch } from 'react-hook-form';
5
5
  import 'dayjs';
6
6
  import { renderLabel } from '../../../utils/Constants/FormConstants.esm.js';
@@ -15,6 +15,48 @@ import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
15
15
  import TableChartIcon from '@mui/icons-material/TableChart';
16
16
  import DescriptionIcon from '@mui/icons-material/Description';
17
17
 
18
+ // ─── Module-level pure utilities ──────────────────────────────────────────────
19
+ // Defined outside the component so they are never recreated on render.
20
+ const EXTENSION_MAP = {
21
+ excel: ['xls', 'xlsx'],
22
+ pdf: ['pdf'],
23
+ image: ['jpg', 'jpeg', 'png'],
24
+ all: ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx', 'zip'],
25
+ zip: ['zip'],
26
+ document: ['doc', 'docx', 'pdf'],
27
+ };
28
+ const isImageFile = (file) => {
29
+ if (!file)
30
+ return false;
31
+ const ext = typeof file === 'string'
32
+ ? file.split('.').pop()?.toLowerCase()
33
+ : file.name?.split('.').pop()?.toLowerCase();
34
+ return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext || '');
35
+ };
36
+ const isPdfFile = (file) => {
37
+ if (!file)
38
+ return false;
39
+ const name = typeof file === 'string' ? file : (file.name || '');
40
+ return name.toLowerCase().endsWith('.pdf');
41
+ };
42
+ const isExcelFile = (file) => {
43
+ if (!file)
44
+ return false;
45
+ const name = typeof file === 'string' ? file : (file.name || '');
46
+ return /\.xlsx?$/.test(name.toLowerCase());
47
+ };
48
+ const isWordFile = (file) => {
49
+ if (!file)
50
+ return false;
51
+ const name = typeof file === 'string' ? file : (file.name || '');
52
+ return /\.(doc|docx)$/.test(name.toLowerCase());
53
+ };
54
+ const getObjectUrl = (file) => {
55
+ if (!file)
56
+ return '';
57
+ return typeof file === 'string' ? file : URL.createObjectURL(file);
58
+ };
59
+ // ─── Component ────────────────────────────────────────────────────────────────
18
60
  const MultiFileWithPreview = ({ props, variant, }) => {
19
61
  const inputRef = useRef(null);
20
62
  const watched = useWatch({ control: props.control, name: props.item.name });
@@ -22,7 +64,7 @@ const MultiFileWithPreview = ({ props, variant, }) => {
22
64
  const [open, setOpen] = useState(false);
23
65
  const [selectedFiles, setSelectedFiles] = useState([]);
24
66
  const [currentFileIndex, setCurrentFileIndex] = useState(0);
25
- // Clamp preview index when selection changes to avoid undefined access
67
+ // Clamp preview index when selection changes
26
68
  useEffect(() => {
27
69
  if (selectedFiles.length === 0) {
28
70
  setCurrentFileIndex(0);
@@ -35,11 +77,12 @@ const MultiFileWithPreview = ({ props, variant, }) => {
35
77
  setCurrentFileIndex(0);
36
78
  }
37
79
  }, [selectedFiles, currentFileIndex]);
38
- // Determine if this is single or multiple file mode
39
80
  const isSingleFileMode = props.item.maxLength === 1;
40
81
  useEffect(() => {
41
82
  const isWatchedEmpty = watched === null || watched === undefined || watched === '';
42
- const isWatchedFileEmpty = watchedFile === null || watchedFile === undefined || (Array.isArray(watchedFile) && watchedFile.length === 0);
83
+ const isWatchedFileEmpty = watchedFile === null ||
84
+ watchedFile === undefined ||
85
+ (Array.isArray(watchedFile) && watchedFile.length === 0);
43
86
  if (isWatchedEmpty && isWatchedFileEmpty) {
44
87
  if (inputRef.current)
45
88
  inputRef.current.value = '';
@@ -48,38 +91,36 @@ const MultiFileWithPreview = ({ props, variant, }) => {
48
91
  }
49
92
  const looksLikeUrl = (s) => typeof s === 'string' && /^(https?:|blob:|data:|\/)/i.test(s);
50
93
  let files = [];
51
- // Check watchedFile first
52
94
  if (Array.isArray(watchedFile) && watchedFile.length > 0) {
53
- files = watchedFile.map(f => {
95
+ files = watchedFile
96
+ .map((f) => {
54
97
  if (f instanceof File)
55
98
  return f;
56
99
  if (typeof f === 'object' && f !== null) {
57
- const possibleKeys = ['image', 'url', 'path', 'filePath'];
58
- const key = possibleKeys.find((k) => f[k]);
100
+ const key = ['image', 'url', 'path', 'filePath'].find((k) => f[k]);
59
101
  return key ? f[key]?.replace(/\\/g, '/').trim() : undefined;
60
102
  }
61
- if (typeof f === 'string') {
62
- return f.replace(/\\/g, '/').trim(); // trust watchedFile strings directly
63
- }
103
+ if (typeof f === 'string')
104
+ return f.replace(/\\/g, '/').trim();
64
105
  return undefined;
65
- }).filter(Boolean);
106
+ })
107
+ .filter(Boolean);
66
108
  }
67
109
  else if (typeof watchedFile === 'string' && watchedFile.trim() !== '') {
68
- files = watchedFile.split(',').map(s => s.replace(/\\/g, '/').trim()).filter(Boolean);
110
+ files = watchedFile.split(',').map((s) => s.replace(/\\/g, '/').trim()).filter(Boolean);
69
111
  }
70
- // Fallback to watched if files array is empty
71
112
  if (files.length === 0) {
72
113
  if (typeof watched === 'string' && watched.trim() !== '') {
73
- const parts = watched.split(',').map(s => s.replace(/\\/g, '/').trim());
74
- files = parts.filter(p => looksLikeUrl(p));
114
+ const parts = watched.split(',').map((s) => s.replace(/\\/g, '/').trim());
115
+ files = parts.filter(looksLikeUrl);
75
116
  }
76
117
  else if (Array.isArray(watched)) {
77
- files = watched.map(item => {
118
+ files = watched
119
+ .map((item) => {
78
120
  if (item instanceof File)
79
121
  return item;
80
122
  if (typeof item === 'object' && item !== null) {
81
- const possibleKeys = ['image', 'url', 'path', 'filePath'];
82
- const key = possibleKeys.find((k) => item[k]);
123
+ const key = ['image', 'url', 'path', 'filePath'].find((k) => item[k]);
83
124
  return key ? item[key]?.replace(/\\/g, '/').trim() : undefined;
84
125
  }
85
126
  if (typeof item === 'string') {
@@ -87,59 +128,41 @@ const MultiFileWithPreview = ({ props, variant, }) => {
87
128
  return looksLikeUrl(str) ? str : undefined;
88
129
  }
89
130
  return undefined;
90
- }).filter(Boolean);
131
+ })
132
+ .filter(Boolean);
91
133
  }
92
134
  }
93
- if (files.length > 0) {
135
+ if (files.length > 0)
94
136
  setSelectedFiles(files);
95
- }
96
137
  }, [watched, watchedFile]);
97
138
  const isError = !!props.errors?.[props.item.name];
98
- const extensionMap = {
99
- excel: ['xls', 'xlsx'],
100
- pdf: ['pdf'],
101
- image: ['jpg', 'jpeg', 'png'],
102
- all: ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx', 'zip'],
103
- zip: ['zip'],
104
- document: ['doc', 'docx', 'pdf'],
105
- };
106
- const getValidExtensions = () => {
139
+ // Derived once per fileType change
140
+ const validExtensions = useMemo(() => {
107
141
  const fileType = props.item.fileType;
108
- if (!fileType) {
109
- return extensionMap.all;
110
- }
142
+ if (!fileType)
143
+ return EXTENSION_MAP.all;
111
144
  if (Array.isArray(fileType)) {
112
- // Check if array contains predefined keys (like 'image', 'pdf') or actual extensions
113
- const hasPredefinedKeys = fileType.some((item) => extensionMap[item]);
145
+ const hasPredefinedKeys = fileType.some((t) => EXTENSION_MAP[t]);
114
146
  if (hasPredefinedKeys) {
115
- // Merge extensions from all predefined keys
116
- const extensions = new Set();
117
- fileType.forEach((item) => {
118
- if (extensionMap[item]) {
119
- extensionMap[item].forEach((ext) => extensions.add(ext));
120
- }
121
- else {
122
- // Treat as direct extension
123
- extensions.add(item);
124
- }
147
+ const exts = new Set();
148
+ fileType.forEach((t) => {
149
+ if (EXTENSION_MAP[t])
150
+ EXTENSION_MAP[t].forEach((e) => exts.add(e));
151
+ else
152
+ exts.add(t);
125
153
  });
126
- return Array.from(extensions);
154
+ return Array.from(exts);
127
155
  }
128
- // All items are direct extensions
129
156
  return fileType;
130
157
  }
131
- return extensionMap[fileType] || extensionMap.all;
132
- };
133
- const getAcceptString = () => {
134
- const extensions = getValidExtensions();
135
- return extensions.map((ext) => `.${ext}`).join(',');
136
- };
137
- const handleFileChange = (event) => {
158
+ return EXTENSION_MAP[fileType] ?? EXTENSION_MAP.all;
159
+ }, [props.item.fileType]);
160
+ const accept = useMemo(() => validExtensions.map((e) => `.${e}`).join(','), [validExtensions]);
161
+ const handleFileChange = useCallback((event) => {
138
162
  const files = event.target.files;
139
163
  if (!files)
140
164
  return;
141
165
  const fileArray = Array.from(files);
142
- const validExtensions = getValidExtensions();
143
166
  const invalidFile = fileArray.find((file) => {
144
167
  const ext = file.name.split('.').pop()?.toLowerCase();
145
168
  return !ext || !validExtensions.includes(ext);
@@ -159,15 +182,12 @@ const MultiFileWithPreview = ({ props, variant, }) => {
159
182
  props.setValue(props.item.name + 'File', []);
160
183
  return;
161
184
  }
162
- let combinedFiles = [];
185
+ let combinedFiles;
163
186
  if (isSingleFileMode) {
164
- // only 1 file allowed → replace
165
187
  combinedFiles = fileArray;
166
188
  }
167
189
  else {
168
- // append to existing
169
190
  combinedFiles = [...selectedFiles, ...fileArray];
170
- // limit by maxLength
171
191
  if (props.item.maxLength && combinedFiles.length > props.item.maxLength) {
172
192
  props.item?.handleFileError?.(`You can only upload up to ${props.item.maxLength} files`);
173
193
  event.target.value = '';
@@ -182,26 +202,25 @@ const MultiFileWithPreview = ({ props, variant, }) => {
182
202
  props.setValue(props.item.name + 'File', combinedFiles);
183
203
  props.clearErrors(props.item.name);
184
204
  setSelectedFiles(combinedFiles);
185
- setCurrentFileIndex(combinedFiles.length - fileArray.length); // move preview to new
205
+ setCurrentFileIndex(combinedFiles.length - fileArray.length);
186
206
  props.item.onChangeSetFileFn?.(combinedFiles);
187
- };
188
- const handlePreviewClick = () => {
207
+ }, [validExtensions, isSingleFileMode, selectedFiles, props.item, props.setValue, props.clearErrors]);
208
+ const handlePreviewClick = useCallback(() => {
189
209
  setCurrentFileIndex(0);
190
210
  setOpen(true);
191
- };
192
- const handleClose = () => setOpen(false);
193
- const handlePrevious = () => {
194
- setCurrentFileIndex((prev) => prev > 0 ? prev - 1 : selectedFiles.length - 1);
195
- };
196
- const handleNext = () => {
197
- setCurrentFileIndex((prev) => prev < selectedFiles.length - 1 ? prev + 1 : 0);
198
- };
199
- const handleDeleteFile = (indexToDelete) => {
200
- const updatedFiles = selectedFiles.filter((_, index) => index !== indexToDelete);
211
+ }, []);
212
+ const handleClose = useCallback(() => setOpen(false), []);
213
+ const handlePrevious = useCallback(() => {
214
+ setCurrentFileIndex((prev) => (prev > 0 ? prev - 1 : selectedFiles.length - 1));
215
+ }, [selectedFiles.length]);
216
+ const handleNext = useCallback(() => {
217
+ setCurrentFileIndex((prev) => (prev < selectedFiles.length - 1 ? prev + 1 : 0));
218
+ }, [selectedFiles.length]);
219
+ const handleDeleteFile = useCallback((indexToDelete) => {
220
+ const updatedFiles = selectedFiles.filter((_, i) => i !== indexToDelete);
201
221
  setSelectedFiles(updatedFiles);
202
- if (inputRef.current) {
222
+ if (inputRef.current)
203
223
  inputRef.current.value = '';
204
- }
205
224
  if (updatedFiles.length === 0) {
206
225
  props.setValue(props.item.name, null);
207
226
  props.setValue(props.item.name + 'File', []);
@@ -216,59 +235,21 @@ const MultiFileWithPreview = ({ props, variant, }) => {
216
235
  if (currentFileIndex >= updatedFiles.length) {
217
236
  setCurrentFileIndex(Math.max(0, updatedFiles.length - 1));
218
237
  }
219
- props.item.onChangeSetFileFn &&
220
- props.item.onChangeSetFileFn(updatedFiles);
221
- if (updatedFiles.length === 0) {
238
+ props.item.onChangeSetFileFn?.(updatedFiles);
239
+ if (updatedFiles.length === 0)
222
240
  setOpen(false);
223
- }
224
- };
225
- const isImageFile = (file) => {
226
- if (!file)
227
- return false;
228
- const ext = typeof file === 'string'
229
- ? file.split('.').pop()?.toLowerCase()
230
- : file.name?.split('.').pop()?.toLowerCase();
231
- return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext || '');
232
- };
233
- const isPdfFile = (file) => {
234
- if (!file)
235
- return false;
236
- const name = typeof file === 'string' ? file : (file.name || '');
237
- return name.toLowerCase().endsWith('.pdf');
238
- };
239
- const isExcelFile = (file) => {
240
- if (!file)
241
- return false;
242
- const name = typeof file === 'string' ? file : (file.name || '');
243
- return /\.xlsx?$/.test(name.toLowerCase());
244
- };
245
- const isWordFile = (file) => {
246
- if (!file)
247
- return false;
248
- const name = typeof file === 'string' ? file : (file.name || '');
249
- return /\.(doc|docx)$/.test(name.toLowerCase());
250
- };
251
- const getObjectUrl = (file) => {
252
- if (!file)
253
- return '';
254
- return typeof file === 'string' ? file : URL.createObjectURL(file);
255
- };
241
+ }, [selectedFiles, currentFileIndex, props.item, props.setValue]);
256
242
  return (jsxs(Fragment, { children: [jsxs(Box, { paddingLeft: "4px", sx: { ...props.item.sx, position: 'relative' }, children: [props.item?.label && (jsx(Box, { sx: { fontSize: '10px' }, children: renderLabel(variant, props, true) })), jsxs(Box, { display: "flex", alignItems: "center", gap: 1, children: [jsxs(Button, { variant: "outlined", component: "label", startIcon: jsx(UploadFileIcon, {}), sx: { flex: 1, textTransform: 'none' }, color: isError ? 'error' : 'primary', children: [selectedFiles.length > 0
257
243
  ? isSingleFileMode
258
244
  ? '1 file selected'
259
245
  : `${selectedFiles.length} file(s) selected`
260
246
  : isSingleFileMode
261
247
  ? 'Upload File'
262
- : 'Upload Files', jsx("input", { type: "file", hidden: true, multiple: !isSingleFileMode, accept: getAcceptString(), ref: inputRef, onChange: handleFileChange })] }), jsx(Box, { sx: { width: 36, display: 'flex', justifyContent: 'center' }, children: props.item.showFileCarousel && selectedFiles.length > 0 ? (jsx(IconButton, { onClick: handlePreviewClick, color: "primary", size: "small", "aria-label": "preview selected files", children: jsx(VisibilityIcon, {}) })) : null })] })] }), jsx(FormBottomField, { ...props }), jsx(Modal, { open: open, onClose: handleClose, children: jsxs(Box, { sx: {
263
- position: 'absolute',
264
- top: '50%',
265
- left: '50%',
248
+ : 'Upload Files', jsx("input", { type: "file", hidden: true, multiple: !isSingleFileMode, accept: accept, ref: inputRef, onChange: handleFileChange })] }), jsx(Box, { sx: { width: 36, display: 'flex', justifyContent: 'center' }, children: props.item.showFileCarousel && selectedFiles.length > 0 ? (jsx(IconButton, { onClick: handlePreviewClick, color: "primary", size: "small", "aria-label": "preview selected files", children: jsx(VisibilityIcon, {}) })) : null })] })] }), jsx(FormBottomField, { ...props }), jsx(Modal, { open: open, onClose: handleClose, children: jsxs(Box, { sx: {
249
+ position: 'absolute', top: '50%', left: '50%',
266
250
  transform: 'translate(-50%, -50%)',
267
- width: 600,
268
- bgcolor: 'background.paper',
269
- borderRadius: 2,
270
- boxShadow: 24,
271
- p: 3,
251
+ width: 600, bgcolor: 'background.paper',
252
+ borderRadius: 2, boxShadow: 24, p: 3,
272
253
  }, children: [jsxs(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", children: [jsx(Typography, { variant: "h6", children: "File Preview" }), jsxs(Box, { display: "flex", gap: 1, children: [jsx(IconButton, { onClick: () => handleDeleteFile(currentFileIndex), color: "error", title: "Delete current file", children: jsx(DeleteIcon, {}) }), jsx(IconButton, { onClick: handleClose, children: jsx(CloseIcon, {}) })] })] }), jsx(Box, { mt: 2, children: selectedFiles.length > 0 && (jsxs(Fragment, { children: [jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "center", gap: 2, children: [selectedFiles.length > 1 && !isSingleFileMode ? (jsx(IconButton, { onClick: handlePrevious, children: jsx(NavigateBeforeIcon, {}) })) : null, jsx(Box, { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", sx: { p: 2, minHeight: '400px', width: '100%' }, children: (() => {
273
254
  const currentFile = selectedFiles[currentFileIndex];
274
255
  if (!currentFile) {
@@ -277,31 +258,21 @@ const MultiFileWithPreview = ({ props, variant, }) => {
277
258
  const fileName = typeof currentFile === 'string'
278
259
  ? currentFile.split('/').pop() || currentFile
279
260
  : (currentFile.name || '');
280
- // Images
281
261
  if (isImageFile(currentFile)) {
282
- return (jsx("img", { alt: "Preview", src: getObjectUrl(currentFile), style: {
283
- maxHeight: '400px',
284
- maxWidth: '100%',
285
- objectFit: 'contain',
286
- borderRadius: 8,
287
- } }));
262
+ return (jsx("img", { alt: "Preview", src: getObjectUrl(currentFile), style: { maxHeight: '400px', maxWidth: '100%', objectFit: 'contain', borderRadius: 8 } }));
288
263
  }
289
- // PDF inline viewer
290
264
  if (isPdfFile(currentFile)) {
291
265
  const url = getObjectUrl(currentFile);
292
- return (jsx(Box, { width: "100%", height: 420, children: jsx("object", { data: url, type: "application/pdf", width: "100%", height: "100%", children: jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column", gap: 1, children: [jsx(PictureAsPdfIcon, { color: "error" }), jsx(Typography, { variant: "body2", children: "Preview not available. " }), jsx(Button, { size: "small", variant: "outlined", href: url, target: "_blank", rel: "noreferrer", children: "Open PDF" })] }) }) }));
266
+ return (jsx(Box, { width: "100%", height: 420, children: jsx("object", { data: url, type: "application/pdf", width: "100%", height: "100%", children: jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column", gap: 1, children: [jsx(PictureAsPdfIcon, { color: "error" }), jsx(Typography, { variant: "body2", children: "Preview not available." }), jsx(Button, { size: "small", variant: "outlined", href: url, target: "_blank", rel: "noreferrer", children: "Open PDF" })] }) }) }));
293
267
  }
294
- // Excel preview placeholder with actions
295
268
  if (isExcelFile(currentFile)) {
296
269
  const url = getObjectUrl(currentFile);
297
270
  return (jsxs(Box, { textAlign: "center", children: [jsx(TableChartIcon, { color: "primary", sx: { fontSize: 64, mb: 1 } }), jsx(Typography, { variant: "body1", gutterBottom: true, children: fileName }), jsx(Button, { size: "small", variant: "outlined", href: url, target: "_blank", rel: "noreferrer", children: "Open" })] }));
298
271
  }
299
- // Word preview placeholder with actions
300
272
  if (isWordFile(currentFile)) {
301
273
  const url = getObjectUrl(currentFile);
302
274
  return (jsxs(Box, { textAlign: "center", children: [jsx(DescriptionIcon, { color: "primary", sx: { fontSize: 64, mb: 1 } }), jsx(Typography, { variant: "body1", gutterBottom: true, children: fileName }), jsx(Button, { size: "small", variant: "outlined", href: url, target: "_blank", rel: "noreferrer", children: "Open" })] }));
303
275
  }
304
- // Fallback: show name
305
276
  return jsx(Typography, { variant: "body1", children: fileName });
306
277
  })() }), selectedFiles.length > 1 && !isSingleFileMode ? (jsx(IconButton, { onClick: handleNext, children: jsx(NavigateNextIcon, {}) })) : null] }), selectedFiles.length > 1 && !isSingleFileMode && (jsx(Box, { display: "flex", justifyContent: "center", mt: 2, children: jsxs(Typography, { variant: "body2", color: "text.secondary", children: [currentFileIndex + 1, " of ", selectedFiles.length] }) }))] })) })] }) })] }));
307
278
  };
@@ -1,44 +1,6 @@
1
1
  import { FormRenderProps, VariantProps } from '../../../utils';
2
- /**
3
- * FormNumberField Component
4
- *
5
- * A numeric input field component that integrates with react-hook-form.
6
- * This component is used for integer inputs, phone numbers, and pincodes.
7
- * It restricts input to numeric characters only.
8
- *
9
- * @component
10
- * @param {Object} props - The component props
11
- * @param {FormRenderProps} props.props - Form rendering properties from parent form
12
- * @param {VariantProps} props.variant - UI variant style ('standard', 'outlined', etc.)
13
- *
14
- * @example
15
- * // Basic usage in FormRender
16
- * <FormNumberField props={formRenderProps} variant="outlined" />
17
- *
18
- * @example
19
- * // Usage with specific configuration
20
- * const formItem = {
21
- * name: "phoneNumber",
22
- * label: "Phone Number",
23
- * inputType: "number",
24
- * required: true,
25
- * maxLength: 10
26
- * };
27
- * <FormNumberField
28
- * props={{
29
- * item: formItem,
30
- * control: formMethods.control,
31
- * errors: formMethods.formState.errors
32
- * }}
33
- * variant="outlined"
34
- * />
35
- */
36
2
  declare const FormNumberField: ({ props, variant }: {
37
3
  props: FormRenderProps;
38
4
  variant: VariantProps;
39
5
  }) => import("react/jsx-runtime").JSX.Element;
40
- /**
41
- * Export the FormNumberField component for use in the form renderer
42
- * This component is used for number, pincode, and phoneNumber input types
43
- */
44
6
  export default FormNumberField;