@availity/mui-file-selector 1.7.1 → 1.8.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.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
+
7
+
8
+ ### Features
9
+
10
+ * **mui-file-selector:** add maxTotalSize check to FileSelector ([03f78d6](https://github.com/Availity/element/commit/03f78d674243a76cc6d3e65a20b6e8ce3a46b8ca))
11
+ * **mui-file-selector:** add maxTotalSize check to FileSelector ([802e2ae](https://github.com/Availity/element/commit/802e2ae502aa8727037e2998849acf01a6ea0382))
12
+ * **mui-file-selector:** add maxTotalSize check to FileSelector2 ([ccd0cfa](https://github.com/Availity/element/commit/ccd0cfa95610d6e354402a3fa698b63b6659644c))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **mui-file-selector:** ensure only the maximum amount of files are uploaded ([5d80d2b](https://github.com/Availity/element/commit/5d80d2b046ea928ea432d7f269d1bfc99dd43782))
18
+
5
19
  ## [1.7.1](https://github.com/Availity/element/compare/@availity/mui-file-selector@1.7.0...@availity/mui-file-selector@1.7.1) (2025-05-30)
6
20
 
7
21
 
package/dist/index.d.mts CHANGED
@@ -54,6 +54,10 @@ type DropzoneProps = {
54
54
  * Maximum size of each file in bytes
55
55
  */
56
56
  maxSize?: number;
57
+ /**
58
+ * Maximum size of total upload in bytes
59
+ */
60
+ maxTotalSize?: number;
57
61
  /**
58
62
  * Whether multiple file selection is allowed
59
63
  */
@@ -88,7 +92,7 @@ type DropzoneProps = {
88
92
  validator?: (file: File) => FileError | FileError[] | null;
89
93
  };
90
94
  declare const DropzoneContainer: typeof MuiBox;
91
- declare const Dropzone: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
95
+ declare const Dropzone: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
92
96
 
93
97
  type Dropzone2Props = DropzoneProps & {
94
98
  uploadOptions: UploadOptions;
@@ -101,7 +105,7 @@ type Dropzone2Props = DropzoneProps & {
101
105
  * `<Dropzone2 />` adds the `uploadOptions` prop that previously existed on
102
106
  * `<FileSelector />`.
103
107
  */
104
- declare const Dropzone2: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, uploadOptions, validator, }: Dropzone2Props) => react_jsx_runtime.JSX.Element;
108
+ declare const Dropzone2: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, uploadOptions, validator, }: Dropzone2Props) => react_jsx_runtime.JSX.Element;
105
109
 
106
110
  type ErrorAlertProps = {
107
111
  /**
@@ -268,6 +272,10 @@ type FileSelectorProps = {
268
272
  * Overrides the standard file size message
269
273
  */
270
274
  customSizeMessage?: React.ReactNode;
275
+ /**
276
+ * Overrides the standard total upload size message
277
+ */
278
+ customTotalSizeMessage?: React.ReactNode;
271
279
  /**
272
280
  * Overrides the standard file types message
273
281
  */
@@ -307,6 +315,11 @@ type FileSelectorProps = {
307
315
  * Use Kibi or Mibibytes. eg: 1kb = 1024 bytes; 1mb = 1024kb
308
316
  */
309
317
  maxSize: number;
318
+ /**
319
+ * Maximum size allowed for total upload in bytes
320
+ * Use Kibi or Mibibytes. eg: 1kb = 1024 bytes; 1mb = 1024kb
321
+ */
322
+ maxTotalSize?: number;
310
323
  /**
311
324
  * Whether multiple file selection is allowed
312
325
  * @default true
@@ -347,7 +360,7 @@ type FileSelectorProps = {
347
360
  */
348
361
  disableRemove?: boolean;
349
362
  };
350
- declare const FileSelector: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, multiple, onChange, onDrop, onUploadRemove, queryOptions, uploadOptions, validator, disableRemove, }: FileSelectorProps) => react_jsx_runtime.JSX.Element;
363
+ declare const FileSelector: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTotalSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, maxTotalSize, multiple, onChange, onDrop, onUploadRemove, queryOptions, uploadOptions, validator, disableRemove, }: FileSelectorProps) => react_jsx_runtime.JSX.Element;
351
364
 
352
365
  type FileSelector2Props = Omit<FileSelectorProps, 'onUploadRemove' | 'queryOptions'> & {
353
366
  onUploadRemove?: (uploads: Upload[], removedUploadId: string) => void;
@@ -361,7 +374,7 @@ type FileSelector2Props = Omit<FileSelectorProps, 'onUploadRemove' | 'queryOptio
361
374
  * `<FileSelector2 />` removes the reliance on `@tanstack/react-query`. The
362
375
  * `Upload` object can now be accessed from the form state.
363
376
  */
364
- declare const FileSelector2: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, multiple, onChange, onDrop, onUploadRemove, uploadOptions, validator, disableRemove, }: FileSelector2Props) => react_jsx_runtime.JSX.Element;
377
+ declare const FileSelector2: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTotalSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, maxTotalSize, multiple, onChange, onDrop, onUploadRemove, uploadOptions, validator, disableRemove, }: FileSelector2Props) => react_jsx_runtime.JSX.Element;
365
378
 
