@zag-js/file-utils 0.22.0 → 0.23.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.d.mts CHANGED
@@ -1,4 +1,11 @@
1
- declare const formatBytes: (bytes: number, decimals?: number) => string;
1
+ declare function downloadFile(fileOrUrl: File | string, win: typeof window): void;
2
+
3
+ interface FormatByteOptions {
4
+ locale?: string;
5
+ }
6
+ declare const formatFileSize: (bytes: number, options?: FormatByteOptions) => string;
7
+
8
+ declare function getAcceptAttrString(accept: Record<string, string[]> | string | undefined): string | undefined;
2
9
 
3
10
  declare const getFileDataUrl: (file: File | Blob) => Promise<string | undefined>;
4
11
 
@@ -6,4 +13,8 @@ declare const getTotalFileSize: (files: File[]) => number;
6
13
 
7
14
  declare const isFileEqual: (file1: File, file2: File) => boolean;
8
15
 
9
- export { formatBytes, getFileDataUrl, getTotalFileSize, isFileEqual };
16
+ declare function isValidFileSize(file: File, minSize?: number, maxSize?: number): [boolean, string | null];
17
+
18
+ declare function isValidFileType(file: File, accept: string | undefined): [boolean, string | null];
19
+
20
+ export { downloadFile, formatFileSize, getAcceptAttrString, getFileDataUrl, getTotalFileSize, isFileEqual, isValidFileSize, isValidFileType };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,11 @@
1
- declare const formatBytes: (bytes: number, decimals?: number) => string;
1
+ declare function downloadFile(fileOrUrl: File | string, win: typeof window): void;
2
+
3
+ interface FormatByteOptions {
4
+ locale?: string;
5
+ }
6
+ declare const formatFileSize: (bytes: number, options?: FormatByteOptions) => string;
7
+
8
+ declare function getAcceptAttrString(accept: Record<string, string[]> | string | undefined): string | undefined;
2
9
 
3
10
  declare const getFileDataUrl: (file: File | Blob) => Promise<string | undefined>;
4
11
 
@@ -6,4 +13,8 @@ declare const getTotalFileSize: (files: File[]) => number;
6
13
 
7
14
  declare const isFileEqual: (file1: File, file2: File) => boolean;
8
15
 
9
- export { formatBytes, getFileDataUrl, getTotalFileSize, isFileEqual };
16
+ declare function isValidFileSize(file: File, minSize?: number, maxSize?: number): [boolean, string | null];
17
+
18
+ declare function isValidFileType(file: File, accept: string | undefined): [boolean, string | null];
19
+
20
+ export { downloadFile, formatFileSize, getAcceptAttrString, getFileDataUrl, getTotalFileSize, isFileEqual, isValidFileSize, isValidFileType };
package/dist/index.js CHANGED
@@ -20,25 +20,63 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
- formatBytes: () => formatBytes,
23
+ downloadFile: () => downloadFile,
24
+ formatFileSize: () => formatFileSize,
25
+ getAcceptAttrString: () => getAcceptAttrString,
24
26
  getFileDataUrl: () => getFileDataUrl,
25
27
  getTotalFileSize: () => getTotalFileSize,
26
- isFileEqual: () => isFileEqual
28
+ isFileEqual: () => isFileEqual,
29
+ isValidFileSize: () => isValidFileSize,
30
+ isValidFileType: () => isValidFileType
27
31
  });
28
32
  module.exports = __toCommonJS(src_exports);
29
33
 
30
- // src/format-bytes.ts
31
- var SIZES = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
34
+ // src/download-file.ts
35
+ function downloadFile(fileOrUrl, win) {
36
+ const doc = win.document;
37
+ const objectUrl = typeof fileOrUrl === "string" ? fileOrUrl : win.URL.createObjectURL(fileOrUrl);
38
+ const anchor = doc.createElement("a");
39
+ anchor.style.display = "none";
40
+ anchor.href = objectUrl;
41
+ anchor.download = typeof fileOrUrl === "string" ? objectUrl : fileOrUrl.name;
42
+ doc.body.appendChild(anchor);
43
+ anchor.click();
44
+ setTimeout(() => {
45
+ if (typeof fileOrUrl !== "string") {
46
+ win.URL.revokeObjectURL(objectUrl);
47
+ }
48
+ anchor?.parentNode?.removeChild(anchor);
49
+ }, 0);
50
+ }
51
+
52
+ // src/format-file-size.ts
53
+ var SIZES = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
32
54
  var KILO = 1024;
