formik-form-components 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +1 -21
  2. package/dist/Form/AppAutoCompleter.d.ts +1 -2
  3. package/dist/Form/AppAutoCompleter.d.ts.map +1 -1
  4. package/dist/Form/AppCheckBox.d.ts +1 -1
  5. package/dist/Form/AppCheckBox.d.ts.map +1 -1
  6. package/dist/Form/AppDateAndTimePicker.d.ts +1 -1
  7. package/dist/Form/AppDateAndTimePicker.d.ts.map +1 -1
  8. package/dist/Form/AppFormErrorMessage.d.ts +1 -1
  9. package/dist/Form/AppFormErrorMessage.d.ts.map +1 -1
  10. package/dist/Form/AppInputField.d.ts +1 -1
  11. package/dist/Form/AppInputField.d.ts.map +1 -1
  12. package/dist/Form/AppMultiSelector.d.ts +1 -1
  13. package/dist/Form/AppMultiSelector.d.ts.map +1 -1
  14. package/dist/Form/AppPhoneNoInput.d.ts +1 -1
  15. package/dist/Form/AppPhoneNoInput.d.ts.map +1 -1
  16. package/dist/Form/AppRadioGroup.d.ts +1 -1
  17. package/dist/Form/AppRadioGroup.d.ts.map +1 -1
  18. package/dist/Form/AppRating.d.ts +1 -1
  19. package/dist/Form/AppRating.d.ts.map +1 -1
  20. package/dist/Form/AppSimpleUploadFile.d.ts +1 -1
  21. package/dist/Form/AppSimpleUploadFile.d.ts.map +1 -1
  22. package/dist/Form/AppSwitch.d.ts +1 -2
  23. package/dist/Form/AppSwitch.d.ts.map +1 -1
  24. package/dist/Form/AppTagsCreator.d.ts +1 -2
  25. package/dist/Form/AppTagsCreator.d.ts.map +1 -1
  26. package/dist/Form/AppTextArea.d.ts +1 -1
  27. package/dist/Form/AppTextArea.d.ts.map +1 -1
  28. package/dist/Form/AppUploadFile.d.ts +1 -1
  29. package/dist/Form/AppUploadFile.d.ts.map +1 -1
  30. package/dist/Form/SubmitButton.d.ts +1 -1
  31. package/dist/Form/SubmitButton.d.ts.map +1 -1
  32. package/dist/index.d.ts +17 -0
  33. package/dist/index.esm.js +2 -1
  34. package/dist/index.js +2 -1
  35. package/dist/lib/index.d.ts +17 -0
  36. package/dist/lib/index.d.ts.map +1 -1
  37. package/package.json +16 -22
  38. package/src/App.css +0 -38
  39. package/src/App.test.tsx +0 -9
  40. package/src/App.tsx +0 -166
  41. package/src/Form/AppAutoCompleter.tsx +0 -252
  42. package/src/Form/AppCheckBox.tsx +0 -101
  43. package/src/Form/AppDateAndTimePicker.tsx +0 -94
  44. package/src/Form/AppDatePicker.tsx +0 -69
  45. package/src/Form/AppFormErrorMessage.tsx +0 -34
  46. package/src/Form/AppInputField.tsx +0 -80
  47. package/src/Form/AppMultiSelector.tsx +0 -163
  48. package/src/Form/AppPhoneNoInput.tsx +0 -106
  49. package/src/Form/AppRadioGroup.tsx +0 -92
  50. package/src/Form/AppRating.tsx +0 -98
  51. package/src/Form/AppSelectInput.tsx +0 -249
  52. package/src/Form/AppSimpleUploadFile.tsx +0 -154
  53. package/src/Form/AppSwitch.tsx +0 -84
  54. package/src/Form/AppTagsCreator.tsx +0 -252
  55. package/src/Form/AppTextArea.tsx +0 -90
  56. package/src/Form/AppUploadFile.tsx +0 -167
  57. package/src/Form/SubmitButton.tsx +0 -122
  58. package/src/Form/index.tsx +0 -27
  59. package/src/assets/illustrations/BackgroundIllustration.tsx +0 -42
  60. package/src/assets/illustrations/UploadIllustration.tsx +0 -659
  61. package/src/assets/illustrations/index.ts +0 -1
  62. package/src/file-thumbnail/types.ts +0 -7
  63. package/src/file-thumbnail/utils.ts +0 -162
  64. package/src/index.css +0 -9
  65. package/src/index.tsx +0 -19
  66. package/src/lib/index.ts +0 -47
  67. package/src/logo.svg +0 -1
  68. package/src/react-app-env.d.ts +0 -1
  69. package/src/reportWebVitals.ts +0 -15
  70. package/src/setupTests.ts +0 -5
  71. package/src/styles/PhoneInputCustom.css +0 -238
  72. package/src/styles/compiled-tailwind.css +0 -1
  73. package/src/upload/Upload.tsx +0 -162
  74. package/src/upload/errors/RejectionFiles.tsx +0 -49
  75. package/src/upload/index.ts +0 -5
  76. package/src/upload/preview/MultiFilePreview.tsx +0 -297
  77. package/src/upload/preview/SingleFilePreview.tsx +0 -81
  78. package/src/upload/types.ts +0 -51
