@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 +14 -0
- package/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +64 -10
- package/dist/index.mjs +64 -10
- package/package.json +1 -1
- package/src/lib/Dropzone.tsx +47 -4
- package/src/lib/Dropzone2.tsx +41 -5
- package/src/lib/FileSelector.tsx +2 -0
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
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
package/src/lib/Dropzone.tsx
CHANGED
|
@@ -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
|
|
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}>
|
package/src/lib/Dropzone2.tsx
CHANGED
|
@@ -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
|
|
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}>
|
package/src/lib/FileSelector.tsx
CHANGED
|
@@ -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}
|