@zag-js/file-utils 1.15.1 → 1.15.3

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.d.mts CHANGED
@@ -2,11 +2,6 @@ declare const getFileEntries: (items: DataTransferItemList, traverseDirectories:
2
2
 
3
3
  declare function dataURItoBlob(uri: string): Blob;
4
4
 
5
- declare function isMSEdge(win: Window): win is Window & {
6
- navigator: {
7
- msSaveOrOpenBlob: Function;
8
- };
9
- };
10
5
  interface DownloadFileOptions {
11
6
  /**
12
7
  * The name of the file
@@ -23,7 +18,19 @@ interface DownloadFileOptions {
23
18
  /**
24
19
  * The window environment
25
20
  */
26
- win: typeof window;
21
+ win?: typeof window;
22
+ /**
23
+ * Whether to add a BOM (Byte Order Mark) to the file.
24
+ * Useful for CSV files.
25
+ */
26
+ appendBOM?: boolean;
27
+ /**
28
+ * The timeout in milliseconds to revoke the object URL.
29
+ * This is a safeguard for when the browser has not finished reading the
30
+ * file data from memory before the URL is revoked.
31
+ * @default 0
32
+ */
33
+ revokeTimeout?: number;
27
34
  }
28
35
  declare function downloadFile(options: DownloadFileOptions): void;
29
36
 
@@ -50,4 +57,4 @@ declare function isValidFileSize(file: File, minSize?: number, maxSize?: number)
50
57
 
51
58
  declare function isValidFileType(file: File, accept: string | undefined): [boolean, FileError | null];
52
59
 
53
- export { type ApplicationFileMimeType, type AudioFileMimeType, type DownloadFileOptions, type FileError, type FileMimeType, type FileMimeTypeGroup, type FontFileMimeType, type ImageFileMimeType, type TextFileMimeType, type VideoFileMimeType, dataURItoBlob, downloadFile, getAcceptAttrString, getFileDataUrl, getFileEntries, getTotalFileSize, isFileEqual, isMSEdge, isValidFileSize, isValidFileType };
60
+ export { type ApplicationFileMimeType, type AudioFileMimeType, type DownloadFileOptions, type FileError, type FileMimeType, type FileMimeTypeGroup, type FontFileMimeType, type ImageFileMimeType, type TextFileMimeType, type VideoFileMimeType, dataURItoBlob, downloadFile, getAcceptAttrString, getFileDataUrl, getFileEntries, getTotalFileSize, isFileEqual, isValidFileSize, isValidFileType };
package/dist/index.d.ts CHANGED
@@ -2,11 +2,6 @@ declare const getFileEntries: (items: DataTransferItemList, traverseDirectories:
2
2
 
3
3
  declare function dataURItoBlob(uri: string): Blob;
4
4
 
5
- declare function isMSEdge(win: Window): win is Window & {
6
- navigator: {
7
- msSaveOrOpenBlob: Function;
8
- };
9
- };
10
5
  interface DownloadFileOptions {
11
6
  /**
12
7
  * The name of the file
@@ -23,7 +18,19 @@ interface DownloadFileOptions {
23
18
  /**
24
19
  * The window environment
25
20
  */
26
- win: typeof window;
21
+ win?: typeof window;
22
+ /**
23
+ * Whether to add a BOM (Byte Order Mark) to the file.
24
+ * Useful for CSV files.
25
+ */
26
+ appendBOM?: boolean;
27
+ /**
28
+ * The timeout in milliseconds to revoke the object URL.
29
+ * This is a safeguard for when the browser has not finished reading the
30
+ * file data from memory before the URL is revoked.
31
+ * @default 0
32
+ */
33
+ revokeTimeout?: number;
27
34
  }
28
35
  declare function downloadFile(options: DownloadFileOptions): void;
29
36
 
@@ -50,4 +57,4 @@ declare function isValidFileSize(file: File, minSize?: number, maxSize?: number)
50
57
 
51
58
  declare function isValidFileType(file: File, accept: string | undefined): [boolean, FileError | null];
52
59
 
53
- export { type ApplicationFileMimeType, type AudioFileMimeType, type DownloadFileOptions, type FileError, type FileMimeType, type FileMimeTypeGroup, type FontFileMimeType, type ImageFileMimeType, type TextFileMimeType, type VideoFileMimeType, dataURItoBlob, downloadFile, getAcceptAttrString, getFileDataUrl, getFileEntries, getTotalFileSize, isFileEqual, isMSEdge, isValidFileSize, isValidFileType };
60
+ export { type ApplicationFileMimeType, type AudioFileMimeType, type DownloadFileOptions, type FileError, type FileMimeType, type FileMimeTypeGroup, type FontFileMimeType, type ImageFileMimeType, type TextFileMimeType, type VideoFileMimeType, dataURItoBlob, downloadFile, getAcceptAttrString, getFileDataUrl, getFileEntries, getTotalFileSize, isFileEqual, isValidFileSize, isValidFileType };
package/dist/index.js CHANGED
@@ -65,30 +65,57 @@ function dataURItoBlob(uri) {
65
65
  }
66
66
 
67
67
  // src/download-file.ts
68
+ var BOM_REGEX = /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i;
69
+ var MAC_REGEX = /Macintosh/;
70
+ var APPLE_WEBKIT_REGEX = /AppleWebKit/;
71
+ var SAFARI_REGEX = /Safari/;
72
+ function getBlob(blobOrString, type, appendBOM) {
73
+ let blob = typeof blobOrString === "string" ? new Blob([blobOrString], { type }) : blobOrString;
74
+ if (appendBOM && BOM_REGEX.test(blob.type)) {
75
+ return new Blob([String.fromCharCode(65279), blob], { type: blob.type });
76
+ }
77
+ return blob;
78
+ }
68
79
  function isMSEdge(win) {
69
80
  return Boolean(win.navigator && win.navigator.msSaveOrOpenBlob);
70
81
  }
82
+ function isWebView(win) {
83
+ return win.navigator && MAC_REGEX.test(win.navigator.userAgent) && APPLE_WEBKIT_REGEX.test(win.navigator.userAgent) && !SAFARI_REGEX.test(win.navigator.userAgent);
84
+ }
71
85
  function downloadFile(options) {
72
- const { file, win, type, name } = options;
86
+ const { file, win = window, type, name, appendBOM, revokeTimeout = 0 } = options;
73
87
  const doc = win.document;
74
- const obj = typeof file === "string" ? new Blob([file], { type }) : file;
75
- const fileName = file instanceof File ? name || file.name : name;
88
+ const blob = getBlob(file, type, appendBOM);
89
+ const fileName = (file instanceof File ? name || file.name : name) || "file-download";
76
90
  if (isMSEdge(win)) {
77
- win.navigator.msSaveOrOpenBlob(obj, fileName || "file-download");
91
+ win.navigator.msSaveOrOpenBlob(blob, fileName);
78
92
  return;
79
93
  }
80
- const url = win.URL.createObjectURL(obj);
94
+ const isMacOSWebView = isWebView(win);
81
95
  const anchor = doc.createElement("a");
82
- anchor.style.display = "none";
83
- anchor.href = url;
84
- anchor.rel = "noopener";
85
- anchor.download = fileName || "file-download";
86
- doc.documentElement.appendChild(anchor);
87
- anchor.click();
96
+ const canUseDownload = "download" in anchor && !isMacOSWebView;
97
+ if (canUseDownload) {
98
+ const url2 = win.URL.createObjectURL(blob);
99
+ anchor.href = url2;
100
+ anchor.rel = "noopener";
101
+ anchor.download = fileName;
102
+ anchor.style.display = "none";
103
+ doc.body.appendChild(anchor);
104
+ anchor.dispatchEvent(new win.MouseEvent("click"));
105
+ setTimeout(() => {
106
+ win.URL.revokeObjectURL(url2);
107
+ anchor.remove();
108
+ }, revokeTimeout);
109
+ return;
110
+ }
111
+ const url = win.URL.createObjectURL(blob);
112
+ const popup = win.open(url, "_blank");
113
+ if (!popup) {
114
+ win.location.href = url;
115
+ }
88
116
  setTimeout(() => {
89
117
  win.URL.revokeObjectURL(url);
90
- anchor.remove();
91
- }, 0);
118
+ }, revokeTimeout);
92
119
  }
