@mainframework/dropzone 1.0.28 → 1.1.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/dist/index.js CHANGED
@@ -1,155 +1,195 @@
1
1
  "use client";
2
+ import { useRef, useId, useState } from 'react';
2
3
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { useRef, memo, useId, useCallback, useState, useEffect, useMemo } from 'react';
4
4
 
5
5
  //Use this to quickly get the file extensions
6
6
  //Devs can pass their own custom type mappings to override these defaults
7
+
7
8
  const defaultTypeExtensions = {
8
- "image/png": ".png",
9
- "image/jpeg": ".jpeg",
10
- "image/jpg": ".jpg",
11
- "image/svg+xml": ".svg",
12
- "application/pdf": ".pdf",
13
- "application/msword": ".doc",
14
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
9
+ "image/png": ".png",
10
+ "image/jpeg": ".jpeg",
11
+ "image/svg+xml": ".svg",
12
+ "application/pdf": ".pdf",
13
+ "application/msword": ".doc",
14
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx"
15
15
  };
16
+
16
17
  //This is the attribute that should be present in all svg files
17
- const xmlns = "<svg xmlns='http://www.w3.org/2000/svg'";
18
+ const xmlns = '<svg xmlns="http://www.w3.org/2000/svg"';
18
19
  const maximumUploadCount = 30;
19
20
  const maximumFileSize = 5e6; //5 mb's
20
- const printableMaximumFileSize = "5 Megabytes";
21
+
22
+ const formatFileSize = bytes => {
23
+ if (bytes >= 1e9) return `${Math.round(bytes / 1e9)} Gigabytes`;
24
+ if (bytes >= 1e6) return `${Math.round(bytes / 1e6)} Megabytes`;
25
+ if (bytes >= 1e3) return `${Math.round(bytes / 1e3)} Kilobytes`;
26
+ return `${bytes} Bytes`;
27
+ };
28
+ const buildAcceptString = acceptedTypes => {
29
+ const seen = new Set();
30
+
31
+ // Prefer extensions (input accept supports extensions and MIME types)
32
+ for (const ext of Object.values(acceptedTypes)) {
33
+ const trimmed = ext.trim();
34
+ if (!trimmed) continue;
35
+ if (!trimmed.startsWith(".")) {
36
+ const withDot = `.${trimmed}`;
37
+ if (!seen.has(withDot)) seen.add(withDot);
38
+ continue;
39
+ }
40
+ if (!seen.has(trimmed)) seen.add(trimmed);
41
+ }
42
+
43
+ // Add MIME keys too (helps some pickers)
44
+ for (const mime of Object.keys(acceptedTypes)) {
45
+ const trimmed = mime.trim();
46
+ if (!trimmed) continue;
47
+ if (!seen.has(trimmed)) seen.add(trimmed);
48
+ }
49
+ return Array.from(seen).join(", ");
50
+ };
51
+
52
+ // TYPE DEFINITIONS
53
+
21
54
  // HELPER: Extract file ID (filename without extension)
22
- /*
23
- * Extracts filename without extension, handling multiple dots correctly.
24
- * "my.photo.final.png" -> "my.photo.final"
25
- * "README.md" -> "README"
26
- * "no-extension" -> "no-extension"
55
+
56
+ /*
57
+ * Extracts filename without extension, handling multiple dots correctly.
58
+ * "my.photo.final.png" -> "my.photo.final"
59
+ * "README.md" -> "README"
60
+ * "no-extension" -> "no-extension"
27
61
  */