33
- var formatBytes = (bytes, decimals = 2) => {
34
- if (bytes === 0) {
35
- return "0 Bytes";
36
- }
37
- const dm = decimals <= 0 ? 0 : decimals || 2;
55
+ var formatFileSize = (bytes, options = {}) => {
56
+ const { locale = "en-US" } = options;
57
+ if (bytes === 0)
58
+ return "0 B";
38
59
  const i = Math.floor(Math.log(bytes) / Math.log(KILO));
39
- return parseFloat((bytes / Math.pow(KILO, i)).toFixed(dm)) + " " + SIZES[i];
60
+ const fileSize = bytes / Math.pow(KILO, i);
61
+ const formattedSize = new Intl.NumberFormat(locale).format(fileSize);
62
+ return `${formattedSize} ${SIZES[i]}`;
40
63
  };
41
64
 
65
+ // src/get-accept-attr.ts
66
+ function isMIMEType(v) {
67
+ return v === "audio/*" || v === "video/*" || v === "image/*" || v === "text/*" || /\w+\/[-+.\w]+/g.test(v);
68
+ }
69
+ function isExt(v) {
70
+ return /^.*\.[\w]+$/.test(v);
71
+ }
72
+ function getAcceptAttrString(accept) {
73
+ if (!accept)
74
+ return;
75
+ if (typeof accept === "string")
76
+ return accept;
77
+ return Object.entries(accept).reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], []).filter((v) => isMIMEType(v) || isExt(v)).join(",");
78
+ }
79
+
42
80
  // src/get-file-data-url.ts