93
120
 
94
121
  // src/get-accept-attr.ts
@@ -189,6 +216,5 @@ exports.getFileDataUrl = getFileDataUrl;
189
216
  exports.getFileEntries = getFileEntries;
190
217
  exports.getTotalFileSize = getTotalFileSize;
191
218
  exports.isFileEqual = isFileEqual;
192
- exports.isMSEdge = isMSEdge;
193
219
  exports.isValidFileSize = isValidFileSize;
194
220
  exports.isValidFileType = isValidFileType;
package/dist/index.mjs CHANGED
@@ -63,30 +63,57 @@ function dataURItoBlob(uri) {
63
63
  }
64
64
 
65
65
  // src/download-file.ts
66
+ var BOM_REGEX = /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i;
67
+ var MAC_REGEX = /Macintosh/;
68
+ var APPLE_WEBKIT_REGEX = /AppleWebKit/;
69
+ var SAFARI_REGEX = /Safari/;
70
+ function getBlob(blobOrString, type, appendBOM) {
71
+ let blob = typeof blobOrString === "string" ? new Blob([blobOrString], { type }) : blobOrString;
72
+ if (appendBOM && BOM_REGEX.test(blob.type)) {
73
+ return new Blob([String.fromCharCode(65279), blob], { type: blob.type });
74
+ }
75
+ return blob;
76
+ }
66
77
  function isMSEdge(win) {
67
78
  return Boolean(win.navigator && win.navigator.msSaveOrOpenBlob);
68
79
  }