366
379
  type FileTypesMessageProps = {
367
380
  /**
@@ -372,6 +385,10 @@ type FileTypesMessageProps = {
372
385
  * Overrides the standard file size message
373
386
  */
374
387
  customSizeMessage?: React.ReactNode;
388
+ /**
389
+ * Overrides the standard total upload size message
390
+ */
391
+ customTotalSizeMessage?: React.ReactNode;
375
392
  /**
376
393
  * Overrides the standard file types message
377
394
  */
@@ -380,9 +397,13 @@ type FileTypesMessageProps = {
380
397
  * Maximum size per file in bytes. This will be formatted. eg: 1024 * 20 = 20 KB
381
398
  */
382
399
  maxFileSize?: number;
400
+ /**
401
+ * Maximum size of the total upload in bytes. This will be formatted. eg: 1024 * 20 = 20 KB
402
+ */
403
+ maxTotalSize?: number;
383
404
  variant?: 'caption' | 'body2';
384
405
  };
385
- declare const FileTypesMessage: ({ allowedFileTypes, customSizeMessage, customTypesMessage, maxFileSize, variant, }: FileTypesMessageProps) => react_jsx_runtime.JSX.Element;
406
+ declare const FileTypesMessage: ({ allowedFileTypes, customSizeMessage, customTotalSizeMessage, customTypesMessage, maxFileSize, maxTotalSize, variant, }: FileTypesMessageProps) => react_jsx_runtime.JSX.Element;
386
407
 
387
408
  type HeaderMessageProps = {
388
409
  /**
@@ -390,11 +411,15 @@ type HeaderMessageProps = {
390
411
  */
391
412
  maxFiles: number;
392
413
  /**
393
- * Maximum combined total size of all files
414
+ * Maximum size of each file
394
415
  */
395
416
  maxSize: number;
417
+ /**
418
+ * Maximum combined total size of all files
419
+ */
420
+ maxTotalSize?: number;
396
421
  };
397
- declare const HeaderMessage: ({ maxFiles, maxSize }: HeaderMessageProps) => react_jsx_runtime.JSX.Element;
422
+ declare const HeaderMessage: ({ maxFiles, maxSize, maxTotalSize }: HeaderMessageProps) => react_jsx_runtime.JSX.Element;
398
423
 
399
424
  type UploadProgressBarProps = {
400
425
  /**
package/dist/index.d.ts CHANGED
@@ -54,6 +54,10 @@ type DropzoneProps = {
54
54
  * Maximum size of each file in bytes
55
55
  */
56
56
  maxSize?: number;
57
+ /**
58
+ * Maximum size of total upload in bytes
59
+ */
60
+ maxTotalSize?: number;
57
61
  /**
58
62
  * Whether multiple file selection is allowed
59
63
  */
@@ -88,7 +92,7 @@ type DropzoneProps = {
88
92
  validator?: (file: File) => FileError | FileError[] | null;
89
93
  };
90
94
  declare const DropzoneContainer: typeof MuiBox;
91
- declare const Dropzone: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
95
+ declare const Dropzone: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, validator, }: DropzoneProps) => react_jsx_runtime.JSX.Element;
92
96
 
