Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b3bbdf02c | |||
| 6b379270a9 | |||
| 28716c7b5a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ uploads
|
||||
public/momentum*js
|
||||
junit.xml
|
||||
/docs/
|
||||
main.mjs
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
6927
package-lock.json
generated
Normal file
6927
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ztimson/utils",
|
||||
"version": "0.29.3",
|
||||
"version": "0.29.6",
|
||||
"description": "Utility library",
|
||||
"author": "Zak Timson",
|
||||
"license": "MIT",
|
||||
|
||||
24
src/files.ts
24
src/files.ts
@@ -83,6 +83,7 @@ export function timestampFilename(name?: string, date: Date | number | string =
|
||||
|
||||
/**
|
||||
* Upload file to URL with progress callback using PromiseProgress
|
||||
* Works in both browser (with progress) and Node.js (fallback without progress)
|
||||
*
|
||||
* @param {{url: string, files: File[], headers?: {[p: string]: string}, withCredentials?: boolean}} options
|
||||
* @return {PromiseProgress<T>} Promise of request with `onProgress` callback
|
||||
@@ -93,10 +94,12 @@ export function uploadWithProgress<T>(options: {
|
||||
headers?: {[key: string]: string};
|
||||
withCredentials?: boolean;
|
||||
}): PromiseProgress<T> {
|
||||
// Browser environment - use XMLHttpRequest for progress
|
||||
if (typeof XMLHttpRequest !== 'undefined') {
|
||||
return new PromiseProgress<T>((res, rej, prog) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const formData = new FormData();
|
||||
options.files.forEach(f => formData.append('file', f));
|
||||
options.files.forEach(f => formData.append('files', f));
|
||||
|
||||
xhr.withCredentials = !!options.withCredentials;
|
||||
xhr.upload.addEventListener('progress', (event) => event.lengthComputable ? prog(event.loaded / event.total) : null);
|
||||
@@ -109,3 +112,22 @@ export function uploadWithProgress<T>(options: {
|
||||
xhr.send(formData);
|
||||
});
|
||||
}
|
||||
|
||||
// Node.js environment - fallback to fetch without progress
|
||||
return new PromiseProgress<T>(async (res, rej) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
options.files.forEach(f => formData.append('files', f));
|
||||
const response = await fetch(options.url, {method: 'POST', headers: options.headers || {}, body: formData});
|
||||
if(!response.ok) {
|
||||
const error = await response.text();
|
||||
rej(JSONAttemptParse(error));
|
||||
} else {
|
||||
const result = await response.text();
|
||||
res(<T>JSONAttemptParse(result));
|
||||
}
|
||||
} catch (error) {
|
||||
rej(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export class PathEvent {
|
||||
let temp = p.split('/').filter(p => !!p);
|
||||
this.module = temp.splice(0, 1)[0] || '';
|
||||
this.path = temp.join('/');
|
||||
this.dir = temp.length > 2 ? temp.slice(0, -1).join('/') : '';
|
||||
this.dir = temp.length > 1 ? temp.slice(0, -1).join('/') : '';
|
||||
this.fullPath = `${this.module}${this.module && this.path ? '/' : ''}${this.path}`;
|
||||
this.name = temp.pop() || '';
|
||||
this.hasGlob = this.fullPath.includes('*');
|
||||
|
||||
@@ -241,7 +241,6 @@ export function snakeCase(str?: string): string {
|
||||
return wordSegments(str).map(w => w.toLowerCase()).join("_");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splice a string together (Similar to Array.splice)
|
||||
*
|
||||
@@ -257,6 +256,20 @@ export function strSplice(str: string, start: number, deleteCount: number, inser
|
||||
return before + insert + after;
|
||||
}
|
||||
|
||||
function titleCase(str: string) {
|
||||
// Normalize separators: replace underscores and hyphens with spaces
|
||||
let normalizedStr = str.replace(/(_|-)/g, ' ');
|
||||
// Handle CamelCase/PascalCase boundaries: insert a space before capital letters
|
||||
normalizedStr = normalizedStr.replace(/([a-z])([A-Z])/g, '$1 $2');
|
||||
// Lowercase the whole string, split by any whitespace, and capitalize each word
|
||||
let words = normalizedStr.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
const titledWords = words.map(word => {
|
||||
if (word.length === 0) return '';
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
});
|
||||
return titledWords.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all substrings that match a given pattern.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user