43
81
  var getFileDataUrl = async (file) => {
44
82
  const reader = new FileReader();
@@ -68,11 +106,58 @@ var getTotalFileSize = (files) => {
68
106
  var isFileEqual = (file1, file2) => {
69
107
  return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type;
70
108
  };
109
+
110
+ // src/is-valid-file-size.ts
111
+ var isDefined = (v) => v !== void 0 && v !== null;
112
+ function isValidFileSize(file, minSize, maxSize) {
113
+ if (isDefined(file.size)) {
114
+ if (isDefined(minSize) && isDefined(maxSize)) {
115
+ if (file.size > maxSize)
116
+ return [false, "TOO_LARGE"];
117
+ if (file.size < minSize)
118
+ return [false, "TOO_SMALL"];
119
+ } else if (isDefined(minSize) && file.size < minSize) {
120
+ return [false, "TOO_SMALL"];
121
+ } else if (isDefined(maxSize) && file.size > maxSize) {
122
+ return [false, "TOO_LARGE"];
123
+ }
124
+ }
125
+ return [true, null];
126
+ }
127
+
128
+ // src/is-valid-file-type.ts
129
+ function isFileAccepted(file, accept) {
130
+ if (file && accept) {
131
+ const types = Array.isArray(accept) ? accept : accept.split(",");
132
+ const fileName = file.name || "";
133
+ const mimeType = (file.type || "").toLowerCase();
134
+ const baseMimeType = mimeType.replace(/\/.*$/, "");
135
+ return types.some((type) => {
136
+ const validType = type.trim().toLowerCase();
137
+ if (validType.charAt(0) === ".") {
138
+ return fileName.toLowerCase().endsWith(validType);
139
+ }
140
+ if (validType.endsWith("/*")) {
141
+ return baseMimeType === validType.replace(/\/.*$/, "");
142
+ }
143
+ return mimeType === validType;
144
+ });
145
+ }
146
+ return true;
147
+ }
148
+ function isValidFileType(file, accept) {
149
+ const isAcceptable = file.type === "application/x-moz-file" || isFileAccepted(file, accept);
150
+ return [isAcceptable, isAcceptable ? null : "FILE_INVALID_TYPE"];
151
+ }
71
152
  // Annotate the CommonJS export names for ESM import in node:
72
153
  0 && (module.exports = {
73
- formatBytes,
154
+ downloadFile,
155
+ formatFileSize,
156
+ getAcceptAttrString,
74
157
  getFileDataUrl,
75
158
  getTotalFileSize,
76
- isFileEqual
159
+ isFileEqual,
160
+ isValidFileSize,
161
+ isValidFileType
77
162
  });
78
163
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/format-bytes.ts","../src/get-file-data-url.ts","../src/get-total-file-size.ts","../src/is-file-equal.ts"],"sourcesContent":["export * from \"./format-bytes\"\nexport * from \"./get-file-data-url\"\nexport * from \"./get-total-file-size\"\nexport * from \"./is-file-equal\"\n","const SIZES = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\nconst KILO = 1024\n\nexport const formatBytes = (bytes: number, decimals = 2) => {\n if (bytes === 0) {\n return \"0 Bytes\"\n }\n const dm = decimals <= 0 ? 0 : decimals || 2\n const i = Math.floor(Math.log(bytes) / Math.log(KILO))\n return parseFloat((bytes / Math.pow(KILO, i)).toFixed(dm)) + \" \" + SIZES[i]\n}\n","export const getFileDataUrl = async (file: File | Blob) => {\n const reader = new FileReader()\n return new Promise<string | undefined>((resolve, reject) => {\n reader.onerror = () => {\n reader.abort()\n reject(new Error(\"There was an error reading a file\"))\n }\n\n reader.onloadend = () => {\n const { result } = reader\n if (result instanceof ArrayBuffer) {\n reject(new Error(\"Expected DataURL as string from Blob/File, got ArrayBuffer\"))\n } else {\n resolve(result || undefined)\n }\n }\n\n reader.readAsDataURL(file)\n })\n}\n","export const getTotalFileSize = (files: File[]) => {\n return files.reduce((acc, file) => acc + file.size, 0)\n}\n","export const isFileEqual = (file1: File, file2: File) => {\n return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAM,QAAQ,CAAC,SAAS,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI;AACtE,IAAM,OAAO;AAEN,IAAM,cAAc,CAAC,OAAe,WAAW,MAAM;AAC1D,MAAI,UAAU,GAAG;AACf,WAAO;AAAA,EACT;AACA,QAAM,KAAK,YAAY,IAAI,IAAI,YAAY;AAC3C,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;AACrD,SAAO,YAAY,QAAQ,KAAK,IAAI,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,CAAC;AAC5E;;;ACVO,IAAM,iBAAiB,OAAO,SAAsB;AACzD,QAAM,SAAS,IAAI,WAAW;AAC9B,SAAO,IAAI,QAA4B,CAAC,SAAS,WAAW;AAC1D,WAAO,UAAU,MAAM;AACrB,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,mCAAmC,CAAC;AAAA,IACvD;AAEA,WAAO,YAAY,MAAM;AACvB,YAAM,EAAE,OAAO,IAAI;AACnB,UAAI,kBAAkB,aAAa;AACjC,eAAO,IAAI,MAAM,4DAA4D,CAAC;AAAA,MAChF,OAAO;AACL,gBAAQ,UAAU,MAAS;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO,cAAc,IAAI;AAAA,EAC3B,CAAC;AACH;;;ACnBO,IAAM,mBAAmB,CAAC,UAAkB;AACjD,SAAO,MAAM,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,MAAM,CAAC;AACvD;;;ACFO,IAAM,cAAc,CAAC,OAAa,UAAgB;AACvD,SAAO,MAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,MAAM;AACxF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/download-file.ts","../src/format-file-size.ts","../src/get-accept-attr.ts","../src/get-file-data-url.ts","../src/get-total-file-size.ts","../src/is-file-equal.ts","../src/is-valid-file-size.ts","../src/is-valid-file-type.ts"],"sourcesContent":["export * from \"./download-file\"\nexport * from \"./format-file-size\"\nexport * from \"./get-accept-attr\"\nexport * from \"./get-file-data-url\"\nexport * from \"./get-total-file-size\"\nexport * from \"./is-file-equal\"\nexport * from \"./is-valid-file-size\"\nexport * from \"./is-valid-file-type\"\n","export function downloadFile(fileOrUrl: File | string, win: typeof window) {\n const doc = win.document\n const objectUrl = typeof fileOrUrl === \"string\" ? fileOrUrl : win.URL.createObjectURL(fileOrUrl)\n\n const anchor = doc.createElement(\"a\")\n anchor.style.display = \"none\"\n anchor.href = objectUrl\n anchor.download = typeof fileOrUrl === \"string\" ? objectUrl : fileOrUrl.name\n\n doc.body.appendChild(anchor)\n anchor.click()\n\n setTimeout(() => {\n if (typeof fileOrUrl !== \"string\") {\n win.URL.revokeObjectURL(objectUrl)\n }\n anchor?.parentNode?.removeChild(anchor)\n }, 0)\n}\n","const SIZES = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\nconst KILO = 1024\n\ninterface FormatByteOptions {\n locale?: string\n}\n\nexport const formatFileSize = (bytes: number, options: FormatByteOptions = {}) => {\n const { locale = \"en-US\" } = options\n if (bytes === 0) return \"0 B\"\n\n const i = Math.floor(Math.log(bytes) / Math.log(KILO))\n const fileSize = bytes / Math.pow(KILO, i)\n\n const formattedSize = new Intl.NumberFormat(locale).format(fileSize)\n return `${formattedSize} ${SIZES[i]}`\n}\n","function isMIMEType(v: string) {\n return v === \"audio/*\" || v === \"video/*\" || v === \"image/*\" || v === \"text/*\" || /\\w+\\/[-+.\\w]+/g.test(v)\n}\n\nfunction isExt(v: string) {\n return /^.*\\.[\\w]+$/.test(v)\n}\n\nexport function getAcceptAttrString(accept: Record<string, string[]> | string | undefined) {\n if (!accept) return\n if (typeof accept === \"string\") return accept\n return Object.entries(accept)\n .reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], [] as string[])\n .filter((v) => isMIMEType(v) || isExt(v))\n .join(\",\")\n}\n","export const getFileDataUrl = async (file: File | Blob) => {\n const reader = new FileReader()\n return new Promise<string | undefined>((resolve, reject) => {\n reader.onerror = () => {\n reader.abort()\n reject(new Error(\"There was an error reading a file\"))\n }\n\n reader.onloadend = () => {\n const { result } = reader\n if (result instanceof ArrayBuffer) {\n reject(new Error(\"Expected DataURL as string from Blob/File, got ArrayBuffer\"))\n } else {\n resolve(result || undefined)\n }\n }\n\n reader.readAsDataURL(file)\n })\n}\n","export const getTotalFileSize = (files: File[]) => {\n return files.reduce((acc, file) => acc + file.size, 0)\n}\n","export const isFileEqual = (file1: File, file2: File) => {\n return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type\n}\n","const isDefined = <T>(v: T | undefined): v is T => v !== undefined && v !== null\n\nexport function isValidFileSize(file: File, minSize?: number, maxSize?: number): [boolean, string | null] {\n if (isDefined(file.size)) {\n if (isDefined(minSize) && isDefined(maxSize)) {\n if (file.size > maxSize) return [false, \"TOO_LARGE\"]\n if (file.size < minSize) return [false, \"TOO_SMALL\"]\n } else if (isDefined(minSize) && file.size < minSize) {\n return [false, \"TOO_SMALL\"]\n } else if (isDefined(maxSize) && file.size > maxSize) {\n return [false, \"TOO_LARGE\"]\n }\n }\n return [true, null]\n}\n","function isFileAccepted(file: File | null, accept: string[] | string | undefined) {\n if (file && accept) {\n const types = Array.isArray(accept) ? accept : accept.split(\",\")\n\n const fileName = file.name || \"\"\n const mimeType = (file.type || \"\").toLowerCase()\n const baseMimeType = mimeType.replace(/\\/.*$/, \"\")\n\n return types.some((type) => {\n const validType = type.trim().toLowerCase()\n\n if (validType.charAt(0) === \".\") {\n return fileName.toLowerCase().endsWith(validType)\n }\n\n if (validType.endsWith(\"/*\")) {\n return baseMimeType === validType.replace(/\\/.*$/, \"\")\n }\n\n return mimeType === validType\n })\n }\n return true\n}\n\nexport function isValidFileType(file: File, accept: string | undefined): [boolean, string | null] {\n const isAcceptable = file.type === \"application/x-moz-file\" || isFileAccepted(file, accept)\n return [isAcceptable, isAcceptable ? null : \"FILE_INVALID_TYPE\"]\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -1,15 +1,49 @@
1
- // src/format-bytes.ts
2
- var SIZES = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
1
+ // src/download-file.ts
2
+ function downloadFile(fileOrUrl, win) {
3
+ const doc = win.document;
4
+ const objectUrl = typeof fileOrUrl === "string" ? fileOrUrl : win.URL.createObjectURL(fileOrUrl);
5
+ const anchor = doc.createElement("a");
6
+ anchor.style.display = "none";
7
+ anchor.href = objectUrl;
8
+ anchor.download = typeof fileOrUrl === "string" ? objectUrl : fileOrUrl.name;
9
+ doc.body.appendChild(anchor);
10
+ anchor.click();
11
+ setTimeout(() => {
12
+ if (typeof fileOrUrl !== "string") {
13
+ win.URL.revokeObjectURL(objectUrl);
14
+ }
15
+ anchor?.parentNode?.removeChild(anchor);
16
+ }, 0);
17
+ }
18
+
19
+ // src/format-file-size.ts
20
+ var SIZES = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
3
21
  var KILO = 1024;
4
- var formatBytes = (bytes, decimals = 2) => {
5
- if (bytes === 0) {
6
- return "0 Bytes";
7
- }
8
- const dm = decimals <= 0 ? 0 : decimals || 2;
22
+ var formatFileSize = (bytes, options = {}) => {
23
+ const { locale = "en-US" } = options;
24
+ if (bytes === 0)
25
+ return "0 B";
9
26
  const i = Math.floor(Math.log(bytes) / Math.log(KILO));
10
- return parseFloat((bytes / Math.pow(KILO, i)).toFixed(dm)) + " " + SIZES[i];
27
+ const fileSize = bytes / Math.pow(KILO, i);
28
+ const formattedSize = new Intl.NumberFormat(locale).format(fileSize);
29
+ return `${formattedSize} ${SIZES[i]}`;
11
30
  };
12
31
 
32
+ // src/get-accept-attr.ts
33
+ function isMIMEType(v) {
34
+ return v === "audio/*" || v === "video/*" || v === "image/*" || v === "text/*" || /\w+\/[-+.\w]+/g.test(v);
35
+ }
36
+ function isExt(v) {
37
+ return /^.*\.[\w]+$/.test(v);
38
+ }
39
+ function getAcceptAttrString(accept) {
40
+ if (!accept)
41
+ return;
42
+ if (typeof accept === "string")
43
+ return accept;
44
+ return Object.entries(accept).reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], []).filter((v) => isMIMEType(v) || isExt(v)).join(",");
45
+ }
46
+
13
47
  // src/get-file-data-url.ts
14
48
  var getFileDataUrl = async (file) => {
15
49
  const reader = new FileReader();
@@ -39,10 +73,57 @@ var getTotalFileSize = (files) => {
39
73
  var isFileEqual = (file1, file2) => {
40
74
  return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type;
41
75
  };
76
+
77
+ // src/is-valid-file-size.ts
78
+ var isDefined = (v) => v !== void 0 && v !== null;
79
+ function isValidFileSize(file, minSize, maxSize) {
80
+ if (isDefined(file.size)) {
81
+ if (isDefined(minSize) && isDefined(maxSize)) {
82
+ if (file.size > maxSize)
83
+ return [false, "TOO_LARGE"];
84
+ if (file.size < minSize)
85
+ return [false, "TOO_SMALL"];
86
+ } else if (isDefined(minSize) && file.size < minSize) {
87
+ return [false, "TOO_SMALL"];
88
+ } else if (isDefined(maxSize) && file.size > maxSize) {
89
+ return [false, "TOO_LARGE"];
90
+ }
91
+ }
92
+ return [true, null];
93
+ }
94
+
95
+ // src/is-valid-file-type.ts
96
+ function isFileAccepted(file, accept) {
97
+ if (file && accept) {
98
+ const types = Array.isArray(accept) ? accept : accept.split(",");
99
+ const fileName = file.name || "";
100
+ const mimeType = (file.type || "").toLowerCase();
101
+ const baseMimeType = mimeType.replace(/\/.*$/, "");
102
+ return types.some((type) => {
103
+ const validType = type.trim().toLowerCase();
104
+ if (validType.charAt(0) === ".") {
105
+ return fileName.toLowerCase().endsWith(validType);
106
+ }
107
+ if (validType.endsWith("/*")) {
108
+ return baseMimeType === validType.replace(/\/.*$/, "");
109
+ }
110
+ return mimeType === validType;
111
+ });
112
+ }
113
+ return true;
114
+ }
115
+ function isValidFileType(file, accept) {
116
+ const isAcceptable = file.type === "application/x-moz-file" || isFileAccepted(file, accept);
117
+ return [isAcceptable, isAcceptable ? null : "FILE_INVALID_TYPE"];
118
+ }
42
119
  export {
43
- formatBytes,
120
+ downloadFile,
121
+ formatFileSize,
122
+ getAcceptAttrString,
44
123
  getFileDataUrl,
45
124
  getTotalFileSize,
46
- isFileEqual
125
+ isFileEqual,
126
+ isValidFileSize,
127
+ isValidFileType
47
128
  };
48
129
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/format-bytes.ts","../src/get-file-data-url.ts","../src/get-total-file-size.ts","../src/is-file-equal.ts"],"sourcesContent":["const SIZES = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\nconst KILO = 1024\n\nexport const formatBytes = (bytes: number, decimals = 2) => {\n if (bytes === 0) {\n return \"0 Bytes\"\n }\n const dm = decimals <= 0 ? 0 : decimals || 2\n const i = Math.floor(Math.log(bytes) / Math.log(KILO))\n return parseFloat((bytes / Math.pow(KILO, i)).toFixed(dm)) + \" \" + SIZES[i]\n}\n","export const getFileDataUrl = async (file: File | Blob) => {\n const reader = new FileReader()\n return new Promise<string | undefined>((resolve, reject) => {\n reader.onerror = () => {\n reader.abort()\n reject(new Error(\"There was an error reading a file\"))\n }\n\n reader.onloadend = () => {\n const { result } = reader\n if (result instanceof ArrayBuffer) {\n reject(new Error(\"Expected DataURL as string from Blob/File, got ArrayBuffer\"))\n } else {\n resolve(result || undefined)\n }\n }\n\n reader.readAsDataURL(file)\n })\n}\n","export const getTotalFileSize = (files: File[]) => {\n return files.reduce((acc, file) => acc + file.size, 0)\n}\n","export const isFileEqual = (file1: File, file2: File) => {\n return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type\n}\n"],"mappings":";AAAA,IAAM,QAAQ,CAAC,SAAS,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI;AACtE,IAAM,OAAO;AAEN,IAAM,cAAc,CAAC,OAAe,WAAW,MAAM;AAC1D,MAAI,UAAU,GAAG;AACf,WAAO;AAAA,EACT;AACA,QAAM,KAAK,YAAY,IAAI,IAAI,YAAY;AAC3C,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;AACrD,SAAO,YAAY,QAAQ,KAAK,IAAI,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,CAAC;AAC5E;;;ACVO,IAAM,iBAAiB,OAAO,SAAsB;AACzD,QAAM,SAAS,IAAI,WAAW;AAC9B,SAAO,IAAI,QAA4B,CAAC,SAAS,WAAW;AAC1D,WAAO,UAAU,MAAM;AACrB,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,mCAAmC,CAAC;AAAA,IACvD;AAEA,WAAO,YAAY,MAAM;AACvB,YAAM,EAAE,OAAO,IAAI;AACnB,UAAI,kBAAkB,aAAa;AACjC,eAAO,IAAI,MAAM,4DAA4D,CAAC;AAAA,MAChF,OAAO;AACL,gBAAQ,UAAU,MAAS;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO,cAAc,IAAI;AAAA,EAC3B,CAAC;AACH;;;ACnBO,IAAM,mBAAmB,CAAC,UAAkB;AACjD,SAAO,MAAM,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,MAAM,CAAC;AACvD;;;ACFO,IAAM,cAAc,CAAC,OAAa,UAAgB;AACvD,SAAO,MAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,MAAM;AACxF;","names":[]}
1
+ {"version":3,"sources":["../src/download-file.ts","../src/format-file-size.ts","../src/get-accept-attr.ts","../src/get-file-data-url.ts","../src/get-total-file-size.ts","../src/is-file-equal.ts","../src/is-valid-file-size.ts","../src/is-valid-file-type.ts"],"sourcesContent":["export function downloadFile(fileOrUrl: File | string, win: typeof window) {\n const doc = win.document\n const objectUrl = typeof fileOrUrl === \"string\" ? fileOrUrl : win.URL.createObjectURL(fileOrUrl)\n\n const anchor = doc.createElement(\"a\")\n anchor.style.display = \"none\"\n anchor.href = objectUrl\n anchor.download = typeof fileOrUrl === \"string\" ? objectUrl : fileOrUrl.name\n\n doc.body.appendChild(anchor)\n anchor.click()\n\n setTimeout(() => {\n if (typeof fileOrUrl !== \"string\") {\n win.URL.revokeObjectURL(objectUrl)\n }\n anchor?.parentNode?.removeChild(anchor)\n }, 0)\n}\n","const SIZES = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\nconst KILO = 1024\n\ninterface FormatByteOptions {\n locale?: string\n}\n\nexport const formatFileSize = (bytes: number, options: FormatByteOptions = {}) => {\n const { locale = \"en-US\" } = options\n if (bytes === 0) return \"0 B\"\n\n const i = Math.floor(Math.log(bytes) / Math.log(KILO))\n const fileSize = bytes / Math.pow(KILO, i)\n\n const formattedSize = new Intl.NumberFormat(locale).format(fileSize)\n return `${formattedSize} ${SIZES[i]}`\n}\n","function isMIMEType(v: string) {\n return v === \"audio/*\" || v === \"video/*\" || v === \"image/*\" || v === \"text/*\" || /\\w+\\/[-+.\\w]+/g.test(v)\n}\n\nfunction isExt(v: string) {\n return /^.*\\.[\\w]+$/.test(v)\n}\n\nexport function getAcceptAttrString(accept: Record<string, string[]> | string | undefined) {\n if (!accept) return\n if (typeof accept === \"string\") return accept\n return Object.entries(accept)\n .reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], [] as string[])\n .filter((v) => isMIMEType(v) || isExt(v))\n .join(\",\")\n}\n","export const getFileDataUrl = async (file: File | Blob) => {\n const reader = new FileReader()\n return new Promise<string | undefined>((resolve, reject) => {\n reader.onerror = () => {\n reader.abort()\n reject(new Error(\"There was an error reading a file\"))\n }\n\n reader.onloadend = () => {\n const { result } = reader\n if (result instanceof ArrayBuffer) {\n reject(new Error(\"Expected DataURL as string from Blob/File, got ArrayBuffer\"))\n } else {\n resolve(result || undefined)\n }\n }\n\n reader.readAsDataURL(file)\n })\n}\n","export const getTotalFileSize = (files: File[]) => {\n return files.reduce((acc, file) => acc + file.size, 0)\n}\n","export const isFileEqual = (file1: File, file2: File) => {\n return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type\n}\n","const isDefined = <T>(v: T | undefined): v is T => v !== undefined && v !== null\n\nexport function isValidFileSize(file: File, minSize?: number, maxSize?: number): [boolean, string | null] {\n if (isDefined(file.size)) {\n if (isDefined(minSize) && isDefined(maxSize)) {\n if (file.size > maxSize) return [false, \"TOO_LARGE\"]\n if (file.size < minSize) return [false, \"TOO_SMALL\"]\n } else if (isDefined(minSize) && file.size < minSize) {\n return [false, \"TOO_SMALL\"]\n } else if (isDefined(maxSize) && file.size > maxSize) {\n return [false, \"TOO_LARGE\"]\n }\n }\n return [true, null]\n}\n","function isFileAccepted(file: File | null, accept: string[] | string | undefined) {\n if (file && accept) {\n const types = Array.isArray(accept) ? accept : accept.split(\",\")\n\n const fileName = file.name || \"\"\n const mimeType = (file.type || \"\").toLowerCase()\n const baseMimeType = mimeType.replace(/\\/.*$/, \"\")\n\n return types.some((type) => {\n const validType = type.trim().toLowerCase()\n\n if (validType.charAt(0) === \".\") {\n return fileName.toLowerCase().endsWith(validType)\n }\n\n if (validType.endsWith(\"/*\")) {\n return baseMimeType === validType.replace(/\\/.*$/, \"\")\n }\n\n return mimeType === validType\n })\n }\n return true\n}\n\nexport function isValidFileType(file: File, accept: string | undefined): [boolean, string | null] {\n const isAcceptable = file.type === \"application/x-moz-file\" || isFileAccepted(file, accept)\n return [isAcceptable, isAcceptable ? null : \"FILE_INVALID_TYPE\"]\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/file-utils",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "JS File API utilities",
5
5
  "keywords": [
6
6
  "js",