28
- const getFileId = (fileName) => {
29
- const lastDotIndex = fileName.lastIndexOf(".");
30
- return lastDotIndex > 0 ? fileName.slice(0, lastDotIndex) : fileName;
62
+ const getFileId = fileName => {
63
+ const lastDotIndex = fileName.lastIndexOf(".");
64
+ return lastDotIndex > 0 ? fileName.slice(0, lastDotIndex) : fileName;
31
65
  };
66
+
32
67
  // BLOB URL REGISTRY - WeakMap for automatic GC cleanup
33
- /*
34
- * WeakMap prevents memory leaks by allowing GC to reclaim File objects.
35
- * No manual cleanup needed when Files are no longer referenced.
68
+
69
+ /*
70
+ * WeakMap prevents memory leaks by allowing GC to reclaim File objects.
71
+ * No manual cleanup needed when Files are no longer referenced.
36
72
  */
37
73
  const blobUrlRegistry = new WeakMap();
38
- const createUrlString = (file) => {
39
- const existing = blobUrlRegistry.get(file);
40
- if (existing)
41
- return existing;
42
- const url = URL.createObjectURL(file);
43
- blobUrlRegistry.set(file, url);
44
- return url;
74
+ const createUrlString = file => {
75
+ const existing = blobUrlRegistry.get(file);
76
+ if (existing) return existing;
77
+ const url = URL.createObjectURL(file);
78
+ blobUrlRegistry.set(file, url);
79
+ return url;
45
80
  };
46
- const clearBlobFromMemory = (file) => {
47
- const url = blobUrlRegistry.get(file);
48
- if (!url)
49
- return;
50
- URL.revokeObjectURL(url);
51
- blobUrlRegistry.delete(file);
81
+ const clearBlobFromMemory = file => {
82
+ const url = blobUrlRegistry.get(file);
83
+ if (!url) return;
84
+ URL.revokeObjectURL(url);
85
+ blobUrlRegistry.delete(file);
52
86
  };
87
+
53
88
  // VALIDATION FUNCTIONS
54
- /*
55
- * Checks if file type is in accepted types map.
56
- * Uses 'in' operator for better performance than Boolean() conversion.
89
+
90
+ /*
91
+ * Checks if file type is in accepted types map.
92
+ * Uses Object.hasOwn for better performance than Boolean() conversion.
57
93
  */
58
- const isValidFileType = (file, acceptedTypes = defaultTypeExtensions) => file.type in acceptedTypes;
59
- /*
60
- * Returns true if ANY file exceeds the maximum size limit.
61
- * Optimized with for-loop to avoid callback allocations in hot path.
94
+ const isValidFileType = (file, acceptedTypes = defaultTypeExtensions) => {
95
+ if (Object.hasOwn(acceptedTypes, file.type)) return true;
96
+ const ext = "." + file.name.split(".").pop()?.toLowerCase();
97
+ return Object.values(acceptedTypes).some(e => e.toLowerCase() === ext);
98
+ };
99
+
100
+ /*
101
+ * Returns true if ANY file exceeds the maximum size limit.
102
+ * Optimized with for-loop to avoid callback allocations in hot path.
62
103
  */
104
+
63
105
  const checkFilesMaximumSize = (files, max = maximumFileSize) => {
64
- let i = 0;
65
- while (i < files.length) {
66
- if (files[i].size > max)
67
- return true;
68
- i++;
69
- }
70
- return false;
106
+ let i = 0;
107
+ while (i < files.length) {
108
+ if (files[i].size > max) return true;
109
+ i++;
110
+ }
111
+ return false;
71
112
  };
113
+
72
114
  // DOCUMENT CREATION
73
- /*
74
- * Creates document data object with file metadata.
75
- * Returns null if file type is not in allowableTypes to prevent invalid state.
115
+
116
+ /*
117
+ * Creates document data object with file metadata.
118
+ * Returns null if file type is not in allowableTypes to prevent invalid state.
76
119
  */
77
120
  const createDocumentData = (file, allowableTypes = defaultTypeExtensions) => {
78
- const type = allowableTypes[file.type];
79
- if (!type)
80
- return null;
81
- return {
82
- id: getFileId(file.name),
83
- type,
84
- file,
85
- url: createUrlString(file),
86
- };
121
+ const type = allowableTypes[file.type];
122
+ if (!type) return null;
123
+ return {
124
+ id: getFileId(file.name),
125
+ type,
126
+ file,
127
+ url: createUrlString(file)
128
+ };
87
129
  };
88
- /*
89
- * Optimized SVG xmlns check - only reads first 2KB initially.
90
- * Avoids toLowerCase() allocation and uses safer regex for replacement.
130
+
131
+ /*
132
+ * Optimized SVG xmlns check - only reads first 2KB initially.
133
+ * Avoids toLowerCase() allocation and uses safer regex for replacement.
91
134
  */
92
- const SvgXmlnsAttributeCheck = async (file, allowableTypes = defaultTypeExtensions) => {
93
- if (file.type !== "image/svg+xml") {
94
- return createDocumentData(file, allowableTypes);
95
- }
96
- // OPTIMIZATION: Only read first 2KB to check for xmlns
97
- const chunk = file.slice(0, 2048);
98
- const text = await chunk.text();
99
- // No toLowerCase() needed - xmlns attribute is case-sensitive in XML
100
- if (text.includes("xmlns=")) {
101
- return createDocumentData(file, allowableTypes);
102
- }
103
- // Missing the xmlns attribute, add it
104
- // Need full file content for modification
105
- const fullText = await file.text();
106
- // SAFER: Use regex with word boundary instead of simple string replace
107
- // Handles <svg> without space and various attribute orders
108
- const svgWithXmlns = fullText.replace(/<svg\b/, xmlns);
109
- return createDocumentData(new File([svgWithXmlns], file.name, {
110
- type: file.type,
111
- }), allowableTypes);
135
+ const svgXmlnsAttributeCheck = async (file, allowableTypes = defaultTypeExtensions) => {
136
+ if (file.type !== "image/svg+xml") {
137
+ return createDocumentData(file, allowableTypes);
138
+ }
139
+
140
+ // OPTIMIZATION: Only read first 2KB to check for xmlns
141
+ const chunk = file.slice(0, 2048);
142
+ const text = await chunk.text();
143
+
144
+ // No toLowerCase() needed - xmlns attribute is case-sensitive in XML
145
+ if (/<svg\b[^>]*\bxmlns\s*=/i.test(text)) {
146
+ return createDocumentData(file, allowableTypes);
147
+ }
148
+
149
+ // Missing the xmlns attribute, add it
150
+ // Need full file content for modification
151
+ const fullText = await file.text();
152
+
153
+ // SAFER: Use regex with word boundary instead of simple string replace
154
+ // Handles <svg> or <SVG> without space and various attribute orders
155
+ const svgWithXmlns = fullText.replace(/<svg\b/i, xmlns);
156
+ clearBlobFromMemory(file);
157
+ return createDocumentData(new File([svgWithXmlns], file.name, {
158
+ type: file.type
159
+ }), allowableTypes);
112
160
  };
161
+
113
162
  // FILE RENAMING
114
- /*
115
- * Renames a file or blob. Optimized to avoid unnecessary File creation
116
- * if name already matches.
117
- */
118
- const renameFile = (file, name) => file instanceof File && file.name === name ? file : new File([file], name, { type: file.type });
119
- /*
120
- * Checks if file needs renaming based on ID comparison.
121
- * - Handles Blobs (no name) by converting to File
122
- * - Avoids repeated string operations
123
- * - Case-sensitive comparison
124
- */
125
- const checkFile = (id, file) => {
126
- // No ID provided, return as-is
127
- if (!id)
128
- return file;
129
- // Handle Blob (not a File subclass)
130
- if (!(file instanceof File)) {
131
- return renameFile(file, id);
132
- }
133
- // It's a File - check if renaming is needed
134
- const currentId = getFileId(file.name);
135
- return id === currentId ? file : renameFile(file, id);
136
- };
137
163
 
138
- const e=e=>"object"==typeof e&&null!==e,t=e=>"function"==typeof e,r$1=e=>e instanceof Date,n=e=>e instanceof RegExp,o=e=>e instanceof Set,f=e=>e instanceof Map,i=e=>e instanceof String||e instanceof Number||e instanceof Boolean,a=e=>ArrayBuffer.isView(e)&&!(e instanceof DataView),s=e=>Object.getPrototypeOf(e),u=e=>null==e||"object"!=typeof e&&"function"!=typeof e,c=(l,y,p=new WeakMap,b={})=>{if(l===y)return true;if("number"==typeof l&&"number"==typeof y&&Number.isNaN(l)&&Number.isNaN(y))return true;if(b.normalizeBoxedPrimitives&&(i(l)&&(l=l.valueOf()),i(y)&&(y=y.valueOf()),u(l)&&u(y)))return l===y;if(u(l)||u(y))return false;if(t(l)||t(y))return l===y;if(!e(l)||!e(y))return false;let g=p.get(l)??(()=>{const e=new WeakMap;return p.set(l,e),e})();const h=g.get(y);if(void 0!==h)return h;g.set(y,true);const w=s(l),A=s(y);let m;return m=Array.isArray(l)&&Array.isArray(y)?((e,t,r,n)=>{if(e.length!==t.length)return false;for(let o=0;o<e.length;o++){if(n.strictSparseArrays){const r=Object.prototype.hasOwnProperty.call(e,o),n=Object.prototype.hasOwnProperty.call(t,o);if(r!==n)return false;if(!r&&!n)continue}if(!c(e[o],t[o],r,n))return false}return true})(l,y,p,b):!Array.isArray(l)&&!Array.isArray(y)&&(r$1(l)&&r$1(y)?((e,t)=>e.getTime()===t.getTime())(l,y):n(l)&&n(y)?((e,t)=>e.source===t.source&&e.flags===t.flags)(l,y):a(l)&&a(y)?((e,t)=>{if(e.constructor!==t.constructor||e.byteLength!==t.byteLength)return false;const r=new Uint8Array(e.buffer,e.byteOffset,e.byteLength),n=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);for(let e=0;e<r.length;e++)if(r[e]!==n[e])return false;return true})(l,y):o(l)&&o(y)?((e,t,r,n)=>{if(e.size!==t.size)return false;const o=Array.from(t),f=new Set;for(const t of e){const e=o.findIndex((e,r)=>!f.has(r)&&t===e);if(-1!==e){f.add(e);continue}let i=false;for(let e=0;e<o.length;e++)if(!f.has(e)&&c(t,o[e],r,n)){f.add(e),i=true;break}if(!i)return false}return true})(l,y,p,b):f(l)&&f(y)?((e,t,r,n)=>{if(e.size!==t.size)return false;const o=Array.from(t),f=new Set;for(const[i,a]of e){if(u(i)){const e=t.get(i);if(void 0===e&&!t.has(i))return false;if(!c(a,e,r,n))return false;continue}let e=false;for(let t=0;t<o.length;t++)if(!f.has(t)){const[s,u]=o[t];if(c(i,s,r,n)&&c(a,u,r,n)){f.add(t),e=true;break}}if(!e)return false}return true})(l,y,p,b):!(!b.allowPrototypeMismatch&&w!==A)&&((e,t,r,n)=>{let o=0;for(const f of Reflect.ownKeys(e)){if(!Object.prototype.hasOwnProperty.call(t,f))return false;if(!c(e[f],t[f],r,n))return false;o++;}return Reflect.ownKeys(t).length===o})(l,y,p,b)),g.set(y,m),m},l=(e,t,r)=>c(e,t,new WeakMap,r);
164
+ /*
165
+ * Checks if file needs renaming based on ID comparison.
166
+ * - Handles Blobs (no name) by converting to File
167
+ * - Avoids repeated string operations
168
+ * - Case-sensitive comparison
169
+ */
170
+ const renameFile = (id, file) => {
171
+ // No ID provided, return as-is
172
+ if (!id) return file;
139
173
 
140
- const useCustomCallback = (callback, dependencies) => {
141
- const refCallback = useRef(callback);
142
- const refDependencies = useRef(dependencies);
143
- // Update refs synchronously during render
144
- if (!dependencies.every((dep, index) => l(dep, refDependencies.current[index]))) {
145
- refDependencies.current = dependencies;
146
- refCallback.current = callback;
147
- }
148
- // Stable callback, always calls latest callback
149
- const stableCallback = useRef((...args) => refCallback.current(...args)).current;
150
- return stableCallback;
174
+ //It's a blob
175
+ if (!(file instanceof File)) {
176
+ return rename(id, file);
177
+ }
178
+ const currentId = getFileId(file.name);
179
+ if (id === currentId) return file;
180
+ const lastDot = file.name.lastIndexOf(".");
181
+ const ext = lastDot === -1 ? defaultTypeExtensions[file.type] ?? "" : file.name.slice(lastDot);
182
+ return rename(`${id}${ext}`, file);
151
183
  };
152
184
 
185
+ /*
186
+ * Renames a file or blob. Optimized to avoid unnecessary File creation
187
+ * if name already matches.
188
+ */
189
+ const rename = (id, file) => file instanceof File && file.name === id ? file : new File([file], id, {
190
+ type: file.type
191
+ });
192
+
153
193
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
154
194
 
155
195
  /**
@@ -499,15 +539,27 @@ const createConfigUtils = config => ({
499
539
  cache: createLruCache(config.cacheSize),
500
540
  parseClassName: createParseClassName(config),
501
541
  sortModifiers: createSortModifiers(config),
542
+ postfixLookupClassGroupIds: createPostfixLookupClassGroupIds(config),
502
543
  ...createClassGroupUtils(config)
503
544
  });
545
+ const createPostfixLookupClassGroupIds = config => {
546
+ const lookup = Object.create(null);
547
+ const classGroupIds = config.postfixLookupClassGroups;
548
+ if (classGroupIds) {
549
+ for (let i = 0; i < classGroupIds.length; i++) {
550
+ lookup[classGroupIds[i]] = true;
551
+ }
552
+ }
553
+ return lookup;
554
+ };
504
555
  const SPLIT_CLASSES_REGEX = /\s+/;
505
556
  const mergeClassList = (classList, configUtils) => {
506
557
  const {
507
558
  parseClassName,
508
559
  getClassGroupId,
509
560
  getConflictingClassGroupIds,
510
- sortModifiers
561
+ sortModifiers,
562
+ postfixLookupClassGroupIds
511
563
  } = configUtils;
512
564
  /**
513
565
  * Set of classGroupIds in following format:
@@ -533,7 +585,18 @@ const mergeClassList = (classList, configUtils) => {
533
585
  continue;
534
586
  }
535
587
  let hasPostfixModifier = !!maybePostfixModifierPosition;
536
- let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);
588
+ let classGroupId;
589
+ if (hasPostfixModifier) {
590
+ const baseClassNameWithoutPostfix = baseClassName.substring(0, maybePostfixModifierPosition);
591
+ classGroupId = getClassGroupId(baseClassNameWithoutPostfix);
592
+ const classGroupIdWithPostfix = classGroupId && postfixLookupClassGroupIds[classGroupId] ? getClassGroupId(baseClassName) : undefined;
593
+ if (classGroupIdWithPostfix && classGroupIdWithPostfix !== classGroupId) {
594
+ classGroupId = classGroupIdWithPostfix;
595
+ hasPostfixModifier = false;
596
+ }
597
+ } else {
598
+ classGroupId = getClassGroupId(baseClassName);
599
+ }
537
600
  if (!classGroupId) {
538
601
  if (!hasPostfixModifier) {
539
602
  // Not a Tailwind class
@@ -664,6 +727,7 @@ const isNever = () => false;
664
727
  const isShadow = value => shadowRegex.test(value);
665
728
  const isImage = value => imageRegex.test(value);
666
729
  const isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);
730
+ const isNamedContainerQuery = value => value.startsWith('@container') && (value[10] === '/' && value[11] !== undefined || value[11] === 's' && value[16] !== undefined && value.startsWith('-size/', 10) || value[11] === 'n' && value[18] !== undefined && value.startsWith('-normal/', 10));
667
731
  const isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);
668
732
  const isArbitraryValue = value => arbitraryValueRegex.test(value);
669
733
  const isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);
@@ -835,6 +899,18 @@ const getDefaultConfig = () => {
835
899
  * @deprecated since Tailwind CSS v4.0.0
836
900
  */
837
901
  container: ['container'],
902
+ /**
903
+ * Container Type
904
+ * @see https://tailwindcss.com/docs/responsive-design#container-queries
905
+ */
906
+ 'container-type': [{
907
+ '@container': ['', 'normal', 'size', isArbitraryVariable, isArbitraryValue]
908
+ }],
909
+ /**
910
+ * Container Name
911
+ * @see https://tailwindcss.com/docs/responsive-design#named-containers
912
+ */
913
+ 'container-named': [isNamedContainerQuery],
838
914
  /**
839
915
  * Columns
840
916
  * @see https://tailwindcss.com/docs/columns
@@ -1763,6 +1839,13 @@ const getDefaultConfig = () => {
1763
1839
  indent: [{
1764
1840
  indent: scaleUnambiguousSpacing()
1765
1841
  }],
1842
+ /**
1843
+ * Tab Size
1844
+ * @see https://tailwindcss.com/docs/tab-size
1845
+ */
1846
+ 'tab-size': [{
1847
+ tab: [isInteger, isArbitraryVariable, isArbitraryValue]
1848
+ }],
1766
1849
  /**
1767
1850
  * Vertical Alignment
1768
1851
  * @see https://tailwindcss.com/docs/vertical-align
@@ -2970,6 +3053,13 @@ const getDefaultConfig = () => {
2970
3053
  * @see https://tailwindcss.com/docs/translate
2971
3054
  */
2972
3055
  'translate-none': ['translate-none'],
3056
+ /**
3057
+ * Zoom
3058
+ * @see https://tailwindcss.com/docs/zoom
3059
+ */
3060
+ zoom: [{
3061
+ zoom: [isInteger, isArbitraryVariable, isArbitraryValue]
3062
+ }],
2973
3063
  // ---------------------
2974
3064
  // --- Interactivity ---
2975
3065
  // ---------------------
@@ -3036,6 +3126,34 @@ const getDefaultConfig = () => {
3036
3126
  'scroll-behavior': [{
3037
3127
  scroll: ['auto', 'smooth']
3038
3128
  }],
3129
+ /**
3130
+ * Scrollbar Thumb Color
3131
+ * @see https://tailwindcss.com/docs/scrollbar-color
3132
+ */
3133
+ 'scrollbar-thumb-color': [{
3134
+ 'scrollbar-thumb': scaleColor()
3135
+ }],
3136
+ /**
3137
+ * Scrollbar Track Color
3138
+ * @see https://tailwindcss.com/docs/scrollbar-color
3139
+ */
3140
+ 'scrollbar-track-color': [{
3141
+ 'scrollbar-track': scaleColor()
3142
+ }],
3143
+ /**
3144
+ * Scrollbar Gutter
3145
+ * @see https://tailwindcss.com/docs/scrollbar-gutter
3146
+ */
3147
+ 'scrollbar-gutter': [{
3148
+ 'scrollbar-gutter': ['auto', 'stable', 'both']
3149
+ }],
3150
+ /**
3151
+ * Scrollbar Width
3152
+ * @see https://tailwindcss.com/docs/scrollbar-width
3153
+ */
3154
+ 'scrollbar-w': [{
3155
+ scrollbar: ['auto', 'thin', 'none']
3156
+ }],
3039
3157
  /**
3040
3158
  * Scroll Margin
3041
3159
  * @see https://tailwindcss.com/docs/scroll-margin
@@ -3294,6 +3412,7 @@ const getDefaultConfig = () => {
3294
3412
  }]
3295
3413
  },
3296
3414
  conflictingClassGroups: {
3415
+ 'container-named': ['container-type'],
3297
3416
  overflow: ['overflow-x', 'overflow-y'],
3298
3417
  overscroll: ['overscroll-x', 'overscroll-y'],
3299
3418
  inset: ['inset-x', 'inset-y', 'inset-bs', 'inset-be', 'start', 'end', 'top', 'right', 'bottom', 'left'],
@@ -3346,6 +3465,7 @@ const getDefaultConfig = () => {
3346
3465
  conflictingClassGroupModifiers: {
3347
3466
  'font-size': ['leading']
3348
3467
  },
3468
+ postfixLookupClassGroups: ['container-type'],
3349
3469
  orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']
3350
3470
  };
3351
3471
  };
@@ -3354,15 +3474,14 @@ const twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);
3354
3474
  const mergeStyles = (...inputs) => twMerge(clsx(inputs));
3355
3475
 
3356
3476
  function styleInject(css, ref) {
3357
- if ( ref === void 0 ) ref = {};
3477
+ if (ref === void 0) ref = {};
3358
3478
  var insertAt = ref.insertAt;
3359
-
3360
- if (typeof document === 'undefined') { return; }
3361
-
3479
+ if (typeof document === 'undefined') {
3480
+ return;
3481
+ }
3362
3482
  var head = document.head || document.getElementsByTagName('head')[0];
3363
3483
  var style = document.createElement('style');
3364
3484
  style.type = 'text/css';
3365
-
3366
3485
  if (insertAt === 'top') {
3367
3486
  if (head.firstChild) {
3368
3487
  head.insertBefore(style, head.firstChild);
@@ -3372,7 +3491,6 @@ function styleInject(css, ref) {
3372
3491
  } else {
3373
3492
  head.appendChild(style);
3374
3493
  }
3375
-
3376
3494
  if (style.styleSheet) {
3377
3495
  style.styleSheet.cssText = css;
3378
3496
  } else {
@@ -3380,18 +3498,18 @@ function styleInject(css, ref) {
3380
3498
  }
3381
3499
  }
3382
3500
 
3383
- var css_248z = "/*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){.file-selector-scope *,.file-selector-scope ::backdrop,.file-selector-scope :after,.file-selector-scope :before{--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{.file-selector-scope,.file-selector-scope :host{--color-yellow-400:oklch(85.2% .199 91.936);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--spacing:.25rem;--font-weight-bold:700;--radius-md:.375rem}}@layer base,components;@layer utilities{.file-selector-scope .flex{display:flex}.file-selector-scope .h-full{height:100%}.file-selector-scope .min-h-0{min-height:calc(var(--spacing)*0)}.file-selector-scope .min-h-\\[120px\\]{min-height:120px}.file-selector-scope .w-full{width:100%}.file-selector-scope .flex-1{flex:1}.file-selector-scope .cursor-pointer{cursor:pointer}.file-selector-scope .flex-col{flex-direction:column}.file-selector-scope .rounded-md{border-radius:var(--radius-md)}.file-selector-scope .border{border-style:var(--tw-border-style);border-width:1px}.file-selector-scope .border-2{border-style:var(--tw-border-style);border-width:2px}.file-selector-scope .border-dashed{--tw-border-style:dashed;border-style:dashed}.file-selector-scope .border-gray-300{border-color:var(--color-gray-300)}.file-selector-scope .border-gray-500{border-color:var(--color-gray-500)}.file-selector-scope .border-yellow-400{border-color:var(--color-yellow-400)}.file-selector-scope .bg-inherit{background-color:inherit}.file-selector-scope .p-4{padding:calc(var(--spacing)*4)}.file-selector-scope .px-4{padding-inline:calc(var(--spacing)*4)}.file-selector-scope .py-2{padding-block:calc(var(--spacing)*2)}.file-selector-scope .text-center{text-align:center}.file-selector-scope .font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.file-selector-scope .text-gray-600{color:var(--color-gray-600)}.file-selector-scope .text-inherit{color:inherit}.file-selector-scope .shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media (hover:hover){.file-selector-scope .hover\\:border-gray-400:hover{border-color:var(--color-gray-400)}}}.file-selector-scope .hiddenInput{display:none}@property --tw-border-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:\"*\";inherits:false}@property --tw-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\"*\";inherits:false}@property --tw-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\"*\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\"*\";inherits:false}@property --tw-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\"*\";inherits:false}@property --tw-inset-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\"*\";inherits:false}@property --tw-ring-offset-width{syntax:\"<length>\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\"*\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}";
3501
+ var css_248z = "/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){.file-selector-scope *,.file-selector-scope ::backdrop,.file-selector-scope :after,.file-selector-scope :before{--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{.file-selector-scope,.file-selector-scope :host{--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-zinc-600:oklch(44.2% .017 285.786);--spacing:.25rem;--font-weight-bold:700;--radius-md:.375rem}}@layer base,components;@layer utilities{.file-selector-scope .flex{display:flex}.file-selector-scope .h-full{height:100%}.file-selector-scope .min-h-0{min-height:0}.file-selector-scope .min-h-\\[120px\\]{min-height:120px}.file-selector-scope .w-full{width:100%}.file-selector-scope .flex-1{flex:1}.file-selector-scope .cursor-pointer{cursor:pointer}.file-selector-scope .flex-col{flex-direction:column}.file-selector-scope .rounded-md{border-radius:var(--radius-md)}.file-selector-scope .border{border-style:var(--tw-border-style);border-width:1px}.file-selector-scope .border-2{border-style:var(--tw-border-style);border-width:2px}.file-selector-scope .border-dashed{--tw-border-style:dashed;border-style:dashed}.file-selector-scope .border-gray-300{border-color:var(--color-gray-300)}.file-selector-scope .border-gray-500{border-color:var(--color-gray-500)}.file-selector-scope .border-zinc-600{border-color:var(--color-zinc-600)}.file-selector-scope .bg-inherit{background-color:inherit}.file-selector-scope .p-4{padding:calc(var(--spacing)*4)}.file-selector-scope .px-4{padding-inline:calc(var(--spacing)*4)}.file-selector-scope .py-2{padding-block:calc(var(--spacing)*2)}.file-selector-scope .text-center{text-align:center}.file-selector-scope .font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.file-selector-scope .text-gray-600{color:var(--color-gray-600)}.file-selector-scope .text-inherit{color:inherit}.file-selector-scope .shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media (hover:hover){.file-selector-scope .hover\\:border-gray-400:hover{border-color:var(--color-gray-400)}}}.file-selector-scope .hiddenInput{display:none}@property --tw-border-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:\"*\";inherits:false}@property --tw-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\"*\";inherits:false}@property --tw-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\"*\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\"*\";inherits:false}@property --tw-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\"*\";inherits:false}@property --tw-inset-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\"*\";inherits:false}@property --tw-ring-offset-width{syntax:\"<length>\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\"*\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}";
3384
3502
  styleInject(css_248z);
3385
3503
 
3386
- const withDragDefaults = (handler) => {
3387
- return (e) => {
3388
- e.preventDefault();
3389
- e.stopPropagation();
3390
- handler(e);
3391
- };
3504
+ const withDragDefaults = handler => {
3505
+ return e => {
3506
+ e.preventDefault();
3507
+ e.stopPropagation();
3508
+ handler(e);
3509
+ };
3392
3510
  };
3393
3511
 
3394
- const defaultAccept = ".png, .jpg, .jpeg, .pdf, .svg, image/svg+xml, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document";
3512
+ const defaultAccept = buildAcceptString(defaultTypeExtensions);
3395
3513
  const SCOPE_CLASS = "file-selector-scope";
3396
3514
  const defaultInputClassName = "hiddenInput";
3397
3515
  const defaultClickableAreaClassName = "dropzone border-dashed border-2 border-gray-500 p-4 rounded-md text-center cursor-pointer bg-inherit hover:border-gray-400 text-inherit font-bold py-2 px-4";
@@ -3399,284 +3517,318 @@ const defaultDropZoneWrapperClassName = "flex flex-col min-h-[120px] border bord
3399
3517
  const defaultMessageParagraphClassName = "text-center text-gray-600";
3400
3518
  const defaultMessageParagraph = "Drag 'n' drop some files here, or click to select files";
3401
3519
  const defaultAriaLabel = "File upload drop zone";
3402
- const defaultAriaDescribedBy = undefined;
3403
3520
  const defaultAriaLabelButton = "Choose files to upload";
3404
- const FileSelectorComponent = ({ inputId, accept = defaultAccept, messageParagraph = defaultMessageParagraph, inputClassName, clickableAreaClassName, dropZoneWrapperClassName, messageParagraphClassName, ariaLabel = defaultAriaLabel, ariaDescribedBy = defaultAriaDescribedBy, ariaLabelButton = defaultAriaLabelButton, ariaLabelledBy, // New prop for button
3405
- onChange, onDragOver, onDrop, onDragEnter, onDragLeave, }) => {
3406
- const fileInputRef = useRef(null);
3407
- const messageId = useId();
3408
- //Note, eslint doesn't like that I'm wrapping the anonymous function in withDragDefaults. Ignore for now
3409
- // eslint-disable-next-line react-hooks/exhaustive-deps
3410
- const internalOnClick = useCallback(withDragDefaults(() => {
3411
- fileInputRef.current?.click();
3412
- }), []);
3413
- // eslint-disable-next-line react-hooks/exhaustive-deps
3414
- const internalOnDrop = useCallback(withDragDefaults((e) => {
3415
- onDrop(e);
3416
- }), [onDrop]);
3417
- // eslint-disable-next-line react-hooks/exhaustive-deps
3418
- const internalOnDragOver = useCallback(withDragDefaults((e) => {
3419
- onDragOver(e);
3420
- }), [onDragOver]);
3421
- // eslint-disable-next-line react-hooks/exhaustive-deps
3422
- const internalOnDragEnter = useCallback(withDragDefaults((e) => {
3423
- onDragEnter(e);
3424
- }), [onDragEnter]);
3425
- // eslint-disable-next-line react-hooks/exhaustive-deps
3426
- const internalOnDragLeave = useCallback(withDragDefaults((e) => {
3427
- onDragLeave(e);
3428
- }), [onDragLeave]);
3429
- const inputClasses = mergeStyles(defaultInputClassName, inputClassName);
3430
- const resolvedClickableAreaClassName = mergeStyles(clickableAreaClassName ?? defaultClickableAreaClassName, "w-full h-full flex-1 min-h-0");
3431
- const resolvedDropZoneWrapperClassName = mergeStyles(dropZoneWrapperClassName ?? defaultDropZoneWrapperClassName);
3432
- const resolvedMessageParagraphClassName = mergeStyles(messageParagraphClassName ?? defaultMessageParagraphClassName);
3433
- const resolvedInputId = inputId ?? messageId;
3434
- return (jsxs("div", { role: "group", "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, className: mergeStyles(SCOPE_CLASS, resolvedDropZoneWrapperClassName), draggable: false, children: [jsx("button", { type: "button", className: resolvedClickableAreaClassName, onClick: internalOnClick, onDragOver: internalOnDragOver, onDrop: internalOnDrop, onDragEnter: internalOnDragEnter, onDragLeave: internalOnDragLeave, draggable: false, "aria-label": ariaLabelButton, "aria-labelledby": ariaLabelledBy ?? messageId, "aria-controls": resolvedInputId, "aria-haspopup": "dialog", children: jsx("p", { id: messageId, className: resolvedMessageParagraphClassName, children: messageParagraph }) }), jsx("input", { ref: fileInputRef, id: resolvedInputId, type: "file", className: inputClasses, onChange: onChange, accept: accept, multiple: true, "aria-hidden": "true", tabIndex: -1 })] }));
3521
+ const FileSelector = ({
3522
+ inputId,
3523
+ accept = defaultAccept,
3524
+ messageParagraph = defaultMessageParagraph,
3525
+ inputClassName,
3526
+ clickableAreaClassName,
3527
+ dropZoneWrapperClassName,
3528
+ messageParagraphClassName,
3529
+ ariaLabel = defaultAriaLabel,
3530
+ ariaDescribedBy,
3531
+ ariaLabelButton = defaultAriaLabelButton,
3532
+ onChange,
3533
+ onDragOver,
3534
+ onDrop,
3535
+ onDragEnter,
3536
+ onDragLeave
3537
+ }) => {
3538
+ const fileInputRef = useRef(null);
3539
+ const messageId = useId();
3540
+
3541
+ //Note, eslint doesn't like that I'm wrapping the anonymous function in withDragDefaults. Ignore for now
3542
+
3543
+ const internalOnClick = withDragDefaults(() => {
3544
+ fileInputRef.current?.click();
3545
+ });
3546
+ const internalOnDrop = withDragDefaults(e => {
3547
+ onDrop(e);
3548
+ });
3549
+ const internalOnDragOver = withDragDefaults(e_0 => {
3550
+ onDragOver(e_0);
3551
+ });
3552
+ const internalOnDragEnter = withDragDefaults(e_1 => {
3553
+ onDragEnter(e_1);
3554
+ });
3555
+ const internalOnDragLeave = withDragDefaults(e_2 => {
3556
+ onDragLeave(e_2);
3557
+ });
3558
+ const inputClasses = mergeStyles(defaultInputClassName, inputClassName);
3559
+ const resolvedClickableAreaClassName = mergeStyles(clickableAreaClassName ?? defaultClickableAreaClassName, "w-full h-full flex-1 min-h-0");
3560
+ const resolvedDropZoneWrapperClassName = mergeStyles(dropZoneWrapperClassName ?? defaultDropZoneWrapperClassName);
3561
+ const resolvedMessageParagraphClassName = mergeStyles(messageParagraphClassName ?? defaultMessageParagraphClassName);
3562
+ const resolvedInputId = inputId ?? messageId;
3563
+ return /*#__PURE__*/jsxs("div", {
3564
+ role: "group",
3565
+ "aria-label": ariaLabel,
3566
+ "aria-describedby": ariaDescribedBy,
3567
+ className: mergeStyles(SCOPE_CLASS, resolvedDropZoneWrapperClassName),
3568
+ draggable: false,
3569
+ children: [/*#__PURE__*/jsx("button", {
3570
+ type: "button",
3571
+ className: resolvedClickableAreaClassName,
3572
+ onClick: internalOnClick,
3573
+ onDragOver: internalOnDragOver,
3574
+ onDrop: internalOnDrop,
3575
+ onDragEnter: internalOnDragEnter,
3576
+ onDragLeave: internalOnDragLeave,
3577
+ draggable: false,
3578
+ "aria-label": ariaLabelButton,
3579
+ "aria-controls": resolvedInputId,
3580
+ children: /*#__PURE__*/jsx("p", {
3581
+ id: messageId,
3582
+ className: resolvedMessageParagraphClassName,
3583
+ children: messageParagraph
3584
+ })
3585
+ }), /*#__PURE__*/jsx("input", {
3586
+ ref: fileInputRef,
3587
+ id: resolvedInputId,
3588
+ type: "file",
3589
+ className: inputClasses,
3590
+ onChange: onChange,
3591
+ accept: accept,
3592
+ multiple: true,
3593
+ "aria-hidden": "true",
3594
+ tabIndex: -1
3595
+ })]
3596
+ });
3435
3597
  };
3436
- const FileSelector = memo(FileSelectorComponent);
3437
3598
  FileSelector.displayName = "FileSelector";
3438
3599
 
3439
- const buildAcceptString = (acceptedTypes) => {
3440
- const seen = new Set();
3441
- // Prefer extensions (input accept supports extensions and MIME types)
3442
- for (const ext of Object.values(acceptedTypes)) {
3443
- const trimmed = ext.trim();
3444
- if (!trimmed)
3445
- continue;
3446
- if (!trimmed.startsWith(".")) {
3447
- const withDot = `.${trimmed}`;
3448
- if (!seen.has(withDot))
3449
- seen.add(withDot);
3450
- continue;
3451
- }
3452
- if (!seen.has(trimmed))
3453
- seen.add(trimmed);
3454
- }
3455
- // Add MIME keys too (helps some pickers)
3456
- for (const mime of Object.keys(acceptedTypes)) {
3457
- const trimmed = mime.trim();
3458
- if (!trimmed)
3459
- continue;
3460
- if (!seen.has(trimmed))
3461
- seen.add(trimmed);
3600
+ const defaultBorder = "border-gray-500";
3601
+ const defaultAlternateBorder = "border-zinc-600";
3602
+ const useFileSelector = ({
3603
+ maximumUploadCount: maximumUploadCount$1 = maximumUploadCount,
3604
+ maximumFileSize: maximumFileSize$1 = maximumFileSize,
3605
+ acceptedTypes = defaultTypeExtensions
3606
+ } = {}) => {
3607
+ const [, setUpdateTrigger] = useState(0);
3608
+ const [validFiles, SetValidFiles] = useState([]);
3609
+ const [invalidFiles, SetInvalidFiles] = useState([]);
3610
+ const dragDepthRef = useRef(0);
3611
+ const callIdRef = useRef(0);
3612
+ const borderClassRef = useRef(defaultAlternateBorder);
3613
+ const maxUploadErrorRef = useRef({
3614
+ status: false,
3615
+ message: ""
3616
+ });
3617
+ const maxFileSizeErrorRef = useRef({
3618
+ status: false,
3619
+ message: ""
3620
+ });
3621
+ const setMaximumUploadsExceeded = (status = false, fileCount, maximumUploads) => {
3622
+ maxUploadErrorRef.current.status = status;
3623
+ maxUploadErrorRef.current.message = status ? `You have attempted to upload ${fileCount} files. The maximum allowable uploads for this feature is ${maximumUploads}` : "";
3624
+ setUpdateTrigger(state => state + 1);
3625
+ };
3626
+ const setMaximumFileSizeExceeded = (status_0 = false) => {
3627
+ maxFileSizeErrorRef.current.status = status_0;
3628
+ maxFileSizeErrorRef.current.message = status_0 ? `You have attempted to upload a file(s) that exceeds the maximum size of ${formatFileSize(maximumFileSize$1)}` : "";
3629
+ setUpdateTrigger(state_0 => state_0 + 1);
3630
+ };
3631
+ const clearBlobs = () => {
3632
+ let i = 0;
3633
+ while (i < validFiles.length) {
3634
+ const file = validFiles[i].file;
3635
+ if (file instanceof File) {
3636
+ clearBlobFromMemory(file);
3637
+ }
3638
+ i++;
3462
3639
  }
3463
- return Array.from(seen).join(", ");
3464
- };
3465
- const useFileSelector = ({ maximumUploadCount: maximumUploadCount$1 = maximumUploadCount, maximumFileSize: maximumFileSize$1 = maximumFileSize, acceptedTypes = defaultTypeExtensions, } = {}) => {
3466
- //State
3467
- //Trigger a re-render
3468
- const [, setUpdateTrigger] = useState(0);
3469
- const [validFiles, SetValidFiles] = useState([]);
3470
- const [invalidFiles, SetInvalidFiles] = useState([]);
3471
- //Refs
3472
- const maxUploadErrorRef = useRef({
3473
- status: false,
3474
- message: "",
3475
- });
3476
- const maxFileSizeErrorRef = useRef({
3477
- status: false,
3478
- message: "",
3479
- });
3480
- const setMaximumUploadsExceeded = useCallback((status = false, fileCount, maximumUploads) => {
3481
- maxUploadErrorRef.current.status = status;
3482
- maxUploadErrorRef.current.message = status
3483
- ? `You have attempted to upload ${fileCount} files. The maximum allowable uploads for this feature is ${maximumUploads}`
3484
- : "";
3485
- setUpdateTrigger((state) => state + 1);
3486
- }, []);
3487
- const setMaximumFileSizeExceeded = useCallback((status = false) => {
3488
- maxFileSizeErrorRef.current.status = status;
3489
- maxFileSizeErrorRef.current.message = status
3490
- ? `You have attempted upload a file(s) that exceeds the maximum size of ${printableMaximumFileSize}`
3491
- : "";
3492
- setUpdateTrigger((state) => state + 1);
3493
- }, []);
3494
- const clearBlobs = useCustomCallback(() => {
3495
- //Remove any blobs created in memory
3496
- let i = 0;
3497
- while (i < validFiles.length) {
3498
- const file = validFiles[i].file;
3499
- if (file instanceof File) {
3500
- clearBlobFromMemory(file);
3501
- }
3502
- i++;
3503
- }
3504
- }, [validFiles]);
3505
- const clearCache = useCustomCallback(() => {
3506
- //Clear any blobs held in memory
3507
- clearBlobs();
3508
- SetInvalidFiles([]);
3509
- SetValidFiles([]);
3510
- }, [clearBlobs]);
3511
- useEffect(() => {
3512
- return () => {
3513
- clearCache();
3514
- };
3515
- }, [clearCache]);
3516
- const onCancel = useCustomCallback(() => {
3517
- clearCache();
3518
- }, [clearCache]);
3519
- const getValidFileStreams = useCallback(() => (validFiles.length ? validFiles.map(({ file }) => file) : []), [validFiles]);
3520
- //Main Engine to deal with files
3521
- const processFiles = useCustomCallback((files) => {
3522
- const maxFileSizeCheck = checkFilesMaximumSize(files, maximumFileSize$1) && !maxFileSizeErrorRef.current.status;
3523
- const maxUploadCountCheck = typeof maximumUploadCount$1 !== "undefined"
3524
- ? !maxUploadErrorRef.current.status && files.length > maximumUploadCount$1
3525
- : false;
3526
- if (maxFileSizeCheck || maxUploadCountCheck) {
3527
- if (maxFileSizeCheck)
3528
- setMaximumFileSizeExceeded(true);
3529
- if (maxUploadCountCheck)
3530
- setMaximumUploadsExceeded(true, files.length, maximumUploadCount$1);
3531
- onCancel();
3532
- }
3533
- else {
3534
- const valid = [];
3535
- const invalid = [];
3536
- let i = 0;
3537
- while (i < files.length) {
3538
- const file = files[i];
3539
- if (isValidFileType(file, acceptedTypes)) {
3540
- valid.push(SvgXmlnsAttributeCheck(file, acceptedTypes));
3541
- }
3542
- else {
3543
- invalid.push(file);
3544
- }
3545
- i++;
3546
- }
3547
- if (valid.length > 0) {
3548
- Promise.all(valid)
3549
- .then((results) => {
3550
- const filteredResults = [];
3551
- let j = 0;
3552
- while (j < results.length) {
3553
- const result = results[j];
3554
- if (result !== null) {
3555
- filteredResults.push(result);
3556
- }
3557
- j++;
3558
- }
3559
- SetValidFiles(filteredResults);
3560
- })
3561
- .catch((error) => {
3562
- console.error("File processing error:", error);
3563
- // Optionally expose error state to engineers
3564
- SetInvalidFiles(invalid.concat(files)); // Treat all as invalid on error
3565
- });
3566
- }
3567
- if (invalid.length > 0) {
3568
- SetInvalidFiles(invalid);
3569
- }
3640
+ };
3641
+ const clearCache = () => {
3642
+ clearBlobs();
3643
+ SetInvalidFiles([]);
3644
+ SetValidFiles([]);
3645
+ setMaximumUploadsExceeded(false);
3646
+ setMaximumFileSizeExceeded(false);
3647
+ };
3648
+ const onCancel = () => {
3649
+ clearCache();
3650
+ };
3651
+ const getValidFileStreams = () => validFiles.length ? validFiles.map(({
3652
+ file: file_0
3653
+ }) => file_0) : [];
3654
+ const processFiles = files => {
3655
+ const callId = ++callIdRef.current;
3656
+ const maxFileSizeCheck = checkFilesMaximumSize(files, maximumFileSize$1);
3657
+ if (maxFileSizeCheck) {
3658
+ if (!maxFileSizeErrorRef.current.status) {
3659
+ setMaximumFileSizeExceeded(true);
3660
+ }
3661
+ SetValidFiles([]);
3662
+ SetInvalidFiles([]);
3663
+ } else {
3664
+ const valid = [];
3665
+ const invalid = [];
3666
+ let i_0 = 0;
3667
+ while (i_0 < files.length) {
3668
+ const file_1 = files[i_0];
3669
+ if (isValidFileType(file_1, acceptedTypes)) {
3670
+ valid.push({
3671
+ file: file_1,
3672
+ promise: svgXmlnsAttributeCheck(file_1, acceptedTypes)
3673
+ });
3674
+ } else {
3675
+ invalid.push(file_1);
3570
3676
  }
3571
- }, [
3572
- acceptedTypes,
3573
- maximumFileSize$1,
3574
- maximumUploadCount$1,
3575
- setMaximumFileSizeExceeded,
3576
- setMaximumUploadsExceeded,
3577
- onCancel,
3578
- ]);
3579
- const onRemoveFile = useCustomCallback((index) => {
3580
- const updatedValidFiles = [];
3581
- let i = 0;
3582
- while (i < validFiles.length) {
3583
- if (i === index) {
3584
- const file = validFiles[i].file;
3585
- if (file instanceof File) {
3586
- clearBlobFromMemory(file);
3587
- }
3588
- }
3589
- else {
3590
- updatedValidFiles.push(validFiles[i]);
3677
+ i_0++;
3678
+ }
3679
+ if (valid.length > 0) {
3680
+ Promise.all(valid.map(v => v.promise)).then(results => {
3681
+ if (callId !== callIdRef.current) return;
3682
+ const filteredResults = [];
3683
+ const nullFiles = [];
3684
+ let j = 0;
3685
+ while (j < results.length) {
3686
+ const result = results[j];
3687
+ if (result !== null) {
3688
+ filteredResults.push(result);
3689
+ } else {
3690
+ nullFiles.push(valid[j].file);
3591
3691
  }
3592
- i++;
3593
- }
3594
- SetValidFiles(updatedValidFiles);
3595
- }, [validFiles]);
3596
- const onIdChange = useCallback((index, id, files) => {
3597
- const updatedValidFiles = files.map((fileData, i) => {
3598
- if (i === index) {
3599
- const renamedFile = checkFile(id, fileData.file);
3600
- return { ...fileData, file: renamedFile, id };
3692
+ j++;
3693
+ }
3694
+ if (nullFiles.length > 0) {
3695
+ SetInvalidFiles(state_1 => state_1.concat(nullFiles));
3696
+ }
3697
+ SetValidFiles(current => {
3698
+ if (current.length + filteredResults.length > maximumUploadCount$1) {
3699
+ setMaximumUploadsExceeded(true, current.length + filteredResults.length, maximumUploadCount$1);
3700
+ return current;
3601
3701
  }
3602
- return fileData;
3702
+ return current.concat(filteredResults);
3703
+ });
3704
+ }).catch(() => {
3705
+ SetInvalidFiles(state_2 => state_2.concat(valid.map(v_0 => v_0.file)));
3603
3706
  });
3604
- SetValidFiles(updatedValidFiles);
3605
- }, []);
3606
- const onInputChange = useCustomCallback((event) => {
3607
- const files = Array.from(event.target.files || []);
3608
- processFiles(files);
3609
- }, [processFiles]);
3610
- //Drag and Drop
3611
- const onDrop = useCustomCallback((e) => {
3612
- e.preventDefault();
3613
- processFiles(Array.from(e.dataTransfer.files));
3614
- }, [processFiles]);
3615
- const onDragOver = useCallback((e) => {
3616
- e.preventDefault();
3617
- e.dataTransfer.dropEffect = "copy";
3618
- }, []);
3619
- const onDragEnter = useCallback((e) => {
3620
- e.preventDefault();
3621
- const { classList } = e.currentTarget;
3622
- classList.add("border-yellow-400");
3623
- classList.remove("border-silver-600");
3624
- }, []);
3625
- const onDragLeave = useCallback((e) => {
3626
- e.preventDefault();
3627
- const currentTarget = e.currentTarget;
3628
- const timeoutId = setTimeout(() => {
3629
- if (currentTarget && !currentTarget.contains(e.relatedTarget)) {
3630
- const { classList } = currentTarget;
3631
- classList.remove("border-yellow-400");
3632
- classList.add("border-silver-600");
3633
- }
3634
- clearTimeout(timeoutId);
3635
- }, 200);
3636
- }, []);
3637
- const createFileSelectorComponent = (handlers) => {
3638
- const handlerProps = {
3639
- onChange: handlers.onInputChange,
3640
- onDragOver: handlers.onDragOver,
3641
- onDrop: handlers.onDrop,
3642
- onDragEnter: handlers.onDragEnter,
3643
- onDragLeave: handlers.onDragLeave,
3707
+ }
3708
+ if (invalid.length > 0) {
3709
+ SetInvalidFiles(state_3 => state_3.concat(invalid));
3710
+ }
3711
+ }
3712
+ };
3713
+ const onRemoveFile = index => {
3714
+ const updatedValidFiles = [];
3715
+ let i_1 = 0;
3716
+ while (i_1 < validFiles.length) {
3717
+ if (i_1 === index) {
3718
+ const file_2 = validFiles[i_1].file;
3719
+ if (file_2 instanceof File) {
3720
+ clearBlobFromMemory(file_2);
3721
+ }
3722
+ } else {
3723
+ updatedValidFiles.push(validFiles[i_1]);
3724
+ }
3725
+ i_1++;
3726
+ }
3727
+ SetValidFiles(updatedValidFiles);
3728
+ };
3729
+ const onIdChange = (index_0, id, files_0) => {
3730
+ const updatedValidFiles_0 = files_0.map((fileData, i_2) => {
3731
+ if (i_2 === index_0) {
3732
+ if (fileData.file instanceof File) clearBlobFromMemory(fileData.file);
3733
+ const renamedFile = renameFile(id, fileData.file);
3734
+ const newUrl = renamedFile instanceof File ? createUrlString(renamedFile) : fileData.url;
3735
+ return {
3736
+ ...fileData,
3737
+ file: renamedFile,
3738
+ id,
3739
+ url: newUrl
3644
3740
  };
3645
- const internalProps = { accept: handlers.accept };
3646
- const Component = (props) => (jsx(FileSelector, { ...props, ...handlerProps, ...internalProps }));
3647
- Component.displayName = "FileSelectorWrapper";
3648
- return Component;
3649
- };
3650
- // Then in the hook's return:
3651
- const accept = useMemo(() => buildAcceptString(acceptedTypes), [acceptedTypes]);
3652
- const BoundFileSelector = useMemo(() => createFileSelectorComponent({
3653
- onInputChange,
3654
- onDragOver,
3655
- onDrop,
3656
- onDragEnter,
3657
- onDragLeave,
3658
- accept,
3659
- }), [onInputChange, onDragOver, onDrop, onDragEnter, onDragLeave, accept]);
3660
- return {
3661
- //Properties
3662
- validFiles,
3663
- invalidFiles,
3664
- //Methods
3665
- clearCache,
3666
- getValidFileStreams,
3667
- onCancel,
3668
- onIdChange,
3669
- onRemoveFile,
3670
- clearBlobs,
3671
- clearBlob: clearBlobFromMemory,
3672
- //Errors
3673
- maxUploadError: maxUploadErrorRef.current,
3674
- maxFileSizeError: maxFileSizeErrorRef.current,
3675
- setMaximumFileSizeExceeded,
3676
- setMaximumUploadsExceeded,
3677
- //Component - Export the FileSelector
3678
- FileSelector: BoundFileSelector,
3679
- };
3741
+ }
3742
+ return fileData;
3743
+ });
3744
+ SetValidFiles(updatedValidFiles_0);
3745
+ };
3746
+ const onInputChange = event => {
3747
+ const files_1 = Array.from(event.target.files || []);
3748
+ processFiles(files_1);
3749
+ event.target.value = "";
3750
+ };
3751
+ const onDrop = e => {
3752
+ e.preventDefault();
3753
+ dragDepthRef.current = 0;
3754
+ const {
3755
+ classList
3756
+ } = e.currentTarget;
3757
+ classList.remove(defaultBorder);
3758
+ classList.add(borderClassRef.current);
3759
+ processFiles(Array.from(e.dataTransfer.files));
3760
+ };
3761
+ const onDragOver = e_0 => {
3762
+ e_0.preventDefault();
3763
+ e_0.dataTransfer.dropEffect = "copy";
3764
+ };
3765
+ const onDragEnter = e_1 => {
3766
+ e_1.preventDefault();
3767
+ dragDepthRef.current++;
3768
+ const {
3769
+ classList: classList_0
3770
+ } = e_1.currentTarget;
3771
+ classList_0.remove(borderClassRef.current);
3772
+ classList_0.add(defaultBorder);
3773
+ };
3774
+ const onDragLeave = e_2 => {
3775
+ e_2.preventDefault();
3776
+ dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
3777
+ if (dragDepthRef.current > 0) return;
3778
+ const {
3779
+ classList: classList_1
3780
+ } = e_2.currentTarget;
3781
+ classList_1.remove(defaultBorder);
3782
+ classList_1.add(borderClassRef.current);
3783
+ };
3784
+ const acceptRef = useRef(buildAcceptString(acceptedTypes));
3785
+ const BoundFileSelector = ({
3786
+ inputId,
3787
+ messageParagraph,
3788
+ inputClassName,
3789
+ clickableAreaClassName,
3790
+ dropZoneWrapperClassName,
3791
+ messageParagraphClassName,
3792
+ ariaLabel,
3793
+ ariaDescribedBy,
3794
+ ariaLabelButton
3795
+ }) => {
3796
+ borderClassRef.current = clickableAreaClassName ?? defaultAlternateBorder;
3797
+ return /*#__PURE__*/jsx(FileSelector, {
3798
+ inputId: inputId,
3799
+ messageParagraph: messageParagraph,
3800
+ inputClassName: inputClassName,
3801
+ clickableAreaClassName: clickableAreaClassName,
3802
+ dropZoneWrapperClassName: dropZoneWrapperClassName,
3803
+ messageParagraphClassName: messageParagraphClassName,
3804
+ ariaLabel: ariaLabel,
3805
+ ariaDescribedBy: ariaDescribedBy,
3806
+ ariaLabelButton: ariaLabelButton,
3807
+ onChange: onInputChange,
3808
+ onDragOver: onDragOver,
3809
+ onDrop: onDrop,
3810
+ onDragEnter: onDragEnter,
3811
+ onDragLeave: onDragLeave,
3812
+ accept: acceptRef.current
3813
+ });
3814
+ };
3815
+ BoundFileSelector.displayName = "FileSelectorWrapper";
3816
+ return {
3817
+ validFiles,
3818
+ invalidFiles,
3819
+ clearCache,
3820
+ getValidFileStreams,
3821
+ onCancel,
3822
+ onIdChange,
3823
+ onRemoveFile,
3824
+ clearBlobs,
3825
+ clearBlob: clearBlobFromMemory,
3826
+ maxUploadError: maxUploadErrorRef.current,
3827
+ maxFileSizeError: maxFileSizeErrorRef.current,
3828
+ setMaximumFileSizeExceeded,
3829
+ setMaximumUploadsExceeded,
3830
+ FileSelector: BoundFileSelector
3831
+ };
3680
3832
  };
3681
3833
 
3682
3834
  export { useFileSelector };