93
97
  type Dropzone2Props = DropzoneProps & {
94
98
  uploadOptions: UploadOptions;
@@ -101,7 +105,7 @@ type Dropzone2Props = DropzoneProps & {
101
105
  * `<Dropzone2 />` adds the `uploadOptions` prop that previously existed on
102
106
  * `<FileSelector />`.
103
107
  */
104
- declare const Dropzone2: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, uploadOptions, validator, }: Dropzone2Props) => react_jsx_runtime.JSX.Element;
108
+ declare const Dropzone2: ({ allowedFileTypes, disabled, enableDropArea, maxFiles, maxSize, maxTotalSize, multiple, name, onChange, onClick, onDrop, setFileRejections, setTotalSize, uploadOptions, validator, }: Dropzone2Props) => react_jsx_runtime.JSX.Element;
105
109
 
106
110
  type ErrorAlertProps = {
107
111
  /**
@@ -268,6 +272,10 @@ type FileSelectorProps = {
268
272
  * Overrides the standard file size message
269
273
  */
270
274
  customSizeMessage?: React.ReactNode;
275
+ /**
276
+ * Overrides the standard total upload size message
277
+ */
278
+ customTotalSizeMessage?: React.ReactNode;
271
279
  /**
272
280
  * Overrides the standard file types message
273
281
  */
@@ -307,6 +315,11 @@ type FileSelectorProps = {
307
315
  * Use Kibi or Mibibytes. eg: 1kb = 1024 bytes; 1mb = 1024kb
308
316
  */
309
317
  maxSize: number;
318
+ /**
319
+ * Maximum size allowed for total upload in bytes
320
+ * Use Kibi or Mibibytes. eg: 1kb = 1024 bytes; 1mb = 1024kb
321
+ */
322
+ maxTotalSize?: number;
310
323
  /**
311
324
  * Whether multiple file selection is allowed
312
325
  * @default true
@@ -347,7 +360,7 @@ type FileSelectorProps = {
347
360
  */
348
361
  disableRemove?: boolean;
349
362
  };
350
- declare const FileSelector: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, multiple, onChange, onDrop, onUploadRemove, queryOptions, uploadOptions, validator, disableRemove, }: FileSelectorProps) => react_jsx_runtime.JSX.Element;
363
+ declare const FileSelector: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTotalSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, maxTotalSize, multiple, onChange, onDrop, onUploadRemove, queryOptions, uploadOptions, validator, disableRemove, }: FileSelectorProps) => react_jsx_runtime.JSX.Element;
351
364
 
352
365
  type FileSelector2Props = Omit<FileSelectorProps, 'onUploadRemove' | 'queryOptions'> & {
353
366
  onUploadRemove?: (uploads: Upload[], removedUploadId: string) => void;
@@ -361,7 +374,7 @@ type FileSelector2Props = Omit<FileSelectorProps, 'onUploadRemove' | 'queryOptio
361
374
  * `<FileSelector2 />` removes the reliance on `@tanstack/react-query`. The
362
375
  * `Upload` object can now be accessed from the form state.
363
376
  */
364
- declare const FileSelector2: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, multiple, onChange, onDrop, onUploadRemove, uploadOptions, validator, disableRemove, }: FileSelector2Props) => react_jsx_runtime.JSX.Element;
377
+ declare const FileSelector2: ({ name, allowedFileNameCharacters, allowedFileTypes, bucketId, clientId, children, customSizeMessage, customTotalSizeMessage, customTypesMessage, customerId, customFileRow, disabled, enableDropArea, endpoint, isCloud, label, maxFiles, maxSize, maxTotalSize, multiple, onChange, onDrop, onUploadRemove, uploadOptions, validator, disableRemove, }: FileSelector2Props) => react_jsx_runtime.JSX.Element;
365
378
 