@@ -1,162 +0,0 @@
1
- 'use client';
2
-
3
- import React from "react";
4
- import { useDropzone } from "react-dropzone";
5
- import { XMarkIcon } from "@heroicons/react/20/solid";
6
- import { UploadIllustration } from "../assets/illustrations";
7
- import RejectionFiles from "./errors/RejectionFiles";
8
- import MultiFilePreview from "./preview/MultiFilePreview";
9
- import SingleFilePreview from "./preview/SingleFilePreview";
10
- import type { UploadProps } from "./types";
11
-
12
- interface StyledDropZoneProps {
13
- isDragActive: boolean;
14
- isError: boolean;
15
- disabled?: boolean;
16
- hasFile: boolean;
17
- children: React.ReactNode;
18
- [key: string]: any;
19
- }
20
-
21
- const StyledDropZone: React.FC<StyledDropZoneProps> = ({
22
- isDragActive,
23
- isError,
24
- disabled,
25
- hasFile,
26
- children,
27
- ...props
28
- }) => (
29
- <div
30
- className={`
31
- outline-none cursor-pointer overflow-hidden relative rounded-lg transition-all
32
- ${isDragActive ? "opacity-80" : ""}
33
- ${
34
- isError
35
- ? "border-2 border-red-500 bg-red-50"
36
- : "border border-dashed border-gray-300"
37
- }
38
- ${disabled ? "opacity-50 pointer-events-none" : ""}
39
- ${hasFile ? "p-12" : "p-8"}
40
- hover:opacity-80
41
- `}
42
- {...props}
43
- >
44
- {children}
45
- </div>
46
- );
47
-
48
- const Placeholder = () => (
49
- <div className="grid place-items-center min-h-[200px] w-full">
50
- <div className="flex flex-col md:flex-row items-center gap-8 text-center md:text-left">
51
- <div className="w-48">
52
- <UploadIllustration />
53
- </div>
54
- <div>
55
- <h5 className="text-lg font-medium mb-2">Drop or Select file</h5>
56
- <p className="text-gray-500">
57
- Drop files here or click{" "}
58
- <span className="underline mx-0.5">browse</span> through your machine
59
- </p>
60
- </div>
61
- </div>
62
- </div>
63
- );
64
-
65
- const Upload: React.FC<UploadProps> = ({
66
- disabled,
67
- multiple = false,
68
- error,
69
- helperText,
70
- file,
71
- onDelete,
72
- files,
73
- thumbnail,
74
- onUpload,
75
- onRemove,
76
- onRemoveAll,
77
- className = "",
78
- ...other
79
- }) => {
80
- const {
81
- getRootProps,
82
- getInputProps,
83
- isDragActive,
84
- isDragReject,
85
- fileRejections,
86
- } = useDropzone({
87
- multiple,
88
- disabled,
89
- ...other,
90
- });
91
-
92
- const hasFile = !!file && !multiple;
93
- const hasFiles = files && multiple && files.length > 0;
94
- const isError = isDragReject || !!error;
95
-
96
- return (
97
- <div className={`w-full relative ${className}`}>
98
- <StyledDropZone
99
- {...getRootProps()}
100
- isDragActive={isDragActive}
101
- isError={isError}
102
- disabled={disabled}
103
- hasFile={hasFile}
104
- >
105
- <input {...getInputProps()} />
106
-
107
- {!hasFile && <Placeholder />}
108
- {hasFile && <SingleFilePreview file={file} />}
109
- </StyledDropZone>
110
-
111
- {helperText && <p className="mt-2 text-sm text-gray-500">{helperText}</p>}
112
-
113
- <RejectionFiles fileRejections={fileRejections} />
114
-
115
- {hasFile && onDelete && (
116
- <button
117
- type="button"
118
- onClick={onDelete}
119
- className="absolute top-4 right-4 z-10 p-1.5 rounded-full bg-black/70 text-white hover:bg-black/50 transition-colors"
120
- >
121
- <XMarkIcon className="w-4 h-4" />
122
- </button>
123
- )}
124
-
125
- {hasFiles && (
126
- <>
127
- <div className="my-6">
128
- <MultiFilePreview
129
- files={files}
130
- thumbnail={thumbnail}
131
- onRemove={onRemove}
132
- />
133
- </div>
134
-
135
- <div className="flex justify-end gap-3">
136
- {onRemoveAll && (
137
- <button
138
- type="button"
139
- onClick={onRemoveAll}
140
- className="px-4 py-1.5 text-sm border rounded-md hover:bg-gray-50"
141
- >
142
- Remove all
143
- </button>
144
- )}
145
-
146
- {onUpload && (
147
- <button
148
- type="button"
149
- onClick={onUpload}
150
- className="px-4 py-1.5 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700"
151
- >
152
- Upload files
153
- </button>
154
- )}
155
- </div>
156
- </>
157
- )}
158
- </div>
159
- );
160
- };
161
-
162
- export default Upload;
@@ -1,49 +0,0 @@
1
- 'use client';
2
-
3
- import type { FileRejection } from "react-dropzone";
4
- import { fileData } from "../../file-thumbnail/utils";
5
-
6
- // ----------------------------------------------------------------------
7
-
8
- type Props = {
9
- fileRejections: readonly FileRejection[];
10
- };
11
-
12
- export default function RejectionFiles({
13
- fileRejections,
14
- }: Props): React.JSX.Element | null {
15
- if (!fileRejections.length) {
16
- return null;
17
- }
18
-
19
- function formatBytes(bytes?: number): string {
20
- const b = bytes ?? 0;
21
- if (b === 0) return "0 Bytes";
22
- const k = 1024;
23
- const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
24
- const i = Math.floor(Math.log(b) / Math.log(k));
25
- return `${parseFloat((b / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
26
- }
27
-
28
- return (
29
- <div className="mt-3 rounded-lg border border-red-200 bg-red-50 p-2">
30
- {fileRejections.map(({ file, errors }) => {
31
- const { path, size } = fileData(file);
32
-
33
- return (
34
- <div key={path} className="my-1">
35
- <p className="truncate text-sm font-medium">
36
- {path} - {size != null ? formatBytes(size) : ""}
37
- </p>
38
-
39
- {errors.map((error) => (
40
- <span key={error.code} className="block text-xs text-red-600">
41
- - {error.message}
42
- </span>
43
- ))}
44
- </div>
45
- );
46
- })}
47
- </div>
48
- );
49
- }
@@ -1,5 +0,0 @@
1
- export * from "./types";
2
- export { default as RejectionFiles } from "./errors/RejectionFiles";
3
- export { default as MultiFilePreview } from "./preview/MultiFilePreview";
4
- export { default as SingleFilePreview } from "./preview/SingleFilePreview";
5
- export { default as Upload } from "./Upload";
@@ -1,297 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useRef, Key } from "react";
4
- import { AnimatePresence, motion } from "framer-motion";
5
- import type { UploadProps } from "../types";
6
- import { formatFileSize } from "../../file-thumbnail/utils";
7
-
8
- export interface FileItem extends File {
9
- id?: number | string;
10
- preview?: string;
11
- url?: string;
12
- is_private?: boolean;
13
- [key: string]: unknown;
14
- }
15
-
16
- export default function MultiFilePreview({
17
- thumbnail,
18
- files,
19
- onRemove,
20
- className = "",
21
- isClickable,
22
- isEditable,
23
- onDeleteButtonClick,
24
- onPrivacyUpdateClick,
25
- }: UploadProps): React.JSX.Element | null {
26
- const [menuAnchor, setMenuAnchor] = useState<null | HTMLElement>(null);
27
- const [selectedFile, setSelectedFile] = useState<FileItem | null>(null);
28
- const menuRef = useRef<HTMLDivElement>(null);
29
- const canViewPrivate = true;
30
-
31
- if (files?.length == null) {
32
- return null;
33
- }
34
-
35
- const handleMenuClose = () => {
36
- setMenuAnchor(null);
37
- setSelectedFile(null);
38
- };
39
-
40
- const getFileThumbnail = (file: FileItem) => {
41
- const typedFile = file as FileItem;
42
- const imgUrl = typedFile.preview;
43
-
44
- // Check if it's an image (following SingleFilePreview logic)
45
- const isImage =
46
- typedFile.type?.startsWith("image/") ||
47
- /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(typedFile.name || "");
48
-
49
- if (imgUrl && isImage) {
50
- if (typedFile.is_private && !canViewPrivate) {
51
- return (
52
- <svg
53
- className="w-full h-full text-gray-400"
54
- xmlns="http://www.w3.org/2000/svg"
55
- viewBox="0 0 20 20"
56
- fill="currentColor"
57
- >
58
- <path
59
- fillRule="evenodd"
60
- d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
61
- clipRule="evenodd"
62
- />
63
- </svg>
64
- );
65
- }
66
- return (
67
- <img
68
- src={imgUrl}
69
- alt={typedFile.name || ""}
70
- className="w-full h-full object-cover"
71
- />
72
- );
73
- }
74
-
75
- // Non-image files - use SingleFilePreview style icons
76
- return (
77
- <div className="w-full h-full flex flex-col items-center justify-center bg-gray-50 p-2 text-center">
78
- <svg
79
- xmlns="http://www.w3.org/2000/svg"
80
- className="h-8 w-8 text-gray-400 mb-1 flex-shrink-0"
81
- fill="none"
82
- viewBox="0 0 24 24"
83
- stroke="currentColor"
84
- strokeWidth={2}
85
- >
86
- <path
87
- strokeLinecap="round"
88
- strokeLinejoin="round"
89
- d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
90
- />
91
- </svg>
92
- <p className="text-xs font-medium text-gray-900 truncate max-w-[80px]">
93
- {typedFile.name}
94
- </p>
95
- </div>
96
- );
97
- };
98
-
99
- return (
100
- <div className={`space-y-2 ${className}`}>
101
- <AnimatePresence>
102
- {files.map((file) => {
103
- const typedFile = file as FileItem;
104
- const { name = "", size = 0 } = typedFile;
105
- const isStringFile = typeof file === "string";
106
- const itemKey = (typedFile.id ||
107
- typedFile.name ||
108
- Math.random().toString(36).substr(2, 9)) as Key;
109
-
110
- if (thumbnail) {
111
- return (
112
- <motion.div
113
- key={itemKey}
114
- initial={{ opacity: 0, y: 20 }}
115
- animate={{ opacity: 1, y: 0 }}
116
- exit={{ opacity: 0, scale: 0.95 }}
117
- className="relative inline-block m-1 w-20 h-20 rounded-lg overflow-hidden border border-gray-200 bg-gray-50"
118
- >
119
- <div className="w-full h-full flex items-center justify-center">
120
- {getFileThumbnail(typedFile)}
121
- </div>
122
- {onRemove && (
123
- <button
124
- type="button"
125
- onClick={(e) => {
126
- e.stopPropagation();
127
- onRemove(typedFile);
128
- }}
129
- className="absolute top-1 right-1 p-1 rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors"
130
- >
131
- <svg
132
- xmlns="http://www.w3.org/2000/svg"
133
- className="h-3 w-3"
134
- viewBox="0 0 20 20"
135
- fill="currentColor"
136
- >
137
- <path
138
- fillRule="evenodd"
139
- d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
140
- clipRule="evenodd"
141
- />
142
- </svg>
143
- </button>
144
- )}
145
- </motion.div>
146
- );
147
- }
148
-
149
- return (
150
- <motion.div
151
- key={itemKey}
152
- initial={{ opacity: 0, y: 20 }}
153
- animate={{ opacity: 1, y: 0 }}
154
- exit={{ opacity: 0, scale: 0.95 }}
155
- className="flex items-center p-3 rounded-lg border border-gray-200 hover:bg-gray-50 transition-colors"
156
- >
157
- {/* List view - SingleFilePreview inspired thumbnail */}
158
- <div className="w-14 h-14 flex-shrink-0 mr-3 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 flex items-center justify-center">
159
- {getFileThumbnail(typedFile)}
160
- </div>
161
-
162
- <div
163
- className="flex-grow min-w-0"
164
- onClick={() => {
165
- if (isClickable && typedFile.url) {
166
- window.open(typedFile.url, "_blank");
167
- }
168
- }}
169
- >
170
- <p className="text-sm font-medium text-gray-900 truncate">
171
- {isStringFile ? file : name}
172
- </p>
173
- {!isStringFile && (
174
- <p className="text-xs text-gray-500">
175
- {formatFileSize(size)}
176
- </p>
177
- )}
178
- </div>
179
-
180
- <div className="flex items-center space-x-1">
181
- {onRemove && (
182
- <button
183
- type="button"
184
- onClick={(e) => {
185
- e.stopPropagation();
186
- onRemove(typedFile);
187
- }}
188
- className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded transition-all"
189
- >
190
- <svg
191
- xmlns="http://www.w3.org/2000/svg"
192
- className="h-4 w-4"
193
- viewBox="0 0 20 20"
194
- fill="currentColor"
195
- >
196
- <path
197
- fillRule="evenodd"
198
- d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
199
- clipRule="evenodd"
200
- />
201
- </svg>
202
- </button>
203
- )}
204
-
205
- {isEditable && (
206
- <div className="relative" ref={menuRef}>
207
- <button
208
- type="button"
209
- onClick={(e) => {
210
- e.stopPropagation();
211
- setMenuAnchor(menuRef.current);
212
- setSelectedFile(typedFile);
213
- }}
214
- className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded transition-all"
215
- >
216
- <svg
217
- xmlns="http://www.w3.org/2000/svg"
218
- className="h-4 w-4"
219
- viewBox="0 0 20 20"
220
- fill="currentColor"
221
- >
222
- <path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
223
- </svg>
224
- </button>
225
-
226
- {menuAnchor && selectedFile === typedFile && (
227
- <div className="absolute right-0 z-20 mt-2 w-48 bg-white rounded-lg shadow-xl border ring-1 ring-black ring-opacity-5 py-1 origin-top-right">
228
- <button
229
- onClick={(e) => {
230
- e.stopPropagation();
231
- if (onDeleteButtonClick) {
232
- onDeleteButtonClick(typedFile);
233
- }
234
- handleMenuClose();
235
- }}
236
- className="flex w-full items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors"
237
- >
238
- <svg
239
- xmlns="http://www.w3.org/2000/svg"
240
- className="mr-3 h-4 w-4"
241
- fill="none"
242
- viewBox="0 0 24 24"
243
- stroke="currentColor"
244
- >
245
- <path
246
- strokeLinecap="round"
247
- strokeLinejoin="round"
248
- strokeWidth={2}
249
- d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
250
- />
251
- </svg>
252
- Delete
253
- </button>
254
- <button
255
- onClick={(e) => {
256
- e.stopPropagation();
257
- if (onPrivacyUpdateClick) {
258
- onPrivacyUpdateClick(typedFile);
259
- }
260
- handleMenuClose();
261
- }}
262
- className="flex w-full items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors"
263
- >
264
- <svg
265
- xmlns="http://www.w3.org/2000/svg"
266
- className="mr-3 h-4 w-4"
267
- fill="none"
268
- viewBox="0 0 24 24"
269
- stroke="currentColor"
270
- >
271
- <path
272
- strokeLinecap="round"
273
- strokeLinejoin="round"
274
- strokeWidth={2}
275
- d={
276
- typedFile?.is_private
277
- ? "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
278
- : "M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z"
279
- }
280
- />
281
- </svg>
282
- {typedFile?.is_private
283
- ? "Make Public"
284
- : "Make Private"}
285
- </button>
286
- </div>
287
- )}
288
- </div>
289
- )}
290
- </div>
291
- </motion.div>
292
- );
293
- })}
294
- </AnimatePresence>
295
- </div>
296
- );
297
- }
@@ -1,81 +0,0 @@
1
- 'use client';
2
-
3
- import { formatFileSize } from "../../file-thumbnail/utils";
4
- import type { CustomFile } from "../types";
5
-
6
- // ----------------------------------------------------------------------
7
-
8
- type Props = {
9
- file: CustomFile | string | null;
10
- className?: string;
11
- };
12
-
13
- export default function SingleFilePreview({
14
- file,
15
- className = "",
16
- }: Props): React.JSX.Element | null {
17
- if (!file) {
18
- return null;
19
- }
20
-
21
- const imgUrl = typeof file === "string" ? file : file.preview;
22
- const isImage =
23
- typeof file === "string"
24
- ? /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file)
25
- : file.type?.startsWith("image/");
26
-
27
- const fileName =
28
- typeof file === "string" ? file.split("/").pop() : file.name || "file";
29
-
30
- const fileSize =
31
- typeof file === "string"
32
- ? null
33
- : file.size
34
- ? formatFileSize(file.size)
35
- : null;
36
-
37
- if (isImage && imgUrl) {
38
- return (
39
- <div
40
- className={`relative w-full h-full flex items-center justify-center ${className}`}
41
- >
42
- <div className="relative max-w-md w-full p-2">
43
- <div className="aspect-square w-full flex items-center justify-center bg-gray-50 rounded-lg border border-gray-200 overflow-hidden">
44
- <img
45
- src={imgUrl}
46
- alt={fileName}
47
- className="max-w-full max-h-[200px] w-auto h-auto object-contain"
48
- style={{
49
- backgroundColor: "var(--color-bg-paper)",
50
- }}
51
- />
52
- </div>
53
- </div>
54
- </div>
55
- );
56
- }
57
-
58
- // For non-image files, show a file icon with name
59
- return (
60
- <div
61
- className={`w-full h-full p-4 flex flex-col items-center justify-center text-center ${className}`}
62
- >
63
- <svg
64
- xmlns="http://www.w3.org/2000/svg"
65
- className="h-10 w-10 text-gray-400 mb-2"
66
- fill="none"
67
- viewBox="0 0 24 24"
68
- stroke="currentColor"
69
- >
70
- <path
71
- strokeLinecap="round"
72
- strokeLinejoin="round"
73
- strokeWidth={2}
74
- d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
75
- />
76
- </svg>
77
- <p className="text-sm font-medium truncate w-full">{fileName}</p>
78
- {fileSize && <p className="text-xs text-gray-500">{fileSize}</p>}
79
- </div>
80
- );
81
- }
@@ -1,51 +0,0 @@
1
- import type { DropzoneOptions, FileRejection, DropEvent } from "react-dropzone";
2
- import type { FileItem } from "./preview/MultiFilePreview";
3
-
4
- // ----------------------------------------------------------------------
5
-
6
- export interface CustomFile extends File {
7
- path?: string;
8
- preview?: string;
9
- lastModifiedDate?: Date;
10
- url?: string;
11
- name: string;
12
- }
13
-
14
- export interface UploadProps extends Omit<DropzoneOptions, "onDrop"> {
15
- error?: boolean;
16
- className?: string;
17
- style?: React.CSSProperties;
18
- thumbnail?: boolean;
19
- isClickable?: boolean;
20
- placeholder?: React.ReactNode;
21
- helperText?: React.ReactNode;
22
- disableMultiple?: boolean;
23
- isEditable?: boolean;
24
- onDeleteButtonClick?: (file: FileItem) => void;
25
- onPrivacyUpdateClick?: (file: FileItem) => void;
26
- // File handling
27
- file?: CustomFile | string | File | null;
28
- onDelete?: () => void;
29
- // Multiple files
30
- files?: (File | string | { name: string; url: string; preview?: string })[];
31
- onUpload?: () => void;
32
- onRemove?: (file: CustomFile | string) => void;
33
- onRemoveAll?: () => void;
34
- // Drop handler - using FileRejection from react-dropzone
35
- onDrop?: <T extends File>(
36
- acceptedFiles: T[],
37
- fileRejections: FileRejection[],
38
- event: DropEvent
39
- ) => void;
40
- }
41
-
42
- // Helper type for file with preview
43
- export interface FileWithPreview extends File {
44
- preview?: string;
45
- url?: string;
46
- }
47
-
48
- // Type guard for file with preview
49
- export const isFileWithPreview = (file: unknown): file is FileWithPreview => {
50
- return file instanceof File && "preview" in file;
51
- };