80
+ function isWebView(win) {
81
+ return win.navigator && MAC_REGEX.test(win.navigator.userAgent) && APPLE_WEBKIT_REGEX.test(win.navigator.userAgent) && !SAFARI_REGEX.test(win.navigator.userAgent);
82
+ }
69
83
  function downloadFile(options) {
70
- const { file, win, type, name } = options;
84
+ const { file, win = window, type, name, appendBOM, revokeTimeout = 0 } = options;
71
85
  const doc = win.document;
72
- const obj = typeof file === "string" ? new Blob([file], { type }) : file;
73
- const fileName = file instanceof File ? name || file.name : name;
86
+ const blob = getBlob(file, type, appendBOM);
87
+ const fileName = (file instanceof File ? name || file.name : name) || "file-download";
74
88
  if (isMSEdge(win)) {
75
- win.navigator.msSaveOrOpenBlob(obj, fileName || "file-download");
89
+ win.navigator.msSaveOrOpenBlob(blob, fileName);
76
90
  return;
77
91
  }
78
- const url = win.URL.createObjectURL(obj);
92
+ const isMacOSWebView = isWebView(win);
79
93
  const anchor = doc.createElement("a");
80
- anchor.style.display = "none";
81
- anchor.href = url;
82
- anchor.rel = "noopener";
83
- anchor.download = fileName || "file-download";
84
- doc.documentElement.appendChild(anchor);
85
- anchor.click();
94
+ const canUseDownload = "download" in anchor && !isMacOSWebView;
95
+ if (canUseDownload) {
96
+ const url2 = win.URL.createObjectURL(blob);
97
+ anchor.href = url2;
98
+ anchor.rel = "noopener";
99
+ anchor.download = fileName;
100
+ anchor.style.display = "none";
101
+ doc.body.appendChild(anchor);
102
+ anchor.dispatchEvent(new win.MouseEvent("click"));
103
+ setTimeout(() => {
104
+ win.URL.revokeObjectURL(url2);
105
+ anchor.remove();
106
+ }, revokeTimeout);
107
+ return;
108
+ }
109
+ const url = win.URL.createObjectURL(blob);
110
+ const popup = win.open(url, "_blank");
111
+ if (!popup) {
112
+ win.location.href = url;
113
+ }
86
114
  setTimeout(() => {
87
115
  win.URL.revokeObjectURL(url);
88
- anchor.remove();
89
- }, 0);
116
+ }, revokeTimeout);
90
117
  }
91
118
 
92
119
  // src/get-accept-attr.ts
@@ -180,4 +207,4 @@ function isValidFileType(file, accept) {
180
207
  return [isAcceptable, isAcceptable ? null : "FILE_INVALID_TYPE"];
181
208
  }
182
209
 
183
- export { dataURItoBlob, downloadFile, getAcceptAttrString, getFileDataUrl, getFileEntries, getTotalFileSize, isFileEqual, isMSEdge, isValidFileSize, isValidFileType };
210
+ export { dataURItoBlob, downloadFile, getAcceptAttrString, getFileDataUrl, getFileEntries, getTotalFileSize, isFileEqual, isValidFileSize, isValidFileType };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/file-utils",
3
- "version": "1.15.1",
3
+ "version": "1.15.3",
4
4
  "description": "JS File API utilities",
5
5
  "keywords": [
6
6
  "js",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "clean-package": "../../../clean-package.config.json",
26
26
  "dependencies": {
27
- "@zag-js/i18n-utils": "1.15.1"
27
+ "@zag-js/i18n-utils": "1.15.3"
28
28
  },
29
29
  "devDependencies": {
30
30
  "clean-package": "2.2.0"