366
379
  type FileTypesMessageProps = {
367
380
  /**
@@ -372,6 +385,10 @@ type FileTypesMessageProps = {
372
385
  * Overrides the standard file size message
373
386
  */
374
387
  customSizeMessage?: React.ReactNode;
388
+ /**
389
+ * Overrides the standard total upload size message
390
+ */
391
+ customTotalSizeMessage?: React.ReactNode;
375
392
  /**
376
393
  * Overrides the standard file types message
377
394
  */
@@ -380,9 +397,13 @@ type FileTypesMessageProps = {
380
397
  * Maximum size per file in bytes. This will be formatted. eg: 1024 * 20 = 20 KB
381
398
  */
382
399
  maxFileSize?: number;
400
+ /**
401
+ * Maximum size of the total upload in bytes. This will be formatted. eg: 1024 * 20 = 20 KB
402
+ */
403
+ maxTotalSize?: number;
383
404
  variant?: 'caption' | 'body2';
384
405
  };
385
- declare const FileTypesMessage: ({ allowedFileTypes, customSizeMessage, customTypesMessage, maxFileSize, variant, }: FileTypesMessageProps) => react_jsx_runtime.JSX.Element;
406
+ declare const FileTypesMessage: ({ allowedFileTypes, customSizeMessage, customTotalSizeMessage, customTypesMessage, maxFileSize, maxTotalSize, variant, }: FileTypesMessageProps) => react_jsx_runtime.JSX.Element;
386
407
 
387
408
  type HeaderMessageProps = {
388
409
  /**
@@ -390,11 +411,15 @@ type HeaderMessageProps = {
390
411
  */
391
412
  maxFiles: number;
392
413
  /**
393
- * Maximum combined total size of all files
414
+ * Maximum size of each file
394
415
  */
395
416
  maxSize: number;
417
+ /**
418
+ * Maximum combined total size of all files
419
+ */
420
+ maxTotalSize?: number;
396
421
  };
397
- declare const HeaderMessage: ({ maxFiles, maxSize }: HeaderMessageProps) => react_jsx_runtime.JSX.Element;
422
+ declare const HeaderMessage: ({ maxFiles, maxSize, maxTotalSize }: HeaderMessageProps) => react_jsx_runtime.JSX.Element;
398
423
 
399
424
  type UploadProgressBarProps = {
400
425
  /**
package/dist/index.js CHANGED
@@ -108,7 +108,7 @@ var import_styles = require("@mui/material/styles");
108
108
  var import_react_dropzone = require("react-dropzone");
109
109
  var import_react_hook_form2 = require("react-hook-form");
110
110
  var import_mui_divider = require("@availity/mui-divider");
111
- var import_mui_icon = require("@availity/mui-icon");
111
+ var import_mui_icon2 = require("@availity/mui-icon");
112
112
  var import_mui_layout = require("@availity/mui-layout");
113
113
  var import_mui_typography = require("@availity/mui-typography");
114
114
 
@@ -166,6 +166,55 @@ var FilePickerBtn = (_a) => {
166
166
  ] });
167
167
  };
168
168
 
169
+ // src/lib/util.ts
170
+ var import_mui_icon = require("@availity/mui-icon");
171
+ function formatBytes(bytes, decimals = 2) {
172
+ if (!+bytes) return "0 Bytes";
173
+ const k = 1024;
174
+ const dm = decimals < 0 ? 0 : decimals;
175
+ const sizes = ["Bytes", "KB", "MB", "GB"];
176
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
177
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
178
+ }
179
+ var FILE_EXT_ICONS = {
180
+ png: import_mui_icon.FileImageIcon,
181
+ jpg: import_mui_icon.FileImageIcon,
182
+ jpeg: import_mui_icon.FileImageIcon,
183
+ gif: import_mui_icon.FileImageIcon,
184
+ csv: import_mui_icon.FileCsvIcon,
185
+ ppt: import_mui_icon.FilePowerpointIcon,
186
+ pptx: import_mui_icon.FilePowerpointIcon,
187
+ xls: import_mui_icon.FileExcelIcon,
188
+ xlsx: import_mui_icon.FileExcelIcon,
189
+ doc: import_mui_icon.FileWordIcon,
190
+ docx: import_mui_icon.FileWordIcon,
191
+ txt: import_mui_icon.FileLinesIcon,
192
+ text: import_mui_icon.FileLinesIcon,
193
+ zip: import_mui_icon.FileArchiveIcon,
194
+ "7zip": import_mui_icon.FileArchiveIcon,
195
+ xml: import_mui_icon.FileCodeIcon,
196
+ html: import_mui_icon.FileCodeIcon,
197
+ pdf: import_mui_icon.FilePdfIcon
198
+ };
199
+ var isValidKey = (key) => key ? key in FILE_EXT_ICONS : false;
200
+ var getFileExtension = (fileName) => {
201
+ var _a;
202
+ return ((_a = fileName.split(".").pop()) == null ? void 0 : _a.toLowerCase()) || "";
203
+ };
204
+ var getFileExtIcon = (fileName) => {
205
+ const ext = getFileExtension(fileName);
206
+ return isValidKey(ext) ? FILE_EXT_ICONS[ext] : import_mui_icon.FileIcon;
207
+ };
208
+ var dedupeErrors = (errors) => {
209
+ const dedupedErrors = errors.reduce((acc, error) => {
210
+ if (!acc.find((err) => err.code === error.code)) {
211
+ acc.push(error);
212
+ }
213
+ return acc;
214
+ }, []);
215
+ return dedupedErrors;
216
+ };
217
+
169
218
  // src/lib/Dropzone.tsx
170
219
  var import_jsx_runtime2 = require("react/jsx-runtime");
171
220
  var outerBoxStyles = {
@@ -205,6 +254,7 @@ var Dropzone = ({
205
254
  enableDropArea = true,
206
255
  maxFiles,
207
256
  maxSize,
257
+ maxTotalSize,
208
258
  multiple,
209
259
  name,
210
260
  onChange,
@@ -257,7 +307,48 @@ var Dropzone = ({
257
307
  }
258
308
  setTotalSize((prev) => prev + newSize);
259
309
  const previous = (_a2 = watch(name)) != null ? _a2 : [];
260
- setValue(name, previous.concat(acceptedFiles));
310
+ if (maxTotalSize) {
311
+ const currentTotalSize = previous.reduce((sum, file) => sum + file.size, 0);
312
+ let newSize2 = 0;
313
+ const availableSize = Math.max(0, maxTotalSize - currentTotalSize);
314
+ let sizeCounter = 0;
315
+ const cutoffIndex = acceptedFiles.findIndex((file) => {
316
+ sizeCounter += file.size;
317
+ return sizeCounter > availableSize;
318
+ });
319
+ if (cutoffIndex !== -1) {
320
+ const filesToAdd2 = acceptedFiles.slice(0, cutoffIndex === 0 ? 0 : cutoffIndex);
321
+ fileRejections.push({
322
+ file: acceptedFiles[cutoffIndex],
323
+ errors: [
324
+ {
325
+ code: "upload-too-large",
326
+ message: `Total upload size exceeds the limit of ${formatBytes(maxTotalSize)}.`
327
+ }
328
+ ],
329
+ id: counter.increment()
330
+ });
331
+ acceptedFiles = filesToAdd2;
332
+ }
333
+ newSize2 = acceptedFiles.reduce((sum, file) => sum + file.size, 0);
334
+ setTotalSize((prev) => prev + newSize2);
335
+ }
336
+ const remainingSlots = maxFiles ? Math.max(0, maxFiles - previous.length) : acceptedFiles.length;
337
+ const filesToAdd = acceptedFiles.slice(0, remainingSlots);
338
+ setValue(name, previous.concat(filesToAdd));
339
+ if (maxFiles && acceptedFiles.length > remainingSlots) {
340
+ fileRejections.push({
341
+ file: acceptedFiles[remainingSlots],
342
+ // Use the first excess file
343
+ errors: [
344
+ {
345
+ code: "too-many-files",
346
+ message: `Too many files. You may only upload ${maxFiles} file(s).`
347
+ }
348
+ ],
349
+ id: counter.increment()
350
+ });
351
+ }
261
352
  if (fileRejections.length > 0) {
262
353
  const TOO_MANY_FILES_CODE = "too-many-files";
263
354
  let hasTooManyFiles = false;
@@ -317,7 +408,7 @@ var Dropzone = ({
317
408
  };
318
409
  const hasFiles = getFieldValue().length > 0;
319
410
  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: [
320
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_icon.CloudUploadIcon, { fontSize: "xlarge", color: "secondary" }),
411
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_icon2.CloudUploadIcon, { fontSize: "xlarge", color: "secondary" }),
321
412
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_typography.Typography, { variant: "subtitle2", fontWeight: "700", children: "Drag and Drop Files Here" }),
322
413
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_divider.Divider, { flexItem: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_typography.Typography, { variant: "subtitle2", children: "OR" }) }),
323
414
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -343,7 +434,7 @@ var Dropzone = ({
343
434
  onClick: handleOnClick,
344
435
  inputProps,
345
436
  onChange: handleOnChange,
346
- startIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_icon.PlusIcon, {}),
437
+ startIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_mui_icon2.PlusIcon, {}),
347
438
  children: hasFiles ? "Add More Files" : "Add File(s)"
348
439
  }
349
440
  );
@@ -358,57 +449,6 @@ var import_mui_icon3 = require("@availity/mui-icon");
358
449
  var import_mui_layout2 = require("@availity/mui-layout");
359
450
  var import_mui_typography2 = require("@availity/mui-typography");
360
451
  var import_upload_core = __toESM(require("@availity/upload-core"));
361
-
362
- // src/lib/util.ts
363
- var import_mui_icon2 = require("@availity/mui-icon");
364
- function formatBytes(bytes, decimals = 2) {
365
- if (!+bytes) return "0 Bytes";
366
- const k = 1024;
367
- const dm = decimals < 0 ? 0 : decimals;
368
- const sizes = ["Bytes", "KB", "MB", "GB"];
369
- const i = Math.floor(Math.log(bytes) / Math.log(k));
370
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
371
- }
372
- var FILE_EXT_ICONS = {
373
- png: import_mui_icon2.FileImageIcon,
374
- jpg: import_mui_icon2.FileImageIcon,
375
- jpeg: import_mui_icon2.FileImageIcon,
376
- gif: import_mui_icon2.FileImageIcon,
377
- csv: import_mui_icon2.FileCsvIcon,
378
- ppt: import_mui_icon2.FilePowerpointIcon,
379
- pptx: import_mui_icon2.FilePowerpointIcon,
380
- xls: import_mui_icon2.FileExcelIcon,
381
- xlsx: import_mui_icon2.FileExcelIcon,
382
- doc: import_mui_icon2.FileWordIcon,
383
- docx: import_mui_icon2.FileWordIcon,
384
- txt: import_mui_icon2.FileLinesIcon,
385
- text: import_mui_icon2.FileLinesIcon,
386
- zip: import_mui_icon2.FileArchiveIcon,
387
- "7zip": import_mui_icon2.FileArchiveIcon,
388
- xml: import_mui_icon2.FileCodeIcon,
389
- html: import_mui_icon2.FileCodeIcon,
390
- pdf: import_mui_icon2.FilePdfIcon
391
- };
392
- var isValidKey = (key) => key ? key in FILE_EXT_ICONS : false;
393
- var getFileExtension = (fileName) => {
394
- var _a;
395
- return ((_a = fileName.split(".").pop()) == null ? void 0 : _a.toLowerCase()) || "";
396
- };
397
- var getFileExtIcon = (fileName) => {
398
- const ext = getFileExtension(fileName);
399
- return isValidKey(ext) ? FILE_EXT_ICONS[ext] : import_mui_icon2.FileIcon;
400
- };
401
- var dedupeErrors = (errors) => {
402
- const dedupedErrors = errors.reduce((acc, error) => {
403
- if (!acc.find((err) => err.code === error.code)) {
404
- acc.push(error);
405
- }
406
- return acc;
407
- }, []);
408
- return dedupedErrors;
409
- };
410
-
411
- // src/lib/Dropzone2.tsx
412
452
  var import_jsx_runtime3 = require("react/jsx-runtime");
413
453
  var counter2 = createCounter();
414
454
  function startUpload(file, options) {
@@ -430,6 +470,7 @@ var Dropzone2 = ({
430
470
  enableDropArea = true,
431
471
  maxFiles,
432
472
  maxSize,
473
+ maxTotalSize,
433
474
  multiple,
434
475
  name,
435
476
  onChange,
@@ -484,8 +525,50 @@ var Dropzone2 = ({
484
525
  }
485
526
  setTotalSize((prev) => prev + newSize);
486
527
  const previous = (_a2 = watch(name)) != null ? _a2 : [];
487
- const uploads = acceptedFiles.map((file) => startUpload(file, uploadOptions));
528
+ if (maxTotalSize) {
529
+ const currentTotalSize = previous.reduce((sum, upload) => sum + upload.file.size, 0);
530
+ console.log({ previous });
531
+ let newSize2 = 0;
532
+ const availableSize = Math.max(0, maxTotalSize - currentTotalSize);
533
+ let sizeCounter = 0;
534
+ const cutoffIndex = acceptedFiles.findIndex((file) => {
535
+ sizeCounter += file.size;
536
+ return sizeCounter > availableSize;
537
+ });
538
+ if (cutoffIndex !== -1) {
539
+ const filesToAdd2 = acceptedFiles.slice(0, cutoffIndex === 0 ? 0 : cutoffIndex);
540
+ fileRejections.push({
541
+ file: acceptedFiles[cutoffIndex],
542
+ errors: [
543
+ {
544
+ code: "upload-too-large",
545
+ message: `Total upload size exceeds the limit of ${formatBytes(maxTotalSize)}.`
546
+ }
547
+ ],
548
+ id: counter2.increment()
549
+ });
550
+ acceptedFiles = filesToAdd2;
551
+ }
552
+ newSize2 = acceptedFiles.reduce((sum, file) => sum + file.size, 0);
553
+ setTotalSize((prev) => prev + newSize2);
554
+ }
555
+ const remainingSlots = maxFiles ? Math.max(0, maxFiles - previous.length) : acceptedFiles.length;
556
+ const filesToAdd = acceptedFiles.slice(0, remainingSlots);
557
+ const uploads = filesToAdd.map((file) => startUpload(file, uploadOptions));
488
558
  setValue(name, previous.concat(yield Promise.all(uploads)));
559
+ if (maxFiles && acceptedFiles.length > remainingSlots) {
560
+ fileRejections.push({
561
+ file: acceptedFiles[remainingSlots],
562
+ // Use the first excess file
563
+ errors: [
564
+ {
565
+ code: "too-many-files",
566
+ message: `Too many files. You may only upload ${maxFiles} file(s).`
567
+ }
568
+ ],
569
+ id: counter2.increment()
570
+ });
571
+ }
489
572
  if (fileRejections.length > 0) {
490
573
  const TOO_MANY_FILES_CODE = "too-many-files";
491
574
  let hasTooManyFiles = false;
@@ -582,6 +665,7 @@ var import_mui_list = require("@availity/mui-list");
582
665
  var import_jsx_runtime4 = require("react/jsx-runtime");
583
666
  var codes = {
584
667
  "file-too-large": "File exceeds maximum size",
668
+ "upload-too-large": "File causes maximum total upload size to be exceeded",
585
669
  "file-invalid-type": "File has an invalid type",
586
670
  "file-too-small": "File is smaller than minimum size",
587
671
  "too-many-file": "Too many files",
@@ -1087,14 +1171,18 @@ var import_jsx_runtime14 = require("react/jsx-runtime");
1087
1171
  var FileTypesMessage = ({
1088
1172
  allowedFileTypes = [],
1089
1173
  customSizeMessage,
1174
+ customTotalSizeMessage,
1090
1175
  customTypesMessage,
1091
1176
  maxFileSize,
1177
+ maxTotalSize,
1092
1178
  variant = "caption"
1093
1179
  }) => {
1094
1180
  const fileSizeMsg = customSizeMessage || (typeof maxFileSize === "number" ? `Maximum file size is ${formatBytes(maxFileSize)}. ` : null);
1181
+ const totalFileSizeMsg = customTotalSizeMessage || (typeof maxTotalSize === "number" ? `Maximum total upload size is ${formatBytes(maxTotalSize)}. ` : null);
1095
1182
  const fileTypesMsg = customTypesMessage || (allowedFileTypes.length > 0 ? `Supported file types include: ${allowedFileTypes.join(", ")}` : "All file types allowed.");
1096
1183
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_mui_typography4.Typography, { variant, children: [
1097
1184
  fileSizeMsg,
1185
+ totalFileSizeMsg,
1098
1186
  fileTypesMsg
1099
1187
  ] });
1100
1188
  };
@@ -1102,12 +1190,14 @@ var FileTypesMessage = ({
1102
1190
  // src/lib/HeaderMessage.tsx
1103
1191
  var import_mui_typography5 = require("@availity/mui-typography");
1104
1192
  var import_jsx_runtime15 = require("react/jsx-runtime");
1105
- var HeaderMessage = ({ maxFiles, maxSize }) => {
1193
+ var HeaderMessage = ({ maxFiles, maxSize, maxTotalSize }) => {
1106
1194
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_mui_typography5.Typography, { variant: "h6", children: [
1107
1195
  "Attach up to ",
1108
1196
  maxFiles,
1109
1197
  " file(s), with a maximum individual size of ",
1110
- formatBytes(maxSize)
1198
+ formatBytes(maxSize),
1199
+ " ",
1200
+ maxTotalSize && `and a maximum total size of ${formatBytes(maxTotalSize)}`
1111
1201
  ] });
1112
1202
  };
1113
1203
 
@@ -1122,6 +1212,7 @@ var FileSelector = ({
1122
1212
  clientId,
1123
1213
  children,
1124
1214
  customSizeMessage,
1215
+ customTotalSizeMessage,
1125
1216
  customTypesMessage,
1126
1217
  customerId,
1127
1218
  customFileRow,
@@ -1132,6 +1223,7 @@ var FileSelector = ({
1132
1223
  label = "Upload file",
1133
1224
  maxFiles,
1134
1225
  maxSize,
1226
+ maxTotalSize,
1135
1227
  multiple = true,
1136
1228
  onChange,
1137
1229
  onDrop,
@@ -1195,6 +1287,7 @@ var FileSelector = ({
1195
1287
  enableDropArea,
1196
1288
  maxFiles,
1197
1289
  maxSize,
1290
+ maxTotalSize,
1198
1291
  multiple,
1199
1292
  onChange,
1200
1293
  onDrop,
@@ -1208,7 +1301,9 @@ var FileSelector = ({
1208
1301
  {
1209
1302
  allowedFileTypes,
1210
1303
  maxFileSize: maxSize,
1304
+ maxTotalSize,
1211
1305
  customSizeMessage,
1306
+ customTotalSizeMessage,
1212
1307
  customTypesMessage,
1213
1308
  variant: "caption"
1214
1309
  }
@@ -1216,11 +1311,12 @@ var FileSelector = ({
1216
1311
  children
1217
1312
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_mui_layout7.Grid, { container: true, rowSpacing: 3, flexDirection: "column", children: [
1218
1313
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_mui_layout7.Grid, { children: [
1219
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HeaderMessage, { maxFiles, maxSize }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HeaderMessage, { maxFiles, maxSize, maxTotalSize }),
1220
1315
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1221
1316
  FileTypesMessage,
1222
1317
  {
1223
1318
  allowedFileTypes,
1319
+ customTotalSizeMessage,
1224
1320
  customSizeMessage,
1225
1321
  customTypesMessage,
1226
1322
  variant: "body2"
@@ -1237,6 +1333,7 @@ var FileSelector = ({
1237
1333
  enableDropArea,
1238
1334
  maxFiles,
1239
1335
  maxSize,
1336
+ maxTotalSize,
1240
1337
  multiple,
1241
1338
  onChange,
1242
1339
  onDrop,
@@ -1298,6 +1395,7 @@ var FileSelector2 = ({
1298
1395
  clientId,
1299
1396
  children,
1300
1397
  customSizeMessage,
1398
+ customTotalSizeMessage,
1301
1399
  customTypesMessage,
1302
1400
  customerId,
1303
1401
  customFileRow,
@@ -1308,6 +1406,7 @@ var FileSelector2 = ({
1308
1406
  label = "Upload file",
1309
1407
  maxFiles,
1310
1408
  maxSize,
1409
+ maxTotalSize,
1311
1410
  multiple = true,
1312
1411
  onChange,
1313
1412
  onDrop,
@@ -1368,6 +1467,7 @@ var FileSelector2 = ({
1368
1467
  enableDropArea,
1369
1468
  maxFiles,
1370
1469
  maxSize,
1470
+ maxTotalSize,
1371
1471
  multiple,
1372
1472
  onChange,
1373
1473
  onDrop,
@@ -1382,7 +1482,9 @@ var FileSelector2 = ({
1382
1482
  {
1383
1483
  allowedFileTypes,
1384
1484
  maxFileSize: maxSize,
1485
+ maxTotalSize,
1385
1486
  customSizeMessage,
1487
+ customTotalSizeMessage,
1386
1488
  customTypesMessage,
1387
1489
  variant: "caption"
1388
1490
  }
@@ -1390,12 +1492,13 @@ var FileSelector2 = ({
1390
1492
  children
1391
1493
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_mui_layout8.Grid, { container: true, rowSpacing: 3, flexDirection: "column", children: [
1392
1494
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_mui_layout8.Grid, { children: [
1393
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(HeaderMessage, { maxFiles, maxSize }),
1495
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(HeaderMessage, { maxFiles, maxSize, maxTotalSize }),
1394
1496
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1395
1497
  FileTypesMessage,
1396
1498
  {
1397
1499
  allowedFileTypes,
1398
1500
  customSizeMessage,
1501
+ customTotalSizeMessage,
1399
1502
  customTypesMessage,
1400
1503
  variant: "body2"
1401
1504
  }
@@ -1411,6 +1514,7 @@ var FileSelector2 = ({
1411
1514
  enableDropArea,
1412
1515
  maxFiles,
1413
1516
  maxSize,
1517
+ maxTotalSize,
1414
1518
  multiple,
1415
1519
  onChange,
1416
1520
  onDrop,