@availity/mui-file-selector 1.8.0 → 1.9.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.9.0](https://github.com/Availity/element/compare/@availity/mui-file-selector@1.8.1...@availity/mui-file-selector@1.9.0) (2025-09-24)
6
+
7
+
8
+ ### Features
9
+
10
+ * **mui-file-selector:** disable FilePickerBtn when max files is reached ([06f1764](https://github.com/Availity/element/commit/06f176448192e89088e760c762b37b469ac39e95))
11
+
12
+ ## [1.8.1](https://github.com/Availity/element/compare/@availity/mui-file-selector@1.8.0...@availity/mui-file-selector@1.8.1) (2025-06-16)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **mui-file-selector:** ensure allowedFileTypes and allowedFileNameCharacters validation ([bb0f3ea](https://github.com/Availity/element/commit/bb0f3eaa1dcab2e82f5037f534e959321366701a))
18
+
5
19
  ## [1.8.0](https://github.com/Availity/element/compare/@availity/mui-file-selector@1.7.1...@availity/mui-file-selector@1.8.0) (2025-06-16)
6
20
 
7
21
 
package/dist/index.d.mts CHANGED
@@ -38,6 +38,11 @@ type DropzoneProps = {
38
38
  * List of allowed file extensions (e.g. ['.pdf', '.doc']). Each extension must start with a dot
39
39
  */
40
40
  allowedFileTypes?: `.${string}`[];
41
+ /**
42
+ * Regular expression pattern of allowed characters in file names
43
+ * @example "a-zA-Z0-9-_."
44
+ */
45
+ allowedFileNameCharacters?: string;
41
46
  /**
42
47
  * Whether the dropzone is disabled
43
48
  */
@@ -92,7 +97,7 @@ type DropzoneProps = {
92
97
  validator?: (file: File) => FileError | FileError[] | null;
93
98
  };
94
99
  declare const DropzoneContainer: typeof MuiBox;
95
- declare const Dropzone: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
100
+ declare const Dropzone: ({ allowedFileTypes, allowedFileNameCharacters, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
96
101
 
97
102
  type Dropzone2Props = DropzoneProps & {
98
103
  uploadOptions: UploadOptions;
package/dist/index.d.ts CHANGED
@@ -38,6 +38,11 @@ type DropzoneProps = {
38
38
  * List of allowed file extensions (e.g. ['.pdf', '.doc']). Each extension must start with a dot
39
39
  */
40
40
  allowedFileTypes?: `.${string}`[];
41
+ /**
42
+ * Regular expression pattern of allowed characters in file names
43
+ * @example "a-zA-Z0-9-_."
44
+ */
45
+ allowedFileNameCharacters?: string;
41
46
  /**
42
47
  * Whether the dropzone is disabled
43
48
  */
@@ -92,7 +97,7 @@ type DropzoneProps = {
92
97
  validator?: (file: File) => FileError | FileError[] | null;
93
98
  };
94
99
  declare const DropzoneContainer: typeof MuiBox;
95
- declare const Dropzone: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
100
+ declare const Dropzone: ({ allowedFileTypes, allowedFileNameCharacters, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
96
101
 
97
102
  type Dropzone2Props = DropzoneProps & {
98
103
  uploadOptions: UploadOptions;
package/dist/index.js CHANGED
@@ -250,6 +250,7 @@ var DropzoneContainer = (0, import_styles.styled)(import_mui_layout.Box, { name:
250
250
  });
251
251
  var Dropzone = ({
252
252
  allowedFileTypes = [],
253
+ allowedFileNameCharacters,
253
254
  disabled,
254
255
  enableDropArea = true,
255
256
  maxFiles,
@@ -284,6 +285,27 @@ var Dropzone = ({
284
285
  message: `Too many files. You may only upload ${maxFiles} file(s).`
285
286
  });
286
287
  }
288
+ if (allowedFileNameCharacters) {
289
+ const fileName = file.name.substring(0, file.name.lastIndexOf("."));
290
+ const regExp = new RegExp(`([^${allowedFileNameCharacters}])`, "g");
291
+ if (fileName.match(regExp) !== null) {
292
+ errors.push({
293
+ code: "invalid-file-name-characters",
294
+ message: "File name contains characters not allowed"
295
+ });
296
+ }
297
+ }
298
+ if (allowedFileTypes.length > 0) {
299
+ const fileName = file.name;
300
+ const fileExt = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
301
+ const lowerCaseAllowedTypes = allowedFileTypes.map((type) => type.toLowerCase());
302
+ if (!lowerCaseAllowedTypes.includes(fileExt)) {
303
+ errors.push({
304
+ code: "file-invalid-type",
305
+ message: `File type ${fileExt} is not allowed`
306
+ });
307
+ }
308
+ }
287
309
  if (validator) {
288
310
  const validatorErrors = validator(file);
289
311
  if (validatorErrors) {
@@ -296,7 +318,7 @@ var Dropzone = ({
296
318
  }
297
319
  return errors.length > 0 ? errors : null;
298
320
  },
299
- [maxFiles, validator]
321
+ [maxFiles, validator, allowedFileNameCharacters, watch, name, allowedFileTypes]
300
322
  );
301
323
  const handleOnDrop = (0, import_react.useCallback)(
302
324
  (acceptedFiles, fileRejections, event) => {
@@ -375,7 +397,7 @@ var Dropzone = ({
375
397
  if (setFileRejections) setFileRejections(fileRejections);
376
398
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
377
399
  },
378
- [setFileRejections]
400
+ [setFileRejections, setTotalSize, watch, name, maxTotalSize, maxFiles, setValue, onDrop]
379
401
  );
380
402
  const accept = allowedFileTypes.join(",");
381
403
  const { getRootProps, getInputProps } = (0, import_react_dropzone.useDropzone)({
@@ -400,13 +422,18 @@ var Dropzone = ({
400
422
  };
401
423
  const handleOnClick = (event) => {
402
424
  if (!enableDropArea && rootProps.onClick) rootProps.onClick(event);
403
- if (onClick) onClick;
425
+ if (onClick) onClick(event);
404
426
  };
405
427
  const getFieldValue = () => {
406
428
  const field = getValues();
407
429
  return field[name] || [];
408
430
  };
409
- const hasFiles = getFieldValue().length > 0;
431
+ const currentFileCount = getFieldValue().length;
432
+ const hasFiles = currentFileCount > 0;
433
+ const isMaxFilesReached = maxFiles && currentFileCount >= maxFiles;
434
+ if (isMaxFilesReached) {
435
+ disabled = true;
436
+ }
410
437
  return enableDropArea ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DropzoneContainer, __spreadProps(__spreadValues({ sx: outerBoxStyles }, rootProps), { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_layout.Box, { sx: innerBoxStyles, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_layout.Stack, { spacing: 2, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
411
438
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_icon2.CloudUploadIcon, { fontSize: "xlarge", color: "secondary" }),
412
439
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_typography.Typography, { variant: "subtitle2", fontWeight: "700", children: "Drag and Drop Files Here" }),
@@ -502,6 +529,27 @@ var Dropzone2 = ({
502
529
  message: `Too many files. You may only upload ${maxFiles} file(s).`
503
530
  });
504
531
  }
532
+ if (uploadOptions.allowedFileNameCharacters) {
533
+ const fileName = file.name.substring(0, file.name.lastIndexOf("."));
534
+ const regExp = new RegExp(`([^${uploadOptions.allowedFileNameCharacters}])`, "g");
535
+ if (fileName.match(regExp) !== null) {
536
+ errors.push({
537
+ code: "invalid-file-name-characters",
538
+ message: "File name contains characters not allowed"
539
+ });
540
+ }
541
+ }
542
+ if (allowedFileTypes.length > 0) {
543
+ const fileName = file.name;
544
+ const fileExt = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
545
+ const lowerCaseAllowedTypes = allowedFileTypes.map((type) => type.toLowerCase());
546
+ if (!lowerCaseAllowedTypes.includes(fileExt)) {
547
+ errors.push({
548
+ code: "file-invalid-type",
549
+ message: `File type ${fileExt} is not allowed`
550
+ });
551
+ }
552
+ }
505
553
  if (validator) {
506
554
  const validatorErrors = validator(file);
507
555
  if (validatorErrors) {
@@ -514,10 +562,10 @@ var Dropzone2 = ({
514
562
  }
515
563
  return errors.length > 0 ? dedupeErrors(errors) : null;
516
564
  },
517
- [maxFiles, validator]
565
+ [maxFiles, validator, uploadOptions.allowedFileNameCharacters, allowedFileTypes, watch, name]
518
566
  );
519
567
  const handleOnDrop = (0, import_react2.useCallback)(
520
- (acceptedFiles, fileRejections, event) => __async(void 0, null, function* () {
568
+ (acceptedFiles, fileRejections, event) => __async(null, null, function* () {
521
569
  var _a2;
522
570
  let newSize = 0;
523
571
  for (const file of acceptedFiles) {
@@ -527,7 +575,6 @@ var Dropzone2 = ({
527
575
  const previous = (_a2 = watch(name)) != null ? _a2 : [];
528
576
  if (maxTotalSize) {
529
577
  const currentTotalSize = previous.reduce((sum, upload) => sum + upload.file.size, 0);
530
- console.log({ previous });
531
578
  let newSize2 = 0;
532
579
  const availableSize = Math.max(0, maxTotalSize - currentTotalSize);
533
580
  let sizeCounter = 0;
@@ -595,7 +642,7 @@ var Dropzone2 = ({
595
642
  if (setFileRejections) setFileRejections(fileRejections);
596
643
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
597
644
  }),
598
- [setFileRejections]
645
+ [setFileRejections, setTotalSize, watch, name, maxTotalSize, maxFiles, uploadOptions, setValue, onDrop]
599
646
  );
600
647
  const { getRootProps, getInputProps } = (0, import_react_dropzone2.useDropzone)({
601
648
  onDrop: handleOnDrop,
@@ -619,13 +666,18 @@ var Dropzone2 = ({
619
666
  };
620
667
  const handleOnClick = (event) => {
621
668
  if (!enableDropArea && rootProps.onClick) rootProps.onClick(event);
622
- if (onClick) onClick;
669
+ if (onClick) onClick(event);
623
670
  };
624
671
  const getFieldValue = () => {
625
672
  const field = getValues();
626
673
  return field[name] || [];
627
674
  };
628
- const hasFiles = getFieldValue().length > 0;
675
+ const currentFileCount = getFieldValue().length;
676
+ const hasFiles = currentFileCount > 0;
677
+ const isMaxFilesReached = maxFiles && currentFileCount >= maxFiles;
678
+ if (isMaxFilesReached) {
679
+ disabled = true;
680
+ }
629
681
  return enableDropArea ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DropzoneContainer, __spreadProps(__spreadValues({ sx: outerBoxStyles }, rootProps), { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_mui_layout2.Box, { sx: innerBoxStyles, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_mui_layout2.Stack, { spacing: 2, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
630
682
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_mui_icon3.CloudUploadIcon, { fontSize: "xlarge", color: "secondary" }),
631
683
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_mui_typography2.Typography, { variant: "subtitle2", fontWeight: "700", children: "Drag and Drop Files Here" }),
@@ -1283,6 +1335,7 @@ var FileSelector = ({
1283
1335
  {
1284
1336
  name,
1285
1337
  allowedFileTypes,
1338
+ allowedFileNameCharacters,
1286
1339
  disabled,
1287
1340
  enableDropArea,
1288
1341
  maxFiles,
@@ -1329,6 +1382,7 @@ var FileSelector = ({
1329
1382
  {
1330
1383
  name,
1331
1384
  allowedFileTypes,
1385
+ allowedFileNameCharacters,
1332
1386
  disabled,
1333
1387
  enableDropArea,
1334
1388
  maxFiles,
package/dist/index.mjs CHANGED
@@ -209,6 +209,7 @@ var DropzoneContainer = styled(Box, { name: "AvDropzoneContainer", slot: "root"
209
209
  });
210
210
  var Dropzone = ({
211
211
  allowedFileTypes = [],
212
+ allowedFileNameCharacters,
212
213
  disabled,
213
214
  enableDropArea = true,
214
215
  maxFiles,
@@ -243,6 +244,27 @@ var Dropzone = ({
243
244
  message: `Too many files. You may only upload ${maxFiles} file(s).`
244
245
  });
245
246
  }
247
+ if (allowedFileNameCharacters) {
248
+ const fileName = file.name.substring(0, file.name.lastIndexOf("."));
249
+ const regExp = new RegExp(`([^${allowedFileNameCharacters}])`, "g");
250
+ if (fileName.match(regExp) !== null) {
251
+ errors.push({
252
+ code: "invalid-file-name-characters",
253
+ message: "File name contains characters not allowed"
254
+ });
255
+ }
256
+ }
257
+ if (allowedFileTypes.length > 0) {
258
+ const fileName = file.name;
259
+ const fileExt = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
260
+ const lowerCaseAllowedTypes = allowedFileTypes.map((type) => type.toLowerCase());
261
+ if (!lowerCaseAllowedTypes.includes(fileExt)) {
262
+ errors.push({
263
+ code: "file-invalid-type",
264
+ message: `File type ${fileExt} is not allowed`
265
+ });
266
+ }
267
+ }
246
268
  if (validator) {
247
269
  const validatorErrors = validator(file);
248
270
  if (validatorErrors) {
@@ -255,7 +277,7 @@ var Dropzone = ({
255
277
  }
256
278
  return errors.length > 0 ? errors : null;
257
279
  },
258
- [maxFiles, validator]
280
+ [maxFiles, validator, allowedFileNameCharacters, watch, name, allowedFileTypes]
259
281
  );
260
282
  const handleOnDrop = useCallback(
261
283
  (acceptedFiles, fileRejections, event) => {
@@ -334,7 +356,7 @@ var Dropzone = ({
334
356
  if (setFileRejections) setFileRejections(fileRejections);
335
357
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
336
358
  },
337
- [setFileRejections]
359
+ [setFileRejections, setTotalSize, watch, name, maxTotalSize, maxFiles, setValue, onDrop]
338
360
  );
339
361
  const accept = allowedFileTypes.join(",");
340
362
  const { getRootProps, getInputProps } = useDropzone({
@@ -359,13 +381,18 @@ var Dropzone = ({
359
381
  };
360
382
  const handleOnClick = (event) => {
361
383
  if (!enableDropArea && rootProps.onClick) rootProps.onClick(event);
362
- if (onClick) onClick;
384
+ if (onClick) onClick(event);
363
385
  };
364
386
  const getFieldValue = () => {
365
387
  const field = getValues();
366
388
  return field[name] || [];
367
389
  };
368
- const hasFiles = getFieldValue().length > 0;
390
+ const currentFileCount = getFieldValue().length;
391
+ const hasFiles = currentFileCount > 0;
392
+ const isMaxFilesReached = maxFiles && currentFileCount >= maxFiles;
393
+ if (isMaxFilesReached) {
394
+ disabled = true;
395
+ }
369
396
  return enableDropArea ? /* @__PURE__ */ jsx2(DropzoneContainer, __spreadProps(__spreadValues({ sx: outerBoxStyles }, rootProps), { children: /* @__PURE__ */ jsx2(Box, { sx: innerBoxStyles, children: /* @__PURE__ */ jsx2(Stack, { spacing: 2, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs2(Fragment2, { children: [
370
397
  /* @__PURE__ */ jsx2(CloudUploadIcon, { fontSize: "xlarge", color: "secondary" }),
371
398
  /* @__PURE__ */ jsx2(Typography, { variant: "subtitle2", fontWeight: "700", children: "Drag and Drop Files Here" }),
@@ -461,6 +488,27 @@ var Dropzone2 = ({
461
488
  message: `Too many files. You may only upload ${maxFiles} file(s).`
462
489
  });
463
490
  }
491
+ if (uploadOptions.allowedFileNameCharacters) {
492
+ const fileName = file.name.substring(0, file.name.lastIndexOf("."));
493
+ const regExp = new RegExp(`([^${uploadOptions.allowedFileNameCharacters}])`, "g");
494
+ if (fileName.match(regExp) !== null) {
495
+ errors.push({
496
+ code: "invalid-file-name-characters",
497
+ message: "File name contains characters not allowed"
498
+ });
499
+ }
500
+ }
501
+ if (allowedFileTypes.length > 0) {
502
+ const fileName = file.name;
503
+ const fileExt = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
504
+ const lowerCaseAllowedTypes = allowedFileTypes.map((type) => type.toLowerCase());
505
+ if (!lowerCaseAllowedTypes.includes(fileExt)) {
506
+ errors.push({
507
+ code: "file-invalid-type",
508
+ message: `File type ${fileExt} is not allowed`
509
+ });
510
+ }
511
+ }
464
512
  if (validator) {
465
513
  const validatorErrors = validator(file);
466
514
  if (validatorErrors) {
@@ -473,10 +521,10 @@ var Dropzone2 = ({
473
521
  }
474
522
  return errors.length > 0 ? dedupeErrors(errors) : null;
475
523
  },
476
- [maxFiles, validator]
524
+ [maxFiles, validator, uploadOptions.allowedFileNameCharacters, allowedFileTypes, watch, name]
477
525
  );
478
526
  const handleOnDrop = useCallback2(
479
- (acceptedFiles, fileRejections, event) => __async(void 0, null, function* () {
527
+ (acceptedFiles, fileRejections, event) => __async(null, null, function* () {
480
528
  var _a2;
481
529
  let newSize = 0;
482
530
  for (const file of acceptedFiles) {
@@ -486,7 +534,6 @@ var Dropzone2 = ({
486
534
  const previous = (_a2 = watch(name)) != null ? _a2 : [];
487
535
  if (maxTotalSize) {
488
536
  const currentTotalSize = previous.reduce((sum, upload) => sum + upload.file.size, 0);
489
- console.log({ previous });
490
537
  let newSize2 = 0;
491
538
  const availableSize = Math.max(0, maxTotalSize - currentTotalSize);
492
539
  let sizeCounter = 0;
@@ -554,7 +601,7 @@ var Dropzone2 = ({
554
601
  if (setFileRejections) setFileRejections(fileRejections);
555
602
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
556
603
  }),
557
- [setFileRejections]
604
+ [setFileRejections, setTotalSize, watch, name, maxTotalSize, maxFiles, uploadOptions, setValue, onDrop]
558
605
  );
559
606
  const { getRootProps, getInputProps } = useDropzone2({
560
607
  onDrop: handleOnDrop,
@@ -578,13 +625,18 @@ var Dropzone2 = ({
578
625
  };
579
626
  const handleOnClick = (event) => {
580
627
  if (!enableDropArea && rootProps.onClick) rootProps.onClick(event);
581
- if (onClick) onClick;
628
+ if (onClick) onClick(event);
582
629
  };
583
630
  const getFieldValue = () => {
584
631
  const field = getValues();
585
632
  return field[name] || [];
586
633
  };
587
- const hasFiles = getFieldValue().length > 0;
634
+ const currentFileCount = getFieldValue().length;
635
+ const hasFiles = currentFileCount > 0;
636
+ const isMaxFilesReached = maxFiles && currentFileCount >= maxFiles;
637
+ if (isMaxFilesReached) {
638
+ disabled = true;
639
+ }
588
640
  return enableDropArea ? /* @__PURE__ */ jsx3(DropzoneContainer, __spreadProps(__spreadValues({ sx: outerBoxStyles }, rootProps), { children: /* @__PURE__ */ jsx3(Box2, { sx: innerBoxStyles, children: /* @__PURE__ */ jsx3(Stack2, { spacing: 2, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs3(Fragment3, { children: [
589
641
  /* @__PURE__ */ jsx3(CloudUploadIcon2, { fontSize: "xlarge", color: "secondary" }),
590
642
  /* @__PURE__ */ jsx3(Typography2, { variant: "subtitle2", fontWeight: "700", children: "Drag and Drop Files Here" }),
@@ -1248,6 +1300,7 @@ var FileSelector = ({
1248
1300
  {
1249
1301
  name,
1250
1302
  allowedFileTypes,
1303
+ allowedFileNameCharacters,
1251
1304
  disabled,
1252
1305
  enableDropArea,
1253
1306
  maxFiles,
@@ -1294,6 +1347,7 @@ var FileSelector = ({
1294
1347
  {
1295
1348
  name,
1296
1349
  allowedFileTypes,
1350
+ allowedFileNameCharacters,
1297
1351
  disabled,
1298
1352
  enableDropArea,
1299
1353
  maxFiles,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@availity/mui-file-selector",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Availity MUI file-selector Component - part of the @availity/element design system",
5
5
  "keywords": [
6
6
  "react",
@@ -51,6 +51,11 @@ export type DropzoneProps = {
51
51
  * List of allowed file extensions (e.g. ['.pdf', '.doc']). Each extension must start with a dot
52
52
  */
53
53
  allowedFileTypes?: `.${string}`[];
54
+ /**
55
+ * Regular expression pattern of allowed characters in file names
56
+ * @example "a-zA-Z0-9-_."
57
+ */
58
+ allowedFileNameCharacters?: string;
54
59
  /**
55
60
  * Whether the dropzone is disabled
56
61
  */
@@ -111,6 +116,7 @@ export const DropzoneContainer = styled(Box, { name: 'AvDropzoneContainer', slot
111
116
 
112
117
  export const Dropzone = ({
113
118
  allowedFileTypes = [],
119
+ allowedFileNameCharacters,
114
120
  disabled,
115
121
  enableDropArea = true,
116
122
  maxFiles,
@@ -148,6 +154,35 @@ export const Dropzone = ({
148
154
  });
149
155
  }
150
156
 
157
+ // Check for allowed file name characters
158
+ if (allowedFileNameCharacters) {
159
+ const fileName = file.name.substring(0, file.name.lastIndexOf('.'));
160
+ const regExp = new RegExp(`([^${allowedFileNameCharacters}])`, 'g');
161
+
162
+ if (fileName.match(regExp) !== null) {
163
+ errors.push({
164
+ code: 'invalid-file-name-characters',
165
+ message: 'File name contains characters not allowed',
166
+ });
167
+ }
168
+ }
169
+
170
+ // Explicit check for allowed file types
171
+ if (allowedFileTypes.length > 0) {
172
+ const fileName = file.name;
173
+ const fileExt = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
174
+
175
+ // Convert all file types to lowercase for comparison
176
+ const lowerCaseAllowedTypes = allowedFileTypes.map((type) => type.toLowerCase());
177
+
178
+ if (!lowerCaseAllowedTypes.includes(fileExt)) {
179
+ errors.push({
180
+ code: 'file-invalid-type',
181
+ message: `File type ${fileExt} is not allowed`,
182
+ });
183
+ }
184
+ }
185
+
151
186
  if (validator) {
152
187
  const validatorErrors = validator(file);
153
188
  if (validatorErrors) {
@@ -161,7 +196,7 @@ export const Dropzone = ({
161
196
 
162
197
  return errors.length > 0 ? errors : null;
163
198
  },
164
- [maxFiles, validator]
199
+ [maxFiles, validator, allowedFileNameCharacters, watch, name, allowedFileTypes]
165
200
  );
166
201
 
167
202
  const handleOnDrop = useCallback(
@@ -268,7 +303,7 @@ export const Dropzone = ({
268
303
  if (setFileRejections) setFileRejections(fileRejections);
269
304
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
270
305
  },
271
- [setFileRejections]
306
+ [setFileRejections, setTotalSize, watch, name, maxTotalSize, maxFiles, setValue, onDrop]
272
307
  );
273
308
 
274
309
  const accept = allowedFileTypes.join(',');
@@ -301,7 +336,7 @@ export const Dropzone = ({
301
336
 
302
337
  const handleOnClick = (event: MouseEvent<HTMLButtonElement>) => {
303
338
  if (!enableDropArea && rootProps.onClick) rootProps.onClick(event);
304
- if (onClick) onClick;
339
+ if (onClick) onClick(event);
305
340
  };
306
341
 
307
342
  const getFieldValue = () => {
@@ -309,7 +344,15 @@ export const Dropzone = ({
309
344
  return field[name] || [];
310
345
  };
311
346
 
312
- const hasFiles = getFieldValue().length > 0;
347
+ const currentFileCount = getFieldValue().length;
348
+
349
+ const hasFiles = currentFileCount > 0;
350
+
351
+ const isMaxFilesReached = maxFiles && currentFileCount >= maxFiles;
352
+
353
+ if (isMaxFilesReached) {
354
+ disabled = true;
355
+ }
313
356
 
314
357
  return enableDropArea ? (
315
358
  <DropzoneContainer sx={outerBoxStyles} {...rootProps}>
@@ -95,6 +95,35 @@ export const Dropzone2 = ({
95
95
  });
96
96
  }
97
97
 
98
+ // Check for allowed file name characters
99
+ if (uploadOptions.allowedFileNameCharacters) {
100
+ const fileName = file.name.substring(0, file.name.lastIndexOf('.'));
101
+ const regExp = new RegExp(`([^${uploadOptions.allowedFileNameCharacters}])`, 'g');
102
+
103
+ if (fileName.match(regExp) !== null) {
104
+ errors.push({
105
+ code: 'invalid-file-name-characters',
106
+ message: 'File name contains characters not allowed',
107
+ });
108
+ }
109
+ }
110
+
111
+ // Explicit check for allowed file types
112
+ if (allowedFileTypes.length > 0) {
113
+ const fileName = file.name;
114
+ const fileExt = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
115
+
116
+ // Convert all file types to lowercase for comparison
117
+ const lowerCaseAllowedTypes = allowedFileTypes.map((type) => type.toLowerCase());
118
+
119
+ if (!lowerCaseAllowedTypes.includes(fileExt)) {
120
+ errors.push({
121
+ code: 'file-invalid-type',
122
+ message: `File type ${fileExt} is not allowed`,
123
+ });
124
+ }
125
+ }
126
+
98
127
  if (validator) {
99
128
  const validatorErrors = validator(file);
100
129
  if (validatorErrors) {
@@ -108,7 +137,7 @@ export const Dropzone2 = ({
108
137
 
109
138
  return errors.length > 0 ? dedupeErrors(errors) : null;
110
139
  },
111
- [maxFiles, validator]
140
+ [maxFiles, validator, uploadOptions.allowedFileNameCharacters, allowedFileTypes, watch, name]
112
141
  );
113
142
 
114
143
  const handleOnDrop = useCallback(
@@ -125,7 +154,6 @@ export const Dropzone2 = ({
125
154
  if (maxTotalSize) {
126
155
  // Calculate current total size
127
156
  const currentTotalSize = previous.reduce((sum: number, upload: Upload) => sum + upload.file.size, 0);
128
- console.log({ previous });
129
157
  let newSize = 0;
130
158
 
131
159
  const availableSize = Math.max(0, maxTotalSize - currentTotalSize);
@@ -217,7 +245,7 @@ export const Dropzone2 = ({
217
245
  if (setFileRejections) setFileRejections(fileRejections);
218
246
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
219
247
  },
220
- [setFileRejections]
248
+ [setFileRejections, setTotalSize, watch, name, maxTotalSize, maxFiles, uploadOptions, setValue, onDrop]
221
249
  );
222
250
 
223
251
  const { getRootProps, getInputProps } = useDropzone({
@@ -248,7 +276,7 @@ export const Dropzone2 = ({
248
276
 
249
277
  const handleOnClick = (event: MouseEvent<HTMLButtonElement>) => {
250
278
  if (!enableDropArea && rootProps.onClick) rootProps.onClick(event);
251
- if (onClick) onClick;
279
+ if (onClick) onClick(event);
252
280
  };
253
281
 
254
282
  const getFieldValue = () => {
@@ -256,7 +284,15 @@ export const Dropzone2 = ({
256
284
  return field[name] || [];
257
285
  };
258
286
 
259
- const hasFiles = getFieldValue().length > 0;
287
+ const currentFileCount = getFieldValue().length;
288
+
289
+ const hasFiles = currentFileCount > 0;
290
+
291
+ const isMaxFilesReached = maxFiles && currentFileCount >= maxFiles;
292
+
293
+ if (isMaxFilesReached) {
294
+ disabled = true;
295
+ }
260
296
 
261
297
  return enableDropArea ? (
262
298
  <DropzoneContainer sx={outerBoxStyles} {...rootProps}>
@@ -249,6 +249,7 @@ export const FileSelector = ({
249
249
  <Dropzone
250
250
  name={name}
251
251
  allowedFileTypes={allowedFileTypes}
252
+ allowedFileNameCharacters={allowedFileNameCharacters}
252
253
  disabled={disabled}
253
254
  enableDropArea={enableDropArea}
254
255
  maxFiles={maxFiles}
@@ -289,6 +290,7 @@ export const FileSelector = ({
289
290
  <Dropzone
290
291
  name={name}
291
292
  allowedFileTypes={allowedFileTypes}
293
+ allowedFileNameCharacters={allowedFileNameCharacters}
292
294
  disabled={disabled}
293
295
  enableDropArea={enableDropArea}
294
296
  maxFiles={maxFiles}