Files
apparel-designer/src/utils/imageLoading.js
2026-05-23 03:28:58 -05:00

53 lines
2.2 KiB
JavaScript

/**
* Image-loading helpers used by photo upload and slot assignment.
*
* Before this module: `useTemplate.assignImageToSlot` and `UploadTab.getImageSize`
* each had their own inline `new Image() + onload + onerror` Promise wrapper,
* and `App.handlePhotoEditComplete` had a third variant using callbacks
* instead of a Promise. The three wrappers had subtly different error
* handling — one rejected with a generic message, another logged to the
* console, the third silently swallowed errors and used a fallback path.
*
* Centralizing here gives every caller the same behavior: a clean Promise
* that resolves to dimensions or rejects with a descriptive Error. The
* App.jsx photo-edit flow still uses the lower-level callback form because
* its onload handler runs synchronously before its setEditingElement call
* (timing matters for the Filerobot modal teardown sequence) — but the
* other two consumers go through `loadImageDimensions`.
*/
/**
* Load an image and resolve to its natural pixel dimensions.
*
* Resolves with `{ width, height }` from `naturalWidth`/`naturalHeight`,
* falling back to `width`/`height` for the rare case where naturalWidth is 0
* (some Safari versions report this for SVG sources without intrinsic size).
*
* Rejects with a descriptive Error if the image fails to load. Callers
* should `await` and use try/catch — not `.then(undefined, () => fallback)`,
* which would silently mask bugs.
*
* @param {string} src - data:, blob:, or remote URL
* @returns {Promise<{ width: number, height: number }>}
*/
export function loadImageDimensions(src) {
return new Promise((resolve, reject) => {
const img = new window.Image();
img.onload = () => resolve({
width: img.naturalWidth || img.width,
height: img.naturalHeight || img.height,
});
img.onerror = () => reject(new Error(`Failed to load image: ${truncate(src, 80)}`));
img.src = src;
});
}
/**
* Truncate a URL for use in error messages. Long data: URLs in stack traces
* blow up log readers; the first 80 chars are enough to identify the type.
*/
function truncate(s, n) {
if (typeof s !== 'string') return String(s);
return s.length > n ? `${s.slice(0, n)}…` : s;
}